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

 

文章探讨了在C++中使用智能指针管理FFmpeg的C风格API资源(如AVFormatContext)的必要性。作者指出,FFmpeg的API常需要传入裸指针或二级指针,且部分API(如avformat_alloc_output_context2)会直接在内部分配内存并覆盖传入的指针,这使得直接使用`std::unique_ptr`配合自定义删除器来管理FFmpeg结构体可能导致内存泄漏或不兼容。文章通过`avformat_open_input`和`avformat_alloc_output_context2`的例子,质疑了智能指针在FFmpeg开发中的实际应用价值和便捷性,并寻求专业解答。

💡 FFmpeg API常采用C风格,需要传入裸指针或二级指针,这与C++智能指针的 RAII(资源获取即初始化)原则存在冲突,使得直接管理变得复杂。

⚠️ `avformat_open_input`函数需要一个二级指针(`AVFormatContext **ps`),虽然可以通过`fmt.get()`获取裸指针并传入其地址,但这种方式在代码风格上不够优雅,且需要额外处理。

❌ `avformat_alloc_output_context2`函数在内部会直接分配内存并赋值给传入的二级指针(`*avctx = s`),如果此时`avctx`指向的`AVFormatContext`对象已经被`std::unique_ptr`管理,那么`avformat_alloc_output_context2`的内部操作将导致原有的`std::unique_ptr`所管理的内存发生泄漏。

🤔 鉴于FFmpeg API的特性,如内部内存分配和覆盖指针的操作,直接使用`std::unique_ptr`管理FFmpeg数据结构的需求和可行性受到质疑,可能需要更底层的裸指针管理或特定的适配层。

❓ 作者希望了解在C++中处理FFmpeg资源时,智能指针管理的真正意义和最佳实践,以及如何在避免内存泄漏和保持代码优雅之间取得平衡。

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适配
相关文章