AVPacket中的字段可用分为两部分:实际的帧数据、帖的属性数据和相关管理信息
数据的属性有以下字段
- pts 显示时间戳 //取值为time_base的num和den之前的值
- dts 解码时间戳
- stream_index Packet所在stream的index
- flats 标志,其中最低为1表示该数据是一个关键帧
- duration 数据的时长,以所属媒体流的时间基准为单位
- pos 数据在媒体流中的位置,未知则值为-1
- convergence_duration 该字段已被deprecated,不再使用
AVPacket可以理解为一个容器,能过一个指针指向帧数据缓存区,然后通过缓存区引用计数来共享缓存区数据,提高数据空间使用效率。AVPacket中包含有两种数据:
- data 指向保存压缩数据的指针,这就是AVPacket实际的数据。
- side_data 容器提供的一些附加数据
- buf 是AVBufferRef类型的指针,用来管理data指针引用的数据缓存的
AVPacket中的内存管理
当从一个Packet去创建另一个Packet的时候,有两种情况:
- 两个Packet的data引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题
- 两个Packet的data引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy。
对于多个Packet共享同一个缓存空间,FFmpeg使用的引用计数的机制(reference-count)。当有新的Packet引用共享的缓存空间时,就将引用计数+1;当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放掉引用的缓存空间。
AVPacket中的AVBufferRef *buf;
就是用来管理这个引用计数的。
能过av_packet_ref
和av_packet_unref可以对引用计数进行操作,av_packet_ref增加引用,av_packet_unref减少引用:av_packet_ref当源buf为空时会分配一个缓存区,并把两个buf都指向过去。
av_read_frame(pFormatCtx, &packet) // 读取
Packet av_packet_ref(&dst,&packet) // dst packet共享同一个数据缓存空间
av_packet_unref(&dst); //减少引用计数,引用计数为0时会释放缓存
AVPacket 相关函数介绍
av_read_frame:从媒体流中读取帧填充到Packet的数据缓存空间。如果Packet->buf
为空,则Packet的数据缓存空间会在下次调用av_read_frame
的时候失效。这也就是为何在FFmpeg3:播放音频中,从流中读取到Packet的时,在将该Packet插入队列时,要调用av_dup_avpacket
重新复制一份缓存数据。
av_packet_alloc:
创建一个AVPacket,将其字段设为默认值(data为空,没有数据缓存空间)
av_packet_free:
释放使用av_packet_alloc
创建的AVPacket,如果该Packet有引用计数(packet->buf不为空),则先调用av_packet_unref(&packet)
。
av_packet_clone:
其功能是 av_packet_alloc
+ av_packet_ref
av_init_packet:
初始化packet的值为默认值,该函数不会影响data引用的数据缓存空间和size,需要单独处理。
av_new_packet:
av_init_packet
的增强版,不但会初始化字段,还为data分配了存储空间。
av_copy_packet:
复制一个新的packet,包括数据缓存。
av_packet_from_data:
初始化一个引用计数的packet,并指定了其数据缓存。
av_grow_packet:
增大Packet->data指向的数据缓存。
av_shrink_packet
:减小Packet->data指向的数据缓存。
av_dup_packet:
是复制src->data引用的数据缓存,赋值给dst,也就是创建两个独立packet。说是3的版本册除了该方法,但4.1的源码里面又出现了该方法。
已经废弃的两个函数:av_dup_packet
和av_free_packet
av_free_packet:
释放packet,包括其data引用的数据缓存,现在可以使用av_packet_free或av_packet_unref
代替。
读包示例代码:
AVPacket packet;
while (av_read_frame(pFormatCtx, &packet) >= 0)
{
// 这里只读视频包
if (packet.stream_index == videoStream) {
push_packet_queue(&packet) // 将包按插入到队列相应位置
} else {
av_packet_unref(&packet); // OR av_packet_free(&packet)
}
}