在 FFmpeg 中正确分配和填充帧

mFe*_*ein 5 c opencv memory-leaks ffmpeg video-encoding

我正在填充一个Frame用于编码的 BGR 图像,并且出现内存泄漏。我想我找到了问题的根源,但它似乎是一个图书馆问题。由于 FFmpeg 是一个如此成熟的库,我认为我在滥用它,我希望得到有关如何正确使用它的指导。

我正在分配一个Frame使用:

AVFrame *bgrFrame = av_frame_alloc();
Run Code Online (Sandbox Code Playgroud)

后来我在Frame使用中分配了图像:

av_image_alloc(bgrFrame->data, bgrFrame->linesize, bgrFrame->width, bgrFrame->height, AV_PIX_FMT_BGR24, 32);
Run Code Online (Sandbox Code Playgroud)

然后我使用以下方法填充分配的图像:

av_image_fill_pointers(bgrFrame->data, AV_PIX_FMT_BGR24, bgrFrame->height, originalBGRImage.data, bgrFrame->linesize);
Run Code Online (Sandbox Code Playgroud)

originalBGRImageOpenCV在哪里Mat

这显然存在内存泄漏,在相同的指针上av_image_alloc()分配内存并av_image_fill_pointers()分配内存(我可以看到bgrFrame->data[0]调用之间的变化)。

如果我打电话

av_freep(&bgrFrame->data[0]);
Run Code Online (Sandbox Code Playgroud)

After av_image_alloc(),很好,但是如果我在之后调用它av_image_fill_pointers(),程序会崩溃,即使bgrFrame->data[0]不是NULL,我觉得很好奇。

查看 FFmpeg 的av_image_alloc() 源代码,我看到它av_image_fill_pointers()在内部调用了两次,一次分配缓冲区buff......然后在av_image_fill_pointers() 源代码中data[0]被图像指针取代,这是(我认为)内存泄漏的来源,因为data[0]它持有buf从之前的av_image_alloc()电话。

所以这带来了最后一个问题:用图像填充框架的正确方法是什么?.

Spa*_*Bot 4

您应该分配一次框架。

AVFrame* alloc_picture(enum PixelFormat pix_fmt, int width, int height)
{
AVFrame *f = avcodec_alloc_frame();
if (!f)
    return NULL;
int size = avpicture_get_size(pix_fmt, width, height);
uint8_t *buffer = (uint8_t *) av_malloc(size);
if (!buffer) {
    av_free(f);
    return NULL;
}
avpicture_fill((AVPicture *)f, buffer, pix_fmt, width, height);
return f;
}
Run Code Online (Sandbox Code Playgroud)

是的,允许强制转换 (AVPicture*) /sf/answers/1434885161/。在后续帧中,您可以写入此帧。由于您的 OpenCV 原始数据是 BGR 并且您需要 RGB 或 YUV,因此您可以使用sws_scale. 在我的应用程序中,我垂直镜像:

struct SwsContext* convertCtx = sws_getContext(width, height, PIX_FMT_RGB24, c->width, c->height, c->pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
avpicture_fill(&pic_raw, (uint8_t*)pixelBuffer, PIX_FMT_RGB24, width, height);
// flip
pic_raw.data[0] += (height - 1) * pic_raw.linesize[0]; 
pic_raw.linesize[0] *= -1;
sws_scale(convertCtx, pic_raw.data, pic_raw.linesize, 0, height, f->data, f->linesize);
out_size = avcodec_encode_video(c, outputBuffer, outputBufferSize, f);
Run Code Online (Sandbox Code Playgroud)

(您可以根据需要调整 PIX_FMT_RGB24 并在cv::Mat不复制数据的情况下读取。)