先抛一个球,从一段代码开始

AVPacket packet;
av_init_packet(&packet);
packet->data = NULL;
packet->size = 0;
while (av_read_frame(ifmt_ctx, &packet) >= 0) {av_free_packet(&packet);
}

这段代码一般也是经常用到了。那么问题来了,为什么packet已经释放了,av_read_frame里面还能继续使用这个packet。不知道大家有没有想过这个问题。

先讲个故事,假设有一个avpacket,另一个人想拷贝,它怎么办呢,再开辟一个空间,然后把avpacket的data copy给他?这太浪费内存了,因为data里面存的是视频解压缩数据,数据量通常都很大的,几千字节到上万字节不等,我们完全没必要做这么愚蠢的copy,因为data是一个指针,我们完全可以让新的avpacket的data也指向src avpacket data指针指向的数据区就可以了。但是这又引入了新的问题,就是这么多人都指向了数据区的内存,万一有人想释放data空间了,他可不知道谁还要继续使用这块内存。所以data数据空间变成了烫手山芋,没人敢释放了。所以为了解决这个问题,ffmpeg引入了引用计数。专门找个人来计数,每个人想释放的时候,这个计数值refcount就-1,如果增加了新的人引用它,refcount就+1,如果某个时刻refcount等于0 了,就表示现在可以安全释放了。

下面具体看ffmpeg怎么实现的,先看AVPacket相关的结构体,我只展示和我们主题有关的东西

AVPacket{/*** A reference to the reference-counted buffer where the packet data is* stored.* May be NULL, then the packet data is not reference-counted.*/AVBufferRef *buf;uint8_t *data;int   size;
}AVBufferRef {AVBuffer *buffer;uint8_t *data;int      size;
} AVBuffer {uint8_t *data; int      size;volatile int refcount;
}

buf这段注释是什么意思呢。意思是说buf指向引用计数缓冲区,如果buf为NULL,就说明这个avpacket是无引用计数的,说白了,就是现在只要他一个人在使用data指向的数据空间。换个角度,buf不为NULL,就说明这个avpacket是ref的,有引用计数的

我们再看av_init_packet()干了什么

pts,dts等一系列字段全部给初始化,buf=NULL,data无分配空间。说明刚开始出来,还是unref的 avpacket

av_read_frame先不看,看av_free_packet()。

这里我们关心的主要做了两件事,第一件事是如果pkt->buf不为NULL,会调用av_buffer_unref, 第二件事是就是把data置为NULL, 这里注意,packet本身没释放。所以这个函数是内存泄漏隐患的。肯定内存泄漏的是avpacket结构体本身空间未释放,其次因为这里只是把data置空,所以data可能内存泄漏,因为是存在一种情况buf为NULL,但是data分配了空间。比如看下面的代码

AVPacket *packet=(AVPacket*)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
packet->data=(uint8_t*)malloc(128);
packet->size=128;
av_free_packet(packet);
av_freep(&packet);//释放packet并将其置空

这段代码的packet的data 调用av_free_packet()就不会被释放。需要我们手动释放。那么什么时候data释放,什么时候data不释放呢.这个问题下面慢慢讲。

继续分析av_free_packet,如果pkt->buf不为空,那么执行av_buffer_unref

分析这个代码前,先看下我上面列的AVPacket相关的结构体。文字描述就是,

avpacket里面有一个buf,有一个data.

这个buf又有一个buffer,有一个data.

这个buffer也有一个data.

我们注意到avpacket里面有data,buf里面有data,buffer里面也有data,其实这三个data都是同一个data,

即avpacket->data=buf->data=buffer->data。每个结构体都存了一份data的地址,实际是方便释放的时候,知道数据空间的首地址.

回过头看av_buffer_unref();

首先要满足buf不为空,如果buf为空,直接滚出,所以不存在还有释放data这回事。所以上面那个内存泄漏就是这么来的。

假设我们buf不为空,继续分析。

b=(*dst)->buffer.  这里dst就是packet的buf,这句话就是先把buffer的地址暂存起来,

src为NULL,所以走av_freep(dst);这句话就是释放了packet的buf,注意啊,av_freep实际就是free+置空。所以这里只是释放结构体本身的内存空间,里面的data堆空间是不会被处理的。

所以我们在释放这个结构体之前,刚刚就是提前把他里面的指针地址给取出来。

继续分析,执行avpriv_ato...()函数,这里就是将buffer的refcount减1,然后值返回,如果refcount变成了0,

通过b->free函数释放data数据空间,然后释放buffer

所以我们看到真正释放data,不是直接free的avpacket里面的那个data,而是曲曲折折free的是avpacket->buf->bufer->data

这个层级关系中,只有有谁是空的,data都不会被释放。

总结:av_free_packet(packet) 不能释放packet本身的内存空间,所以需要手动调用av_freep(&packet);

注意,av_freep需要传指针的地址

其次,av_free_packet能够自动释放data,取决于需要同时满足packet的ref不为NULL且引用计数变为0

再说个有些人会搞混的,就是packet->data是否为空,和data指向的堆空间是否被释放,是完全两码事,不要搞错了啊。

data本身是一个栈内存上的指针变量,data你不手动把它置为NULL, 就算你free了data, data也不会变成NULL的。free的是它指向的那块内存,对它自己是不会有影响的。

还有一个问题,就是你把data置为NULL,不是说free它了。这个c和java不同,java里面的虚拟机是根据内存引用计数来的,没有引用了,垃圾回收器就回收了。但是c里面,没有垃圾回收器自动回收这么一说。每块堆内存都需要我们自己去管理释放。

data没有调用free(),就直接置为NULL,那是妥妥的内存泄漏。free()的本质就是告诉系统,这块内存我现在不用了,请标记为未分配状态。相当于free()是一个通知系统的操作。你不free(),直接置NULL, 在系统那,那块堆内存就一直是分配状态,无法得到回收。

av_read_frame代码很复杂,我们采用debug方式观察执行完av_read_frame以后,buf是不是为NULL,

结论是不为空,说明av_read_frame会生产一个ref的avpacket, (且内部调用了av_init_packet)。

此时refcount=1,所以free的时候,满足refcount-1=0,能够释放掉data空间。

下面继续分析增加一个data引用,内存中会发生什么事。

涉及到的函数是av_packet_ref()

...
while (av_read_frame(format_context, &packet) >= 0) {AVPacket dst;av_packet_ref(&dst, &packet);av_free_packet(&packet);
}

av_packet_copy_props,把一些pts,dts等字段进行copy

判断src是不是ref的,如果src不是ref的,先给dst的buf分配内存空间,然后把src的data数据拷贝给它。

注意,此时dst的data和src的data是两块内存。

如果src是ref的,执行av_buffer_ref

给dst的buf分配内存空间(即这里的ret),并且把src的buf的值赋给它。

这里*ret=*buf,是关键。

一个指针p1把值赋给另一个指针p2,如果赋值内容是指针,就copy指针地址,如果是基础数据类型,比如int,字符串类型,就copy值。

此时buf中的内容是AVBuffer,data,size. 前两个都是指针。所以是copy指针地址,即av_buffer_ref实际上两者共享同一个data堆空间。且只要buf释放自己的data,则ret的data也会被释放。但是buf改变自己的size的值,不会影响ret的size的值。(string类型或者int类型的数据不会受影响)

继续分析代码,执行到avpriv_atomic_int_add_and_fetch ,这里是把buf->buffer->refcount++了, 因为refcount属于buffer指针的,和dst是共享的一份,所以等同于dst的refcount++了。

这样就能保证,每个人在操作引用计数的时候,都操作的是同一个。

和av_packet_ref对应的函数,是av_packet_unref

这里av_buffer_unref 也都已经分析过。

内存分析(一)AVPacket相关推荐

  1. CUDA统一内存分析

    CUDA统一内存分析 PascalMIG 如 NVIDIA Titan X 和 NVIDIA Tesla P100 是第一个包含页 GPUs 定额引擎的 GPUs ,它是统一内存页错误处理和 MIG ...

  2. Windows系统内存分析工具的介绍

    Windows系统内存分析工具的介绍(进程管理器,资源管理器,性能监视器, VMMap, RamMap,PoolMon) 微软官方提供多种工具来分析Windows 的内存使用情况,除了系统自带的任务管 ...

  3. C++对象的内存分析(2)

    C++对象的内存分析(2) Binhua Liu 前言 本章节讨论单继承情况下类对象的内存特性.阅读时请思考这几个问题:从子类到基类的类型转换,编译器做了什么?多态是怎么实现的?类的成员函数(包括虚函 ...

  4. [转] python运行时内存分析工具meliae

    转自:https://my.oschina.net/markco/blog/601773 利用meliae来监控python进程的内存占用情况 meliae是一个python进程内存占用监控.分析工具 ...

  5. Java程序内存分析

    2019独角兽企业重金招聘Python工程师标准>>> Java程序内存分析:使用mat工具分析内存占用 http://my.oschina.net/biezhi/blog/2862 ...

  6. linux下基于内存分析的rootkit检测方法

    0x00 引言 某Linux服务器发现异常现象如下图,确定被植入Rootkit,但运维人员使用常规Rootkit检测方法无效,对此情况我们还可以做什么? 图1 被植入Rootkit的Linux服务器 ...

  7. android应用内存分析,Android应用程序内存分析-Memory Analysis for Android Applications

    Android应用程序内存分析 原文链接:http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html ...

  8. python 画图 内存-python的内存分析和处理

    2.内存分析和处理 程序的运行离不开对内存的操作,一个软件要运行,须将数据加载到内存中,通过CPU进行内存数据的读写,完 成数据的运算. 2.1 程序内存浅析 软件的程序在计算机中的执行,主要是通过数 ...

  9. Java内存分析1 - 从两个程序说起

    这次看一些关于JVM内存分析的内容. 两个程序 程序一 首先来看两个程序,这里是程序一:JVMStackTest,看下代码: package com.zhyea.robin.jvm;public cl ...

  10. Android内存分析和调优

    最近我们的android app占用了大量内存,于是领导安排做减少内存占用的工作. 要优化内存,首先要做的就是分析内存占用情况.android提供了多个工具和命令进行内存分析. 第一层 Procran ...

最新文章

  1. 【C++】浅析析构函数(基类中)为什么要写成虚基类?
  2. 0/0型极限等于多少_求极限时是否可以进行代入?
  3. 25个Java机器学习工具库
  4. mq集群要建传输队列吗_面试官:消息队列这些我必问!
  5. oracle字段去重查询,oracle怎么去重查询
  6. 图鸭科技获数千万元A轮融资,金沙江创投领投
  7. vc2010c语言,VC2010下载_VisualC++2010官方下载「vc2010」-太平洋下载中心
  8. 子网掩码最简单通俗的解释
  9. linux驱动下载中心,Kvaser Linux驱动程序| Linux驱动程序和SDK开发包
  10. 词干提取(stemming)与词形还原(lemmatization)
  11. 用python计算狗的年龄_狗狗年龄与人的年龄的对比:狗的年龄:一年相当于人类的几岁...
  12. 使用Guava-retrying优雅地解决异常重试场景
  13. SEO是什么?前端如何进行SEO优化
  14. 从学校到工作的一些收获
  15. 计算机网络原理第七版word,计算机网络原理简答题
  16. 【答粉丝问】面试时,面试官说“谈谈你的缺点”时,该怎么回答?
  17. 孤立森林(Isolation Forest)
  18. 各种进制转换通用代码
  19. 论文中常用的英语短语
  20. 线性代数学习笔记6-1:行列式与线性变换

热门文章

  1. 空当接龙的BUG?Win7下的哦。
  2. 共享windows无线网络给ubuntu有线网卡
  3. I2C总线内容总结分享
  4. 数据库管理员是开发数据库的专门人员吗?
  5. 安卓AKP升级出现对话框不动的情况
  6. 本周行情回顾和下周预期2022.6.26(连续大跌后,企稳反弹?)
  7. 修改Vivado的文本编辑器为Sublime Text 3
  8. caffe源码深入学习6:超级详细的im2col绘图解析,分析caffe卷积操作的底层实现
  9. 互联网35岁是个坎,博士可不可以干到40岁?
  10. 私域流量头部公司积木新消费荣获HICOOL 2022全球创业大赛优胜奖