FFMpeg AVBufferPool 的理解与掌握

FFMpeg AVBufferPool 的理解与掌握

深刻理解含义需要调试代码,
但观赏记录可以事半功倍!

pool的总体思路是:
如果pool中没有BufferPoolEntry,则新建
内存释放时,实际数据返回到pool中,由pool将BufferPoolEntry链接起来。
如果再取数据,如果pool中有entry,则使用pool中entry.

调用av_buffer_pool_init函数

分配一个AVBufferPool 实例,返回该实例指针,
其中pool的refcount为1,size 为传入值,以后按此大小分配,alloc 函数默认为av_buffer_alloc 或者传入的内存分配函数


调用av_buffer_pool_get函数
    此时pool->pool为空, 送给buf, pool->pool 是BufferPoolEntry 指针.
    !buf 成立,调用pool_alloc_buffer(pool)
    此函数中调用av_buffer_alloc(pool->size)分配内存,返回AVBufferRef 指针 ret. 也是该函数的返回值
    然后再分配一个BufferPoolEntry实例,此实例的data指向新申请的内存空间,pool保存传来的AVBufferPool。
    该实例指针赋值给AVBuffeRef的ret->buffer->opaque。
    ret->buffer->free   = pool_release_buffer;此句是关键,AVBuffer free时调用该函数
    然后把pool->refcount 增1变成了2.


调用av_buffer_unref函数
    此时传递的参数是AVBufferRef 指针, 其buffer->free 是pool_release_buffer
    将AVBuffer及AVBufferRef结构体对应的内存空间都释放掉,
    在释放数据时,实际调用pool_release_buffer函数。传入b->opaque,b->data, b是AVBuffer指针
    b->opaque(不透明的,模糊的) 是前面传入的BufferPoolEntry 指针, 由此可以拿到pool, 将该opaque加入到pool链表顶部,供后续使用
    pool 的refcount -1, 当减为0时,整个pool 要free. buffer_pool_free(pool).
    
 
再调用av_buffer_pool_get函数
    此时pool->pool 不为空, 是前面分配的BufferPoolEntry实例,付给buf,此时不为空值.
    然后调用av_buffer_create(buf->data,pool->size,pool_rease_buffer,buf,0)
    它创建AVBuffer实例 和 AVBufferRef 实例, 然后 ref.
    buf->data 就是AVBuffer 的buffer->data, pool->size 就是buffer->size pool_rease_buffer 就是buffer->free
    buf 就是buffer->opaque, 0是buffer->flags
    这样就用到了前面释放过的buf->data.
    然后把buf->next 付给 pool->pool,调整pool 链表, buf->next 付给0
    把pool->refcount 增1

其它函数说明: 直接上函数加注释.

void av_buffer_pool_uninit(AVBufferPool **ppool) //uninit 就是调用flush(), 如果pool的refcount=1,就调用free
{
    AVBufferPool *pool   = *ppool;
    buffer_pool_flush(pool);
    if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
        buffer_pool_free(pool);
}

static void buffer_pool_free(AVBufferPool *pool) // 先调用flush, 再销毁mutex, 有free调用free,释放自己结构
{
    buffer_pool_flush(pool);
    ff_mutex_destroy(&pool->mutex);
    if (pool->pool_free)
        pool->pool_free(pool->opaque);
    av_freep(&pool);
}

static void buffer_pool_flush(AVBufferPool *pool) // 如果pool的entry 为真,则释放该entry,同时查看下一条entry
{
    while (pool->pool) {
        BufferPoolEntry *buf = pool->pool;
        pool->pool = buf->next;

        buf->free(buf->opaque, buf->data);
        av_freep(&buf);
    }
}


知识点:
0. 理解的基础还是要先理解AVBuffer 的概念.后面就好办了.
1、AVBuffer的释放器默认为av_buffer_default_free,用在pool时,此值为pool_release_buffer。
2、在AVBuffer结构体定义一个opaque(模糊不确定)变量,然后将BufferPoolEntry指针存放在此opaque中,
    在调用AVBuffer的free函数时, 将此opaque作为参数传递。达到内存回收到pool

付测试代码:

#include <QCoreApplication>
#include <iostream>
#include <iomanip>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/replaygain.h>
#include <libavutil/pixdesc.h>
}
using namespace std;
 
 
/*
1. av_buffer_pool_init
    初始化 内存池
2 av_buffer_pool_get
   从内存池中获取buffer
3.av_buffer_pool_uninit
    释放内存池
4.pool_release_buffer
   回收buffer
 */
 
const int loopCount = 1;        // 通过修改循环次数来测试是否有内存的泄漏
void testAVBufferPool()
{
    std::cout << "\n\n--------------- Test testAVBufferPool  into -------------" <<  std::endl;
    AVBufferPool *pool;
    AVBufferRef *ref0;
    AVBufferRef *ref1;
    AVBufferRef *ref2;
 
    for(int i = 0; i < loopCount; i++)
    {
        pool = av_buffer_pool_init(1*1024*1024, nullptr); //只分配88字节内存池头部
		//会分配1M字节数据内存+sizeof(AVBuffer)+sizeof(AVBufferRef)+sizeof(BufferPoolEntry)
		//数据内存1M
		//sizeof(AVBufferRef)==24
		//sizeof(AVBuffer)==40
		//sizeof(BufferPoolEntry)==40
        ref0 = av_buffer_pool_get(pool); 
        std::cout <<"ref0(p):" << hex << ref0 <<std::endl;
        std::cout << "ref0 size = " <<dec << ref0->size << std::endl;
		// 释放 ref0,只释放了AVBufferRef,AVBuffer, 数据被还回缓冲池,BufferPoolEntry 未释放
        av_buffer_unref(&ref0);  
 
 //从缓冲池中拿到了一项,但还要分配AVBufferRef(24)+ AVBuffer(40)
        ref1 = av_buffer_pool_get(pool);
		//缓冲池中没有空闲项,分配一个全新的.1M+40+24+40
        ref2 = av_buffer_pool_get(pool);
 
        std::cout <<"AVBufferPool" << std::endl;
        std::cout <<"ref0(p):" << hex << ref0 <<std::endl;
        std::cout <<"ref1(p):" << hex << ref1 <<std::endl;   
        std::cout <<"ref2(p):" << hex << ref2 <<std::endl;
        if(ref1)
            std::cout << "av_buffer_get_ref_count(ref1) = " <<  av_buffer_get_ref_count(ref1) << std::endl;
		//分配1M+40+24+40
        ref0 = av_buffer_pool_get(pool);
        av_buffer_pool_uninit(&pool); // 这里没有释放内存,只是把pool的引用计数减1,当然你也可以把它放在后面.
 
// 通过注释该部分来测试是否有内存的泄漏
        av_buffer_unref(&ref0); //数据释放回缓冲池, free (40+24)
        av_buffer_unref(&ref1);//数据释放回缓冲池 , free (40+24)
        av_buffer_unref(&ref2);//数据释放回缓冲池 , free(40+24) 由于缓冲池引用数减为0,触发整个缓冲池释放
		//整个缓冲池此时是3*(1M+40) 内存,在这里被释放,如果把 av_buffer_pool_uninit放到这来触发内存释放,看起来会更顺!
    }
 
    std::cout << "\n--------------- Test testAVBufferPool  leave -------------" <<  std::endl;
 
}
int main()
{
    testAVBufferPool();
    return  0;
}

何时释放缓冲池, 当缓冲池的refcount==0 的时候, 对于此例,是在

 av_buffer_unref(&avBufferRef2); 的时候。而非av_buffer_pool_uninit的时候。

此时的堆栈调用为:

(gdb) bt
  #0  pool_release_buffer (opaque=0x5555555716c0, data=0x7ffff45ee040 "") at libavutil/buffer.c:338
  #1  0x00007ffff7cea7b1 in buffer_replace (dst=0x7fffffffdd28, src=0x0) at libavutil/buffer.c:120
  #2  0x00007ffff7cea808 in av_buffer_unref (buf=0x7fffffffdd28) at libavutil/buffer.c:130
  #3  0x00005555555556bf in testAVBufferPool () at main.cpp:71
  #4  0x000055555555571c in main () at main.cpp:79

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值