ffmpeg buffer管理


除了内存管理之外,数据的前后级流转也涉及到buffer管理. 个人觉得ffmpeg里面的buffer管理实现极为巧妙,也很值得借鉴.

概述

image

重要数据结构

AVBufferPool

在libavutil/buffer_internal.h中定义,为内部数据结构,不能在应用程序中直接引用.

image

    • mutex 用于多线程资源保护
    • pool  指向BufferPoolEntry链表的头,AVBufferPool可以管理所有的BufferPoolEntry.
    •           在初始化AVBufferPool时pool是空,只有在av_buffer_pool_get()时才会判断
    •           和创建.如图av_buffer_pool_get函数流程:

image

    • refcount AVBufferPool的引用次数,初始化之后为1,av_buffer_pool_get()会+1,
    •                 pool_release_buffer()  av_buffer_pool_uninit()会-1. 为0时会buffer_pool_free
    •                释放pool
    • size  不是指缓存池的个数,而是指AVBuffer.size
    • opaque 用户自定义需要传递给回调函数参数,对应pool_free回调函数的opaque参数
    • allo alloc2 提供两种内存分配方式,1: 注册回调函数  2: av_buffer_alloc
    • pool_free  注册回调函数,av_buffer_pool_init2()注册,用于指定一种更负责的内存分配器。

BufferPoolEntry

在libavutil/buffer_internal.h中定义,为内部数据结构,不能在应用程序中直接引用.

从数据结构中开看到BufferPoolEntry就是一个链表,AVBufferPool中存储了BufferPoolEntry根节点.

image

    • data opaqu free 以上的BufferPoolEntry的opaque、data、free都是指向了AVBuffer最
    •                              开始的opaque、data、free,然后AVBuffer自己的opaque和free则指
    •                              向了BufferPoolEntry和pool_release_buffer
    • pool  当前BufferPoolEntry指向创建他的AVBufferPool. 这样每个BufferPoolEntry都可
    •                             以找到创建和管理他的AVBufferPool
    • next  
    • buffer 实际存储buffer相关信息,新版本才增加的

AVBufferRef

在libavutil/buffer.h中定义,是对外的,可以在应用程序中直接引用.

image

    • *buffer
    • *data size AVBufferRef.data和size与AVBuffer.data和size是一致的

       有两种方式创建AVBufferRef,一种是通过av_buffer_alloc,只要指定大小即可;另一种是

通过av_buffer_create,这种主要是用于已经有了数据的情况,同时它也支持自定义释放此

数据内存的方法

AVBuffer

在libavutil/buffer_internal.h中定义,为内部数据结构,不能在应用程序中直接引用.

AVBuffer对外不公开,必须要通过AVBufferRef来间接使用AVBuffer.

image

    • *data 实际存放数据的地址
    • size   实际存放数据的大小
    • refcount 记录当前buffer的引用计数
    • free  回调函数用于释放数据,当引用计数变为0时会被调用来释放内存。
    • opaque 用户自定义需要传递给回调函数参数,对应了回调函数的opaque参数
    • flags 标识一些属性 , AV_BUFFER_FLAG_READONLY
    •                         表示只读,引用计数为1时表示只有一个对象引用它,此时是可写的,否则就是只读的
    • flags_internal 标识一些buffer属性, 有BUFFER_FLAG_REALLOCATABLE, BUFFER_FLAG_NO_FREE两个。
    •                         BUFFER_FLAG_REALLOCATABLE标识是否可以重分配
    •                         BUFFER_FLAG_NO_FREE标识被其他结构体引用,不能释放.需要手动释放内存.
    •          新版本中
    •                          1.  BUFFER_FLAG_REALLOCATABLE移动到了flags_internal  
    •                          2. BUFFER_FLAG_READONLY改为了AV_BUFFER_FLAG_READONLY
    •                           3. 新增加了BUFFER_FLAG_NO_FREE

       AVBuffer在创建的时候引用计数为1,当调用av_buffer_ref()对其进行操作时,引用计数+1,当

av_buffer_unref对其操作时则引用计数-1(当-1后引用计数为0时,av_buffer_unref将自动释放分配的数据缓存

定义在libavutil/buffer.c中操作接口

Buffer管理

             以上的BufferPoolEntry的opaque、data、free都是指向了AVBuffer最开始的opaque、data、free,

然后AVBuffer自己的opaque和free则指向了BufferPoolEntry和pool_release_buffer为何这样设计呢,

因为AVBuffer最终要进行释放的话,那还是得调用它自己最本来的free函数,但是此时由于要放到

缓存池管理,因此在free时不能真正把AVBuffer释放了,因此用BufferPoolEntry来保存AVBuffer真正

的释放内存的函数,然后再用缓存池释放函数pool_release_buffer来代替free函数,这样,当用户

释放的时候,AVBuffer的free函数已经指向了pool_release_buffer函数,因此可以在pool_release_buffer

里把AVBuffer返回给缓存池,等到缓存池自己想要被释放的时候,这个时候缓存池就从BufferPoolEntry

把之前保存的真正释放AVBuffer的函数取出来,进行调用;不得不说,这个设计虽然非常绕,其实很巧妙.

image

如图:

  • 365行,调用pool.alloc分配一个新的AVBufferRef
  • 370行,  分配一个BufferPoolEntry
  • 376-379行,将AVBufferRef的data, opaque,  free赋值给BufferPoolEntry
  • 381-382行,重新赋值AVBuffer.opaque和AVBuffer.free, pool_release_buffer来代替free函数,
  •                         用户释放的时并不会真正释放内存而是通过pool_release_buffer将AVBuffer返回
  •                       给了缓存池.当用于真正需要释放内存时,再调用BufferPoolEntry.free实现真正意义释放.
  • 以上机制对用户不可见,巧妙将内存释放和缓存池回收buffer结合在一起了.


有一个写的很好的播客,不过对应的ffmpeg版本要旧一点, 在ffmpeg 5.0版本中有些结构体和实现方式有变化.

https://blog.csdn.net/huweijian5/article/details/105549692/

2022-05-16

posted @   追随技术  阅读(685)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示