V2EX 07月22日
[C++] 用智能指针管理 ffmpeg 中的数据结构是有必要的吗?
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文探讨了在C++中使用智能指针管理FFmpeg(如AVFormatContext、AVCodecContext等)资源的实践。作者提出,由于FFmpeg的API多采用C风格的裸指针,并且部分API(如avformat_open_input和avformat_alloc_output_context2)需要传入二级指针,甚至在内部直接分配和覆盖指针,这使得直接使用`std::unique_ptr`并自定义删除器的方式面临挑战。文章通过`avformat_open_input`和`avformat_alloc_output_context2`的例子,指出了直接管理可能存在的内存泄漏风险,并引发了对是否仍有必要使用智能指针来管理FFmpeg数据结构的疑问,寻求社区的解答。

🎯 FFmpeg API风格与C++智能指针的兼容性问题:FFmpeg的API主要采用C语言风格,大量使用裸指针。当在C++中使用时,自然会想到利用`std::unique_ptr`等智能指针进行资源管理,通过自定义删除器来调用FFmpeg提供的释放函数(如`avformat_free_context`)。

⚠️ 二级指针与智能指针的冲突:FFmpeg中的一些关键API,如`avformat_open_input`和`avformat_alloc_output_context2`,要求传入的是指向指针的指针(二级指针)。这使得直接将`std::unique_ptr`转换为可用参数变得复杂,例如需要通过`ptr.get()`获取裸指针,然后取地址传递。

❗ 内部内存管理导致智能指针失效:更严重的是,`avformat_alloc_output_context2`这类API会在函数内部直接分配内存并赋给传入的二级指针,且不检查原指针状态。这会导致即使智能指针已经管理了内存,该API的行为也会覆盖掉智能指针的原始指针,造成原先由智能指针管理的内存无法被正确释放,从而引发内存泄漏。

🤔 对智能指针管理必要性的质疑:鉴于上述API的行为模式,作者对使用C++智能指针来管理FFmpeg数据结构的必要性产生了深刻怀疑。文章的核心问题在于,当FFmpeg的API设计本身就可能绕过或破坏智能指针的正常管理机制时,投入精力使用智能指针是否反而增加了不必要的复杂性和潜在风险。

ffmpeg 的 api 和 数据结构都是 c 风格,当我在 c++ 中使用它们时,很自然就想到用智能指针去管理(例如 AVFormatContext*AVCodecContext* 等),因为可以自定义删除器,将 ffmpeg 提供的 free 操作放进去;但 ffmpeg 中的一些 api 需要传入裸指针,一些 api 甚至会在内部直接分配空间,这样子用智能指针管理的想法会不会是没有必要的?

AVFormatContext 来举例,正常可以像这样得到一个被智能指针管理的 AVFormatContext 结构

auto deleter = [](AVFormatContext* f){    if(f) avformat_free_context(f);};std::unique_ptr<AVFormatContext, decltype(deleter)> fmt(avformat_alloc_context(), deleter);

但和它相关的一个 api 是 avformat_open_input,它的函数声明如下(以下贴出一部分实现)

int avformat_open_input(AVFormatContext **ps, const char *url,                        const AVInputFormat *fmt, AVDictionary **options);                        // demux.c 下 avformat_open_input 的一部分实现int avformat_open_input(AVFormatContext **ps, const char *filename,                        const AVInputFormat *fmt, AVDictionary **options){    ...    AVFormatContext *s = *ps;    ...    if (!s && !(s = avformat_alloc_context()))        return AVERROR(ENOMEM);    ...}

可以看到 avformat_open_input 需要一个二级指针,所以需要直接传入裸指针。如果想要将一个初始化的 unique_ptr<AVFormatContext> 搭配 avformat_open_input 使用,就需要像这样(网上看到的做法)

auto deleter = [](AVFormatContext* f){    if(f) avformat_free_context(f);};std::unique_ptr<AVFormatContext, decltype(deleter)> fmt(avformat_alloc_context(), deleter);auto tmp = fmt.get();avformat_open_input(&tmp, ...);

到这里我就开始怀疑用 unique_ptr 管理 AVFormatContext 的意义了,不过以上这个例子还好,只是观感上没那么优雅。但以下的例子让我质疑用智能指针做管理的必要。

AVFormatContext 还有一个相关的 api 是 avformat_alloc_output_context2,以下是函数声明和部分实现:

int avformat_alloc_output_context2(AVFormatContext **ctx, const AVOutputFormat *oformat,                                   const char *format_name, const char *filename);                                                                      int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat *oformat,                                   const char *format, const char *filename){    AVFormatContext *s = avformat_alloc_context();    int ret = 0;    *avctx = NULL;    ...    *avctx = s;}

可以看到,avformat_alloc_output_context2 同样需要传入二级指针,但与 avformat_open_input 的区别在于,它内部直接将 *avctx = NULL,并没有判断其是否为空,同时还将分配了新的内存地址给 avctx,这也就意味着以下的操作会造成内存泄漏:

auto deleter = [](AVFormatContext* f){    if(f) avformat_free_context(f);};std::unique_ptr<AVFormatContext, decltype(deleter)> fmt(avformat_alloc_context(), deleter);auto tmp = fmt.get();avformat_alloc_output_context2(&tmp, ...);

至此让我产生用智能指针管理 ffmpeg 数据结构的必要性,有没有大佬来解答一下。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

FFmpeg C++ 智能指针 内存管理 API设计
相关文章