ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

(音视频学习笔记):FFmpeg内存模型及AVPacket、AVFrame常用API

2021-01-17 17:01:05  阅读:440  来源: 互联网

标签:__ AVPacket FFmpeg pkt frame packet 音视频 av


目录

FFmpeg内存模型

FFmpeg内存模型-引用计数

AVPacket常用API

AVFrame常用API

API应用简介

FFmpeg内存模型

  • 从av_read_frame读取到一个AVPacket后怎么放入解码器队列?
  • 从avcodec_recevice_frame读取到一个AVFrame后又怎么放入解压后的帧队列?
     
  • 从现有的Packet拷贝一个新Packet的时候,有两种情况
    • ①两个Packet的buf引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题。
    • ②两个Packet的buf引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy。

  • 内存模型

  • 更为精确的模型

FFmpeg内存模型-引用计数

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

AVPacket常用API

AVPacket *av_packet_alloc(void);分配AVPacket这个时候和buffer没有关系
void av_packet_free(AVPacket **pkt);释放AVPacket和_alloc对应
void av_init_packet(AVPacket *pkt);初始化AVPacket只是单纯初始化pkt字段
int av_new_packet(AVPacket *pkt, int size);给AVPacket的buf分配内存, 引用计数初始化为1
int av_packet_ref(AVPacket *dst, const AVPacket *src);增加引用计数
void av_packet_unref(AVPacket *pkt);减少引用计数
void av_packet_move_ref(AVPacket *dst, AVPacket *src);转移引用计数
AVPacket *av_packet_clone(const AVPacket *src);等于av_packet_alloc()+av_packet_ref(

AVFrame常用API

AVFrame *av_frame_alloc(void);分配AVFrame
void av_frame_free(AVFrame **frame);释放AVFrame
int av_frame_ref(AVFrame *dst, const AVFrame *src);增加引用计数
void av_frame_unref(AVFrame *frame);减少引用计数
void av_frame_move_ref(AVFrame *dst, AVFrame *src);转移引用计数
int av_frame_get_buffer(AVFrame *frame, int align);根据AVFrame分配内存
AVFrame *av_frame_clone(const AVFrame *src);等于av_frame_alloc()+av_frame_ref()

 

API应用简介

  • av_packet_alloc 简单的创建一个AVPacket,将其字段设为默认值(data为空,没有数据缓存空间),data的指针需要另外去赋值
AVPacket *av_packet_alloc(void);
int av_new_packet(AVPacket *pkt, int size);
  • 释放使用 av_packet_alloc 创建的AVPacket,如果该Packet有引用计数(packet->buf不为空),则先调用av_packet_unref(&packet)
    • 只有当引用计数为0时,才会在调用av_packet_free()时释放data的缓存。
void av_packet_free(AVPacket **pkt);
  • av_init_packet 初始化packet的值为默认值,该函数不会影响buffer引用的数据缓存空间和size,需要单独处理。
    • AVPacket中的buf为空。
    • 比如 av_get_packet里调用av_init_packet
void av_init_packet(AVPacket *pkt);
  • av_packet_ref 使用引用计数的浅拷贝
    • 该函数会先拷贝所有非缓存类数据,然后创建一个src->buf的新的引用计数。
    • 如果src已经设置了引用计数发(src->buf不为空),则直接将其引用计数+1。
    • 如果src没有设置引用计数(src->buf为空),则为dst创建一个新的引用计数buf,并复制src->data到buf->buffer中。
    • 最后,复制src的其他字段到dst中。所以av_packet_ref()是将2个AVPacket共用一个缓存的。
int av_packet_ref(AVPacket *dst, const AVPacket *src);
  • av_packet_unref 使用引用计数清理数据
    • 将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。
    • 如果引用计数为0,自动的释放缓存空间
void av_packet_unref(AVPacket *pkt);
  • av_packet_move_ref 把src整个结构体直接赋值给dst,所以引用计数没有发生变化,并且src被av_init_packet重置
    • 其功能是 av_packet_alloc() + av_packet_ref();
void av_packet_move_ref(AVPacket *dst, AVPacket *src);
  • av_packet_clone 先创建一个新的AVPacket,然后再进行计数引用+数据拷贝,使得新的AVPacket指向老的AVPacket同一个data。
AVPacket *av_packet_clone(const AVPacket *src);
  • AVPacket案例:
/**
 * @brief 测试av_packet_alloc和av_packet_free的配对使用
 */
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
}

/**
 * @brief 测试误用av_init_packet将会导致内存泄漏
 */
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);
}

/**
 * @brief 测试av_packet_move_ref后,可以av_init_packet
 */
void av_packet_test3()
{
    AVPacket *pkt = NULL;
    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_alloc();   // 必须先alloc
    av_packet_move_ref(pkt2, pkt);//内部其实也调用了av_init_packet
    av_init_packet(pkt);
    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}
/**
 * @brief 测试av_packet_clone
 */
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);
}

/**
 * @brief 测试av_packet_ref
 */
void av_packet_test5()
{
    AVPacket *pkt = NULL;
    AVPacket *pkt2 = NULL;
    int ret = 0;

    pkt = av_packet_alloc(); //
    if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt->buf));
    }

    ret = av_new_packet(pkt, MEM_ITEM_SIZE);
    if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt->buf));
    }
    memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);

    pkt2 = av_packet_alloc();   // 必须先alloc
    av_packet_move_ref(pkt2, pkt); // av_packet_move_ref
//    av_init_packet(pkt);  //av_packet_move_ref

    av_packet_ref(pkt, pkt2);
    av_packet_ref(pkt, pkt2);     // 多次ref如果没有对应多次unref将会内存泄漏
    if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt->buf));
    }
    if(pkt2->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt2->buf));
    }
    av_packet_unref(pkt);   // 将为2
    av_packet_unref(pkt);   // 做第二次是没有用的
    if(pkt->buf)
        printf("pkt->buf没有被置NULL\n");
    else
        printf("pkt->buf已经被置NULL\n");
    if(pkt2->buf)        // 打印referenc-counted,必须保证传入的是有效指针
    {    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,
               av_buffer_get_ref_count(pkt2->buf));
    }
    av_packet_unref(pkt2);


    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}

/**
 * @brief 测试AVPacket整个结构体赋值, 和av_packet_move_ref类似
 */
void av_packet_test6()
{
    AVPacket *pkt = NULL;
    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_alloc();   // 必须先alloc
    *pkt2 = *pkt;   // 有点类似  pkt可以重新分配内存
    av_init_packet(pkt);

    av_packet_free(&pkt);
    av_packet_free(&pkt2);
}
  • AVFrame案例测试
void av_frame_test()
{
    AVFrame *frame = NULL;
    int ret = 0;

    frame = av_frame_alloc();// 没有类似的AVPacket的av_new_packet的API
    // 1024 *2 * (16/8) =
    frame->nb_samples     = 1024;
    frame->format         = AV_SAMPLE_FMT_S16;//AV_SAMPLE_FMT_S16P AV_SAMPLE_FMT_S16
    frame->channel_layout = AV_CH_LAYOUT_STEREO;    //AV_CH_LAYOUT_MONO AV_CH_LAYOUT_STEREO
    ret = av_frame_get_buffer(frame, 0);    // 根据格式分配内存
    if(frame->buf && frame->buf[0])
        printf("%s(%d) 1 frame->buf[0]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[0]->size);    //受frame->format等参数影响
    if(frame->buf && frame->buf[1])
        printf("%s(%d) 1 frame->buf[1]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[1]->size);    //受frame->format等参数影响

    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
        printf("%s(%d) ref_count1(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));

    ret = av_frame_make_writable(frame);    // 当frame本身为空时不能make writable
    printf("av_frame_make_writable ret = %d\n", ret);

    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
        printf("%s(%d) ref_count2(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));

    av_frame_unref(frame);
    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
        printf("%s(%d) ref_count3(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));

    av_frame_free(&frame);
}

 

标签:__,AVPacket,FFmpeg,pkt,frame,packet,音视频,av
来源: https://blog.csdn.net/baidu_41388533/article/details/112736236

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有