前言
AVPacket是ffmpeg常用的结构体,位于libavcodec/avcodec.h中,它用于表示一帧压缩的视频或者多帧压缩的音频,使用了引用计数机制来管理内存
- AVPacket结构体分析
- 常用函数
- AVPacket使用方式
AVPacket结构体分析
如下为源码中关于AVPacket的描述(基于ffmpeg版本为4.2),已去掉注释部分
typedef struct AVPacket {
AVBufferRef *buf;
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
AVPacketSideData *side_data;
int side_data_elems;
int64_t duration;
int64_t pos; ///< byte position in stream, -1 if unknown
#if FF_API_CONVERGENCE_DURATION
attribute_deprecated
int64_t convergence_duration;
#endif
} AVPacket;
- uint8_t *data;
用于存放压缩的音视频数据 - int size;
压缩的音视频数据的大小 - AVBufferRef *buf;
用于实现引用计数机制管理data内存的关键,它的data与AVPacket的data指向同一内存地址,size一般都是大于等于AVPacket的size(要字节对齐);
如果用引用计数机制,那么AVPacket分配内存时是为buf分配的内存,然后将data指向这块内存的地址
- AVPacketSideData *side_data;
用于存放非音视频的附属数据,数据量不大。 - int side_data_elems;
附属数据的个数
AVPacket内存管理方式
这里主要是data的内存管理,手动管理和引用计数方式管理:
- 手动管理
AVBufferRef *buf;为NULL时,由用户自己释放AVPacket的data内存 - 引用计数方式管理
AVBufferRef *buf;不为NULL时,如果AVBufferRef中的引用计数为0,则释放data中的内存
AVPacket常用相关函数
- AVPacket *av_packet_alloc(void);
只是为AVPacket结构体赋予初值,里面的data,buf,side_data等都为NULL其它成员的值也都是默认的值 - void av_packet_free(AVPacket **pkt);
由av_packet_alloc()创建的AVPacket最后还必须要用av_packet_free()来释放,调用此函数后pkt将置为NULL;内部会调用一次av_packet_unref() - int av_new_packet(AVPacket *pkt, int size);
为data分配内存,并且将buf的引用计数设置为1 - int av_packet_ref(AVPacket *dst, const AVPacket *src);
如果src的buf不为NULL,则将src的成员值全部赋值给dst,期间没有数据拷贝,并且src的buf引用计数+1;如果src的buf为NULL,则dst会创建一个新buf,然后将src的data指向内存拷贝到buf - void av_packet_unref(AVPacket *pkt);
让AVPacket的buf引用计数-1,如果最后引用计数为0,则释放AVPacket的data对应内存 - int av_packet_make_writable(AVPacket *pkt);
AVPacket是否可写:buf不为空,并且对应的flags非AV_BUFFER_FLAG_READONLY
首先判断是否可写,不可写则重新创建buf,并将data指向内存拷贝到buf中,将引用计数设置为1
- int av_packet_make_refcounted(AVPacket *pkt);
如果没有buf字段或者buf的大小与AVPacket的size不相同,则会重新创建buf字段,则重新创建buf并将data指向内存拷贝到buf中,将引用计数设置为1
tips:AVPacket对象如果由av_packet_free()等函数释放了,则不能调用此函数了,会奔溃
AVPacket基本使用示例
包括采用引用计数管理内存的使用方式和非引用计数管理内存的使用方式
1、AVPacket的使用方式一
// ====== 正确的使用方式 一 ====== //
// 在堆内存创建AVPacket对象,初始化为默认值,data,buf,side_data等都为NULL,它内部会调用av_init_packet()方法
AVPacket *allocpkt = av_packet_alloc();
// 为buf分配内存,并且将data指向该内存,并且将引用计数设置为1
av_new_packet(allocpkt, 200);
// 引用计数减少1;如果引用计数为0,data内存将在此函数中释放,并且data和buf字段都置为NULL
..........
这里是伪代码。。。。举编码时的例子
内部会自动为allocpkt的buf分配内存,然后将编码后的数据存储到buf中并设置引用计数为1
avcodec_receive_packet(codecctx,allocpkt)
// 应用接受到编码后的allocpkt,处理完allocpkt的buf中数据
process(allocpkt->data)
// 调用一次,将引用计数-1;释放buf指向内存
av_packet_unref(allocpkt);
这里是伪代码。。。。。。
..........
// 由av_packet_alloc()创建的AVPacket最后还必须要用av_packet_free()来释放,调用此函数后allocpkt将置为NULL;
// 内部会调用一次av_packet_unref()
av_packet_free(&allocpkt);
// ====== 正确的使用方式 一 ====== //
2、AVPacket的使用方式二
// ====== 正确的使用方式 二 ====== //
// 栈内存创建AVPacket对象,按栈内存方式初始化AVPacket的值(所有的值)
AVPacket initpkt;
// 初始化AVPacket的值为默认值,data,buf,side_data等都为NULL
av_init_packet(&initpkt);
// 分配data内存,并将引用计数设置为1
av_new_packet(&initpkt, 100);
// 引用计数减少1;如果引用计数为0,data内存将在此函数中释放,并且data和buf字段都置为NULL
av_packet_unref(&initpkt);
// 由于AVPacket是创建在栈内,所以函数调用结束后,该对象占用内存会自动释放
// ====== 正确的使用方式 二 ====== //
3、AVPacket的使用方式三
// ====== 正确的使用方式 三 ====== //
AVPacket pkt;
av_init_packet(&initpkt);
pkt.data = (uint8_t*)av_malloc(300);
pkt.size = 200;
..........
这里是伪代码。。。。举解码时的例子
avcodec_send_packet(codecctx,&initpkt)
解码器不会对initpkt做任何处理
..........
// 解码器解码完成后,必须的手动释放内存
av_freep(pkt.data);
// ====== 正确的使用方式 三 ====== //
from:https://www.jianshu.com/p/b0627f8ac018