- 【说明】课程学习地址:https://ke.qq.com/course/468797
FFmpeg内存模型
FFmpeg内存模型
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
问题(数据的申请和释放):
- 从
av_read_frame
读取到一个AVPacket后怎么放入视频包队列? - 从
avcodec_recieve_frame
读取到一个AVPacket后怎么放入视频帧队列?
从现有Packet拷贝到一个新的Packet的时候(比如从解码器如何处理Packet),有两种情况:
- ①两个Packet的buf引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题。
- ②两个Packet的buf引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy。
对于第一种情况,可以类比智能指针shared_ptr的实现,即采用引用计数
由此可以理解如下的内存模型
AVPacket和AVFrame内部都有AVBufferRef,AVBufferRef内部使用AVBuffer存储数据,AVBuffer内部有一个计数器
或者更为精确的描述是:
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
} AVPacket;
typedef struct AVBufferRef {
AVBuffer *buffer;
uint8_t *data;
int size;
} AVBufferRef;
struct AVBuffer {
uint8_t *data; /**< data described by this buffer */
int size; /**< size of data in bytes */
// 引用计数器
atomic_uint refcount;
// 析构函数
void (*free)(void *opaque, uint8_t *data);
void *opaque;
int flags;
};
从源码中也可以看出确实是类似shared_ptr的引用计数,我们在分配内存时也需要注意指向的是数据还是引用计数;AVFrame也是这个原理
FFmpeg AVPacket API
// 分配AVPacket,这时候还没buffer
AVPacket *av_packet_alloc(void);
// 释放AVPacket和alloc成对使用
void av_packet_free(AVPacket **pkt);
// 初始化AVPacket,只是单纯初始化pkt字段为默认值,不涉及数据和大小
void av_init_packet(AVPacket *pkt);
// 给AVPacket的buf分配内存,引用计数初始化为1
int av_new_packet(AVPacket *pkt, int size);
// 增加引用计数
int av_packet_ref(AVPacket *dst, const AVPacket *src);
// 减少引用计数
int av_packet_ref(AVPacket *pkt);
// 转移引用计数
void av_packet_move_ref(AVPacket *dst, AVPacket *src);
// 等于alloc+ref
AVPacket *av_packet_clone(const AVPacket *src);
FFmpeg AVFrame API
AVFrame *av_frame_alloc(void); // 分配AVFrame
void av_frame_free(AVFrame **frame); //释放AVFrame
int av_frame_ref(AVFrame *dst, const AVFrame *src);
void av_frame_unref(AVFrame *frame);
void av_frame_move_ref(AVFrame *dst, AVFrame *src);
int av_frame_get_buffer(AVFrame *frame, int align); //分配内存
AVFrame *av_frame_clone(const AVFrame *src); //等于alloc+ref