av_parse_pars2() 代码内存使用分析

------------------------------------------------------------
author: hjjdebug
date: 2024年 07月 30日 星期二 14:01:27 CST
description: av_parse_pars2() 代码内存使用分析
------------------------------------------------------------

int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx,
                     uint8_t **poutbuf, int *poutbuf_size,
                     const uint8_t *buf, int buf_size,
                     int64_t pts, int64_t dts, int64_t pos)

演示代码:
如果你有兴趣阅读本博,你一定知道这个函数的定义, 你也会有它的使用代码.
你想要个简单的演示代码,好吧, 你可以看以下链接的演示代码.

ffmpeg av_parser_parse2函数分析各种码流测试程序_ffmepg 解析码流信息-CSDN博客

那片博客与本篇侧重点不同,本篇直接针对内存分配而来.
函数介绍: 
参数1: Parser Context 对象,分析结果存放到这里.
参数2: Codec Context 对象, 数据有可能需要部分解码,所以用到该对象.
参数3, 4: 输出的数据
参数5,6: 输入的数据.
参数6,7,8: 输入的一些属性,时间戳,位置,非本次分析的重点.

观察到从文件中读取了n次后,每次读4096个字节, 认为组成了一个包.
问题来了:
------------------------------------------------------------
1. 前边n次读取的数据都存放到哪了, 它们什么时候释放内存呢?
------------------------------------------------------------

或者问:
为什么只有4096个数据,第一次却分配了4452个字节的内存,而后续都分配了4352字节内存
大概是读了5,6次吧,组成了一个包.

看调用栈:
0 in av_fast_realloc of libavutil/mem.c:490
1 in ff_combine_frame of libavcodec/parser.c:263
2 in h264_parse of libavcodec/h264_parser.c:592
3 in av_parser_parse2 of libavcodec/parser.c:166
4 in main of decode_video.c:163

1.1: 在ff_combine_frame 时, 有代码:
    void *new_buffer = av_fast_realloc(pc->buffer, &pc->buffer_size,
                                           *buf_size + pc->index +
                                           AV_INPUT_BUFFER_PADDING_SIZE);
第1个参数: pc->buffer, 上一次的内存指针, 第一次是空
第2个参数: pc->buf_size, 上一次的内存大小, 第一次是0
第3个参数,申请分配的大小:
*buf_size + pc->index + AV_INPUT_BUFFER_PADDING_SIZE 大小内存.
其中*buf_size 是新添加的数据 4096
pc->index 是保存的旧数据,第一次为0.
AV_INPUT_BUFFER_PADDING_SIZE 是填充大小,为64bytes, 实际是安全边界.
所以ff_combine_frame时将数据大小扩充了64bytes, 成4096+64=4160 bytes
当申请的内存大小小于旧内存大小,直接返回旧内存指针,不用再分配.
当申请内存大于旧内存大小,重新申请内存,并把旧数据copy到新内存中.

1.2: 在av_fast_realloc() 中 , 内存空间被再一次扩充到4452
    min_size = FFMAX(min_size + min_size / 16 + 32, min_size);
至于这次为什么要扩充, 是因为它考虑了一些复杂情况. 反正我没遇到.咱简单的理解就可以了.
再后续数据处理中,由于pc->index 是旧数据大小,加上padding 及扩充,计算后每次增加为4352大小.
但实际存储pc->index 只增加4096. 

再补充一点, av_fast_realloc() 调用 av_realloc();
    ptr = av_realloc(ptr, min_size);
而av_realloc() 调用 c库的 realloc(), 到这里就追查到底了. 记住,它用的是realloc 这条线
    return realloc(ptr, size);

1.3: 分配的内容保存到了哪里? 啥时候释放?

        pc->buffer = new_buffer;    // 被realloc 的内存保存到pc->buffer
        memcpy(&pc->buffer[pc->index], *buf, *buf_size); // 新数据被接到后面
        pc->index += *buf_size;  // 更新数据的长度
    其中pc 就是 ParseContext 对象
    AVCodecParserContext *s; // 是我们函数传递下去的AVCodecParseContext 对象.
    H264ParseContext *p = s->priv_data;
    ParseContext *pc = &p->pc;

    可见分配的内存由对象保存. 啥时候释放请看最后.

------------------------------------------------------------
2. 当发现可以组装成一个包时,发生了什么?
------------------------------------------------------------
        next = h264_find_frame_end(p, buf, buf_size, avctx); //其中p 是H264ParseContext
        next 等于-100 表示没有找到尾巴. 此时是上面的操作,当next 是真正的尾巴时,例如:1133

        pc->buffer = new_buffer;
        memcpy(&pc->buffer[pc->index], *buf, next + AV_INPUT_BUFFER_PADDING_SIZE);
        pc->index = 0;

    我们看到,它还是多copy了padding_size个数据,并且pc->incex 恢复成了0.  
    但记住,此时的pc->buffer指向一个大的缓冲空间了.
    pc->buffer_size 等于26212 字节
    以后当申请内存小于上面哪个值时,直接返回旧指针,不用重新分配.
    只有当申请的内存比它大时,才会重新分配内存.

------------------------------------------------------------
3. parser 申请的内存啥时候释放.
------------------------------------------------------------
  在整个数据处理期间,它是不释放的. 只会往上涨,如果后面需要保留更大数据的话.
  只有分析结束, AVCodecParseContext 对象被释放,parse对象才会释放. parse 申请的内存才会释放.

  所以不用纠结为什么有的内存看起来不释放?是不是内存泄漏了? 
  没有泄漏, 它是可以控制的. (still reach able), realloc 就是这样工作的.

小结: 本博从内存分配的角度,分析了av_parse_parse2() 的工作过程,
   并说明了为什么它申请的内存看起来没有及时释放.
   这个内存是由对象pc->buffer 来保管.
   整个对象存在期间内存只会增大, 由realloc 执行增大操作.
   但也只是保留一个最大的包使用的内存.

   这是我查找内存泄漏的一个收获.
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值