FFmpeg结构体: AVPacket

AVPacket 是什么?

AVPacket是存储压缩编码数据相关信息的结构体

/**
 * 该结构用来存储压缩数据. 这是典型的解复合器的输出然后塞入解码器,或者是接收编码器的输出然后塞入复合器
 *
 * 解复合器 --> AVPacket --> 解码器 --> YUV/RGB --> 编码器 --> AVPacket -->复合器
 *
 * 对视频而言, 它通常包含一个压缩帧. 对音频而言,则它可能包含多个压缩帧. 
 * 编码器允许输出一个空包, 没有压缩数据, 仅包含侧数据。
 * (例如在编码结束时更新一些流参数)
 *
 * AVPacket是FFmpeg少数几个结构之一, 其大小是公共ABI的一部分.
 *
 * 数据所有权的语义取决于buf域.
 * 如果被设置, 分组数据动态分配,且永远有效,直到一个叫av_packet_unref()减少引用计数为0时才被释放
 *
 * 如果buf域没有被设置,那么av_packet_ref()将做一个复制而不会增加引用计数
 *
 * 附加数据始终由av_malloc()分配内存, 由av_packet_ref()进行拷贝,由av_packet_unref()执行释放.
 *
 * @浏览 av_packet_ref()
 * @浏览 av_packet_unref()
 */
typedef struct AVPacket 
{
    // 用来管理data指针引用的数据缓存的
    // 为NULL时,那么数据包是不计数的
    AVBufferRef *buf;
 
    // 显示时间戳, 对应时间戳AVStream->time_base单元; 这个时间点, 解压缩的数据包将被提交给用户
    // 如果时间不被存储在文件里, 则可以写成AV_NOPTS_VALUE
    // pts必须大于或等于dts, 因为显示不能在解压缩之前被发生, 除非有人想查看十六进制存储。
    // 某些格式误用了这个名词dts或者是pts/cts那是意味着别的意思, 所以时间戳必须在被存储到AVPacket之前转换成真正的PTS/DTS。
    int64_t pts;
 
    // 解码时间戳, 对应时间戳AVStream->time_base单元; 这个时间点, 数据包被解码
    // 如果时间不被存储在文件里, 则可以写成AV_NOPTS_VALUE
    int64_t dts;
 
    // 存储的数据,指向一个缓存,这是AVPacket实际的数据
    uint8_t *data;
 
    // 数据的大小
    int size;
    
    // 标识该AVPacket所属的音频/视频流的索引
    int stream_index;
    
    // 一个AV_PKT_FLAG标识值, 最低为置1表示关键帧
    int flags;
 
    // 容器可以提供的附加数据
    // 包可以包含几种AVPacketSideDataType类型的侧信息
    AVPacketSideData *side_data;
 
    // 附加信息元素
    int side_data_elems;
 
    // 数据的时长,以所属媒体流的时间基准为单位
    int64_t duration;
 
    // 该数据在媒体流中的字节偏移量
    int64_t pos;
 
    // 该字段不再使用
#if FF_API_CONVERGENCE_DURATION
    attribute_deprecated
    int64_t convergence_duration;
#endif
 
} AVPacket;

重要的变量
在AVPacket结构体中,重要的变量有以下几个:

  1. uint8_t *data:压缩编码的数据。
    例如对于H.264来说。1个AVPacket的data通常对应一个NAL。
    注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流
    因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到视音频的码流文件。
  2. int size:data的大小
  3. int64_t pts:显示时间戳
  4. int64_t dts:解码时间戳
  5. int stream_index:标识该AVPacket所属的视频/音频流。

容器
AVPacket实际上可用看作一个容器,它本身并不包含压缩的媒体数据,而是通过data指针引用数据的缓存空间。
所以将一个Packet作为参数传递的时候,就要根据具体的需要,对data引用的这部分数据缓存空间进行特殊的处理。

当从一个Packet去创建另一个Packet的时候,有两种情况:

  1. 两个Packet的data引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题;
  2. 两个Packet的data引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy;

第二种情况,数据空间的管理比较简单,但是数据实际上有多个copy造成内存空间的浪费。
所以要根据具体的需要,来选择到底是两个Packet共享一个数据缓存空间,还是每个Packet拥有自己独自的缓存空间。

引用计数
对于多个Packet共享同一个缓存空间,FFmpeg使用的引用计数的机制(reference-count):
当有新的Packet引用共享的缓存空间时,就将引用计数+1;
当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放掉引用的缓存空间。

而类AVBufferRef就是用来管理引用机制的:

typedef struct AVBufferRef {
    AVBuffer *buffer;
 
    /**
     * The data buffer. It is considered writable if and only if
     * this is the only reference to the buffer, in which case
     * av_buffer_is_writable() returns 1.
     */
    uint8_t *data;
    /**
     * Size of data in bytes.
     */
    int      size;
} AVBufferRef;

其中,AVPacket会使用两个函数:

  1. int av_packet_ref(AVPacket *dst, const AVPacket *src):
    可以理解为使用引用计数的浅拷贝。
    该函数会先拷贝所有非缓存类数据,
    然后创建一个src->data的新的引用计数。如果src已经设置了引用计数发(src->buffer不为空),则直接将其引用计数+1;
    如果src没有设置引用计数(src->buffer为空),则为dst创建一个新的引用计数buf,并复制src->data到buf->buffer中。最后,复制src的其他字段到dst中。所以av_packet_ref()是将2个AVPacket共用一个缓存的。

  2. void av_packet_unref(AVPacket *pkt):
    可以理解为使用引用计数的数据清理。
    将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。如果引用计数为0,自动的释放缓存空间。所以,有两个Packet共享同一个数据缓存空间的时候可用这么做。

参考

https://blog.csdn.net/leixiaohua1020/article/details/14215755
https://blog.csdn.net/davidsguo008/article/details/72628675

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容