流媒体开发18FFmpeg内存模型

Posted 叮咚咕噜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了流媒体开发18FFmpeg内存模型相关的知识,希望对你有一定的参考价值。

本文介绍了AVPacket和AVFrame两块buffer是怎么管理的,并梳理了所有相关的接口,介绍这些接口内部 具体实现了什么。

一、引出



问题:
1、从av_read_frame读取到一个AVPacket后怎么放入队列?
2、从avcodec_recevice_frame读取到一个AVFrame后又怎么放入队列?

二、拷贝

从现有的Packet拷贝一个新Packet的时候,有两种情况:

  • 浅拷贝:两个Packet的buf引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题;
  • 深拷贝:两个Packet的buf引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy;

三、引用计数与结构体层级

本节我们只讨论浅拷贝,packet1和和packet2都不使用时才能释放,增加引用计数的概念,每次拷贝都会将计数器加1,释放时引用计数减1,代码中的定义如下:


结构体AVPacket和AVFrame都包含结构体AVBufferRef,AVBufferRef中包含指针AVBuffer,AVBuffer的定义如下:

struct AVBuffer {
    uint8_t *data; /**< 实际的内存指针地址 */
    size_t size; /**< 申请的内存大小 */
    atomic_uint refcount;		/* 引用计数,用于管理内存的申请和释放 */

    /**
     * a callback for freeing the data
     */
    void (*free)(void *opaque, uint8_t *data);

    /**
     * an opaque pointer, to be used by the freeing callback
     */
    void *opaque;

    /**
     * A combination of AV_BUFFER_FLAG_*
     */
    int flags;

    /**
     * A combination of BUFFER_FLAG_*
     */
    int flags_internal;
};
  • 对于多个AVPacket共享同一个缓存空间,FFmpeg使用的引用计数的机制(reference-count):
    • 初始化引用计数为0,只有真正分配AVBuffer的时候,引用计数初始化为1;
    • 当有新的Packet引用共享的缓存空间时,就将引用计数+1;
    • 当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放掉引用的缓存空间AVBuffer。
  • AVFrame也是采用同样的机制

四、常用API

4.1AVPacket常用API


av_packet_ref和 av_packet_move_ref的区别:
av_packet_ref最终指向同一个AVBuffer,av_packet_move_ref则是删除AVPacket1中对AVBufferRef的引用(AVBufferRef会置为NULL),而是AVPacket2会指向到AVPacket1的AVBufferRef

4.2AVFrame常用API

五、demo级验证

1、av_packet_free内部会调用av_packet_unref,av_packet_unref重复调用也没关系 ,每判空,av_packet_unref中是将AVPacket的内容清空

void av_packet_test1()
{
    AVPacket *pkt = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE); // 引用计数初始化为1
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
    av_packet_unref(pkt);       // 要不要调用
    av_packet_free(&pkt);       // 如果不free将会发生内存泄漏,内部调用了 av_packet_unref
}

2、av_init_packet会将AVPacket清空,不能随意调用,可能造成内存的泄露

void av_packet_test2()
{
    AVPacket *pkt = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
//    av_init_packet(pkt);        // 这个时候init就会导致内存无法释放
    av_packet_free(&pkt);
}

3、av_packet_clone等价于av_packet_alloc()+av_packet_ref()

void av_packet_test4()
{
    AVPacket *pkt = NULL;
    // av_packet_alloc()没有必要,因为av_packet_clone内部有调用 av_packet_alloc
    AVPacket *pkt2 = NULL;
    int ret = 0;

    pkt = av_packet_alloc();
    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);
    pkt2 = av_packet_clone(pkt); // av_packet_alloc()+av_packet_ref()
    av_init_packet(pkt);
    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}

4、多次ref到同一个AVPacket中会导致后面释放时内存泄露

av_packet_ref(pkt, pkt2);
av_packet_ref(pkt, pkt2);     // 多次ref如果没有对应多次unref将会内存泄漏

av_packet_unref(pkt);	//调过一次之后,AVPacket会设置为0,后面再次调用发现为0,就不会减引用了 

以上是关于流媒体开发18FFmpeg内存模型的主要内容,如果未能解决你的问题,请参考以下文章

FFmpeg SDK开发模型之中的一个:解码器

流媒体开发19FFMpeg解封装流程分析

流媒体开发19FFMpeg解封装流程分析

流媒体开发19FFMpeg解封装流程分析

流媒体开发19FFMpeg解封装流程分析

TSINGSEE青犀视频通道FFmpeg获取视频片段的方法介绍