本文为做者原创,转载请注明出处:http://www.javashuo.com/article/p-fxcsqrre-en.htmlhtml
本文基于FFmpeg 4.1版本。数据结构
struct AVPacket定义于<libavcodec/avcodec.h>app
struct AVPacket packet;
AVPacket中存储的是通过编码的压缩数据。在解码中,AVPacket由解复用器输出到解码器;在编码中,AVPacket由编码器输出到复用器。下图中,解复用器(demuxer)的输出和复用器(muxer)的输入“encoded data packets”的数据类型就是AVPacket:less
_______ ______________ | | | | | input | demuxer | encoded data | decoder | file | ---------> | packets | -----+ |_______| |______________| | v _________ | | | decoded | | frames | |_________| ________ ______________ | | | | | | | output | <-------- | encoded data | <----+ | file | muxer | packets | encoder |________| |______________|
对于视频而言,一个AVPacket一般只包含一个压缩视频帧。而对于音频而言,一个AVPacket可能包含多个完整的音频压缩帧。AVPacket也能够不包含压缩编码数据,而只包含side data,这种包能够称为空packet。例如,编码结束后只须要更新一些参数时就能够发空packet。ide
AVPacket对象能够在栈上分配,注意此处指的是AVPacket对象自己。而AVPacket中包含的数据缓冲区是经过av_malloc()在堆上分配的。
TODO: AVPacket对象在栈上分配,原理不清楚,待研究函数
/** * This structure stores compressed data. It is typically exported by demuxers * and then passed as input to decoders, or received as output from encoders and * then passed to muxers. * * For video, it should typically contain one compressed frame. For audio it may * contain several compressed frames. Encoders are allowed to output empty * packets, with no compressed data, containing only side data * (e.g. to update some stream parameters at the end of encoding). * * AVPacket is one of the few structs in FFmpeg, whose size is a part of public * ABI. Thus it may be allocated on stack and no new fields can be added to it * without libavcodec and libavformat major bump. * * The semantics of data ownership depends on the buf field. * If it is set, the packet data is dynamically allocated and is * valid indefinitely until a call to av_packet_unref() reduces the * reference count to 0. * * If the buf field is not set av_packet_ref() would make a copy instead * of increasing the reference count. * * The side data is always allocated with av_malloc(), copied by * av_packet_ref() and freed by av_packet_unref(). * * @see av_packet_ref * @see av_packet_unref */ typedef struct 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; /** * Presentation timestamp in AVStream->time_base units; the time at which * the decompressed packet will be presented to the user. * Can be AV_NOPTS_VALUE if it is not stored in the file. * pts MUST be larger or equal to dts as presentation cannot happen before * decompression, unless one wants to view hex dumps. Some formats misuse * the terms dts and pts/cts to mean something different. Such timestamps * must be converted to true pts/dts before they are stored in AVPacket. */ int64_t pts; /** * Decompression timestamp in AVStream->time_base units; the time at which * the packet is decompressed. * Can be AV_NOPTS_VALUE if it is not stored in the file. */ int64_t dts; uint8_t *data; int size; int stream_index; /** * A combination of AV_PKT_FLAG values */ int flags; /** * Additional packet data that can be provided by the container. * Packet can contain several types of side information. */ AVPacketSideData *side_data; int side_data_elems; /** * Duration of this packet in AVStream->time_base units, 0 if unknown. * Equals next_pts - this_pts in presentation order. */ int64_t duration; int64_t pos; ///< byte position in stream, -1 if unknown #if FF_API_CONVERGENCE_DURATION /** * @deprecated Same as the duration field, but as int64_t. This was required * for Matroska subtitles, whose duration values could overflow when the * duration field was still an int. */ attribute_deprecated int64_t convergence_duration; #endif } AVPacket;
音视频数据缓冲区ui
uint8_t *data
:int size
:AVBufferRef *buf
管理。AVBufferRef *buf
:uint8_t *data
指向的内存区域提供引用计数等管理机制。buf
值为NULL,则data
指向的数据缓冲区不使用引用计数机制。av_packet_ref(dst, src)
将执行数据缓冲区的拷贝,而非仅仅增长缓冲区引用计数。buf
值非NULL,则data
指向的数据缓冲区使用引用计数机制。av_packet_ref(dst, src)
将不拷贝缓冲区,而仅增长缓冲区引用计数。av_packet_unref()
将数据缓冲区引用计数减1,当缓冲区引用计数为0时,缓冲区内存被FFmpeg回收。struct AVPacket pkt
对象,若是pkt.buf
值非NULL,则有pkt.data == pkt.buf->data == pkt.buf->buffer.data额外类型数据this
AVPacketSideData *side_data
int side_data_elems
packet属性编码
int64_t pts
:int64_t dts
:int stream_index
:int flags
:int64_t duration
:int64_t pos
:这里列出的几个关键函数,主要是为了帮助理解struct AVPacket
数据结构code
int av_packet_ref(AVPacket *dst, const AVPacket *src) { int ret; ret = av_packet_copy_props(dst, src); if (ret < 0) return ret; if (!src->buf) { ret = packet_alloc(&dst->buf, src->size); if (ret < 0) goto fail; if (src->size) memcpy(dst->buf->data, src->data, src->size); dst->data = dst->buf->data; } else { dst->buf = av_buffer_ref(src->buf); if (!dst->buf) { ret = AVERROR(ENOMEM); goto fail; } dst->data = src->data; } dst->size = src->size; return 0; fail: av_packet_free_side_data(dst); return ret; }
av_packet_ref()做了处理以下:
a) 若是src->buf为NULL,则将src缓冲区拷贝到新建立的dst缓冲区,注意src缓冲区不支持引用计数,但新建的dst缓冲区是支持引用计数的,由于dst->buf不为NULL。
b) 若是src->buf不为空,则dst与src共用缓冲区,调用av_buffer_ref()
增长缓冲区引用计数便可。av_buffer_ref()
分析参考“FFmpeg数据结构AVBuffer”
void av_packet_unref(AVPacket *pkt) { av_packet_free_side_data(pkt); av_buffer_unref(&pkt->buf); av_init_packet(pkt); pkt->data = NULL; pkt->size = 0; }
av_packet_unref()
注销AVPacket *pkt
对象,并调用av_buffer_unref(&pkt->buf);
将缓冲区引用计数减1。
av_buffer_unref()
中将缓冲区引用计数减1后,若缓冲区引用计数变成0,则回收缓冲区内存。av_buffer_unref()
分析参考“FFmpeg数据结构AVBuffer”
[1] FFmpeg数据结构:AVPacket解析, http://www.javashuo.com/article/p-bdnshwjk-q.html
2018-12-14 V1.0 初稿