用智能指针管理 ffmpeg 中的数据结构是有必要的吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zcion
V2EX    C++

用智能指针管理 ffmpeg 中的数据结构是有必要的吗?

  •  
  •   zcion 79 天前 2790 次点击
    这是一个创建于 79 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 数据结构的必要性,有没有大佬来解答一下。

    24 条回复    2025-07-24 10:30:19 +08:00
    codehz
        1
    codehz  
       79 天前
    现在不是有很多 c++ wrapper 吗,ffmpeg 的 c 接口变化也不是很大,直接用现成的包装器就好了
    zcion
        2
    zcion  
    OP
       79 天前
    @codehz 能举例说一下吗,这一块不是很懂,谢谢
    itechify
        3
    itechify  
    PRO
       79 天前
    把你原文复制给 ai ,他会很清晰的说明
    rainbowhu
        4
    rainbowhu  
       79 天前
    完全没必要。个人的观点是 c 风格的 api 直接用就行了,大不了封装一些功能类的时候写成 c++。如果只是把 c 的 api 转成 c++,没有任何增加的新内容,完全是给自己找麻烦了。c++的很多语法看着很爽,其实都是有固定场景的,场景不对就各种用着不爽,一直不喜欢这些花里胡哨的东西。
    NessajCN
        5
    NessajCN  
       79 天前
    确实没必要,手动 alloc free 吧
    johnnyyeen
        6
    johnnyyeen  
       79 天前
    写了 10 多年 C++代码,唯一生产实践中用类似智能指针机制的场景,是 COM/ATL 相关的开发。
    从来没用过 C++的智能指针。
    引入的复杂度比带来的好处多不了多少。
    qieqie
        7
    qieqie  
       79 天前
    先传入裸指针调`avformat_alloc_output_context2` 做内存分配, 再把所有权转移给 unique_ptr
    wesleywaters
        8
    wesleywaters  
       79 天前   1
    RAII 原则要在资源生命周期与对象生命周期匹配时才 make sense ,你觉得别扭的原因就是因为你只是用智能指针声明了一个对象,但这个对象实际蕴含的资源生命周期和智能指针对象并不 match ,甚至其原生的管理接口与智能指针语义相悖。

    合理的做法是大约有两种

    1 、不用多纠结这个问题,C 风格的接口就对应 C 风格的调用方式,只是和项目种其他现代风格的代码模块做必要区隔即可。
    2 、给 ffmpeg 写一个 wrapper ,以你说的 AVFormatContext 为例,可以在 ffmpeg api 的基础上提供一个新的封装类 A ,将 AVFormatContext 相关的资源与操作都封装在其中,确保 A 的构造、析构等方法正确管理底层资源。同时,业务代码只通过 A 来管理这些资源或调用这些操作。此时,你在业务代码中声明 A 的智能指针对象并进行管理就不会觉得别扭了,也符合 RAII 的真实语义。
    MIUIOS
        9
    MIUIOS  
       79 天前
    @oneisall8955 AI 不是万能的
    valord577
        10
    valord577  
       79 天前
    以下仅个人工作相关的观点哈:


    1. 如果是自有的产品 能完全掌控部署和编译环境 那么只要内部团队协商好就行。
    2. 如果是提供 sdk 动态库给客户用,一定是提供 c 的 api 。原因有二,abi 稳定 客户可以用自己的编译器 / api 符号稳定。至于 sdk 内部语言用 c 还是 c++都无所谓 如果用 c++实现的 sdk 切记打包静态 c++运行时(libstdc++.a/libc++.a) 不然客户那边会有一大堆问题
    aiyolo
        11
    aiyolo  
       79 天前
    @johnnyyeen 不用智能指针,用 c++干吗呢
    unused
        12
    unused  
       79 天前
    没看懂最后一个为什么会泄露。fmt 还是原来的 fmt ,最后 fmt.reset(tmp) 就行了
    msg7086
        13
    msg7086  
       79 天前
    C 风格指针你用 C++那套来管理就是会别扭的。
    真要用 C++那套那你得把 C 指针那些丑陋的部分包装起来,但这样也就变成 1 楼说的那种了……
    minami
        14
    minami  
       79 天前 via Android
    如果你有做资源池的话可以用智能指针管理,如果没有的话,用 defer 的形式更方便
    ysc3839
        15
    ysc3839  
       79 天前 via Android
    有必要。假如有个轻量的 FFmpeg C++封装库的话,我会使用。
    但如果找不到现成的库,就比较尴尬了,我会看需求自己写。

    顺带一提,你那么写 deleter 似乎会导致最终的 unique_ptr 变大。

    @rainbowhu 不用智能指针的话,错误处理会麻烦很多,也有可能忘记释放。
    kneep
        16
    kneep  
       79 天前
    没有必要,直接用它原生的风格就好。C 语言的库,API 分配内存,使用者来释放,很常见,也不难理解。硬要用智能指针,会使代码更能难读。
    aminobody
        17
    aminobody  
       79 天前
    也许你更需要 scope_guard 这样的东西, 而不是智能指针.
    nightwitch
        18
    nightwitch  
       79 天前
    这种可能会获取到资源的 allloc 函数,就先按 C 风格调,然后 reset 给一个 unique_ptr 管理就行了。
    zcion
        19
    zcion  
    OP
       79 天前
    感谢各位大佬的意见
    wnpllrzodiac
        20
    wnpllrzodiac  
       78 天前 via Android
    ffmpeg 用 c 实现了智能指针和其他 cpp 高级特性
    mirrorman
        21
    mirrorman  
       78 天前
    通过智能指针管理资源的生命周期,让对象和资源的生命周期绑定,让资源的生命周期和有效性受到控制,至于在接口中传递的情况,使用裸指针、还是引用、还是仍旧使用智能指针,这反而是次要的,你仍然可以使用 unique_ptr 和 shared_ptr 的 get()接口来获得包裹的原始指针去与其他接口交互
    mirrorman
        22
    mirrorman  
       78 天前
    而且建议在使用 unique_ptr 的时候,Deleter 以 factor 的方式定义:
    struct Deleter { void operator()(T* p) {//...} };
    这样构造的时候不需要再传入一个 Deleter 的实例,而且 unique_ptr 的内存占用跟裸指针就是一样大的;不然内部还会保存一个 Deleter 实例,unique_ptr 的内存占用会变大。
    johnnyyeen
        23
    johnnyyeen  
       77 天前
    @aiyolo 智能指针根本一点都不智能,而且同样也容易 out of control 。
    johnnyyeen
        24
    johnnyyeen  
       77 天前
    在我看来,智能指针无非就是为了应付针对指针的诟病,临时打的一个补丁,而且还没补好。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3721 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 00:51 PVG 08:51 LAX 17:51 JFK 20:51
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86