【问题标题】:ffmpeg C API - creating queue of framesffmpeg C API - 创建帧队列
【发布时间】:2016-05-25 19:02:04
【问题描述】:

我使用 ffmpeg 的 C API 创建了一个 C++ 应用程序,该应用程序从文件中读取帧并将它们写入新文件。一切正常,只要我立即将帧写入输出。换句话说,程序的以下结构输出正确的结果(我现在只放伪代码,如果需要我也可以发布一些真正的 sn-ps,但我为处理 ffmpeg 功能而创建的类非常大) :

AVFrame* frame = av_frame_alloc();
int got_frame;

// readFrame returns 0 if file is ended, got frame = 1 if
// a complete frame has been extracted
while(readFrame(inputfile,frame, &got_frame)) {
  if (got_frame) {
    // I actually do some processing here
    writeFrame(outputfile,frame);
  }
}
av_frame_free(&frame);

下一步是并行化应用程序,因此,帧在读取后不会立即写入(我不想深入讨论并行化的细节)。在这种情况下会出现问题:输出中有一些闪烁,就好像某些帧随机重复一样。但是,输出视频的帧数和持续时间保持正确。

我现在要做的是将串行实现中的读取与写入完全分开,以了解发生了什么。我正在创建一个指向帧的指针队列:

std::queue<AVFrame*> queue;
int ret = 1, got_frame;
while (ret) {
  AVFrame* frame = av_frame_alloc();
  ret = readFrame(inputfile,frame,&got_frame);
  if (got_frame) 
    queue.push(frame);
}

要将帧写入输出文件,我这样做:

while (!queue.empty()) {
  frame = queue.front();
  queue.pop();
  writeFrame(outputFile,frame);
  av_frame_free(&frame);
}

这种情况下的结果是具有正确持续时间和帧数的输出视频,它只是视频最后 3 帧(我认为)的重复。

我的猜测是可能会出错,因为在第一种情况下,我总是使用相同的内存位置来读取帧,而在第二种情况下,我分配了许多不同的帧。

关于可能是什么问题的任何建议?

【问题讨论】:

    标签: c++ c video ffmpeg


    【解决方案1】:

    啊,所以我假设 readFrame() 是 libavformat 的 av_read_frame() 和 libavcodec 的 avcodec_decode_video2() 的包装器,对吗?

    来自documentation

    AVCodecContext.refcounted_frames 设置为 1 时,帧为 引用计数并且返回的引用属于调用者。 调用者必须使用av_frame_unref() 释放框架,当 不再需要框架。

    和:

    什么时候 AVCodecContext.refcounted_frames 设置为 0,返回的引用 属于解码器,仅在下一次调用它之前有效 函数或直到关闭或刷新解码器。

    显然,由此得出,您需要将AVCodecContext.refcounted_frames 设置为1。默认值为0,所以我的直觉是您需要将其设置为1,这将解决您的问题。使用后不要忘记在图片上使用av_fame_unref() 以防止内存泄漏,如果got_frame = 0 也不要忘记在此循环中释放您的AVFrame - 再次以防止内存泄漏:

    while (ret) {
      AVFrame* frame = av_frame_alloc();
      ret = readFrame(inputfile,frame,&got_frame);
      if (got_frame) 
        queue.push(frame);
      else
        av_frame_free(frame);
    }
    

    (或者,您也可以为frame 实现一些缓存,这样只有在前一个对象被推入队列时才重新分配它。)

    【讨论】:

    • 谢谢!这正是我的问题!
    【解决方案2】:

    您的伪代码没有明显的问题。几乎可以肯定,问题在于您如何锁定线程之间的队列。

    【讨论】:

    • 感谢您的回答。我忘了说明我暂时搁置了并行化并且队列是按顺序构建的(我编辑了我的问题),
    • 如果您不想发布完整代码,我建议记录推送和弹出的帧指针地址。确保您在两个方向上看到相同的地址序列。
    • 您还可以记录框架的 pts 并验证其单调性。
    【解决方案3】:

    您的内存分配对我来说似乎相同。您是否可能在读取和写入帧之间做其他事情?

    queue 是读取和写入帧的例程中的同一个队列吗?

    【讨论】:

      猜你喜欢
      • 2018-03-14
      • 2020-06-06
      • 2021-02-21
      • 1970-01-01
      • 1970-01-01
      • 2020-04-19
      • 2013-12-21
      • 2015-05-30
      • 1970-01-01
      相关资源
      最近更新 更多