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

 

文章探讨了在C++中使用智能指针管理FFmpeg的C风格API数据结构时遇到的挑战。作者指出,虽然可以通过自定义删除器将FFmpeg的释放函数(如`avformat_free_context`)绑定到`std::unique_ptr`,但FFmpeg的一些API(如`avformat_open_input`和`avformat_alloc_output_context2`)需要传入二级裸指针,并且内部会直接分配或修改指针指向的内存。这导致在直接使用`std::unique_ptr`管理这些上下文时,可能会出现内存泄漏或使用不当的问题,引发了对智能指针管理FFmpeg数据结构必要性的质疑。

📝 **FFmpeg API的C风格与C++智能指针的潜在冲突**:FFmpeg的API设计遵循C语言风格,其核心数据结构(如`AVFormatContext`、`AVCodecContext`)通常通过裸指针 (`*`) 或二级指针 (`**`) 进行传递和管理。当开发者尝试在C++环境中使用`std::unique_ptr`等智能指针来自动管理这些资源时,会遇到兼容性问题。智能指针的 RAII(Resource Acquisition Is Initialization)特性旨在确保资源在作用域结束时被安全释放,但这需要智能指针能够准确地接管和释放裸指针所指向的内存。

💡 **`avformat_alloc_context`与`std::unique_ptr`的初步结合**:作者首先尝试使用`std::unique_ptr`配合自定义删除器来管理通过`avformat_alloc_context()`分配的`AVFormatContext`。例如,定义了一个lambda表达式作为删除器,调用`avformat_free_context()`来释放内存。这种方式在资源分配和释放的生命周期管理上似乎是可行的,为后续的讨论奠定了基础。

⚠️ **`avformat_open_input`带来的挑战:二级指针的传递**:当使用`avformat_open_input`这个API时,它需要一个`AVFormatContext **`类型的参数。这意味着需要传递一个指向`AVFormatContext*`的指针。即使`AVFormatContext`已经被`std::unique_ptr`管理,也需要通过`fmt.get()`获取裸指针,并将其地址传递给API。虽然这种做法在技术上可行,但它使得智能指针的封装性受到一定影响,代码可读性也可能降低,让开发者开始怀疑其必要性。

💥 **`avformat_alloc_output_context2`引发的内存泄漏风险**:更严峻的问题出现在`avformat_alloc_output_context2`这个API上。该API同样需要一个`AVFormatContext **`参数,但它会在内部直接将传入的指针指向新的内存地址(`*avctx = s;`),并且不检查原始指针是否有效。如果此时传入的是一个已经被`std::unique_ptr`管理的裸指针的地址,那么`std::unique_ptr`管理的原有内存将无法被正确释放,因为API直接覆盖了指针,导致内存泄漏。这使得使用智能指针管理此类FFmpeg上下文的安全性大打折扣。

🤔 **对智能指针管理FFmpeg数据结构的质疑**:鉴于上述API的设计特点,作者对在C++中是否仍有必要或可行使用智能指针来管理FFmpeg的数据结构产生了深刻的质疑。FFmpeg的API在设计上似乎并未考虑C++的智能指针模型,其内部的内存管理机制(直接分配、覆盖指针)与智能指针的自动管理逻辑存在天然的冲突,这使得在实际应用中需要非常谨慎地处理,甚至可能需要回归传统的裸指针管理方式。

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设计
相关文章