V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zcion
V2EX  ›  C++

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

  •  
  •   zcion · 10 天前 · 1926 次点击

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

    合理的做法是大约有两种——

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


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

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

    @rainbowhu 不用智能指针的话,错误处理会麻烦很多,也有可能忘记释放。
    kneep
        16
    kneep  
       10 天前
    没有必要,直接用它原生的风格就好。C 语言的库,API 分配内存,使用者来释放,很常见,也不难理解。硬要用智能指针,会使代码更能难读。
    aminobody
        17
    aminobody  
       10 天前
    也许你更需要 scope_guard 这样的东西, 而不是智能指针.
    nightwitch
        18
    nightwitch  
       10 天前
    这种可能会获取到资源的 allloc 函数,就先按 C 风格调,然后 reset 给一个 unique_ptr 管理就行了。
    zcion
        19
    zcion  
    OP
       10 天前
    感谢各位大佬的意见
    wnpllrzodiac
        20
    wnpllrzodiac  
       9 天前 via Android
    ffmpeg 用 c 实现了智能指针和其他 cpp 高级特性
    mirrorman
        21
    mirrorman  
       9 天前
    通过智能指针管理资源的生命周期,让对象和资源的生命周期绑定,让资源的生命周期和有效性受到控制,至于在接口中传递的情况,使用裸指针、还是引用、还是仍旧使用智能指针,这反而是次要的,你仍然可以使用 unique_ptr 和 shared_ptr 的 get()接口来获得包裹的原始指针去与其他接口交互
    mirrorman
        22
    mirrorman  
       9 天前
    而且建议在使用 unique_ptr 的时候,Deleter 以 factor 的方式定义:
    struct Deleter { void operator()(T* p) {//...} };
    这样构造的时候不需要再传入一个 Deleter 的实例,而且 unique_ptr 的内存占用跟裸指针就是一样大的;不然内部还会保存一个 Deleter 实例,unique_ptr 的内存占用会变大。
    johnnyyeen
        23
    johnnyyeen  
       8 天前
    @aiyolo 智能指针根本一点都不智能,而且同样也容易 out of control 。
    johnnyyeen
        24
    johnnyyeen  
       8 天前
    在我看来,智能指针无非就是为了应付针对指针的诟病,临时打的一个补丁,而且还没补好。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2178 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 00:30 · PVG 08:30 · LAX 17:30 · JFK 20:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.