FFmpeg
- FFmpeg
- FFmpeg struct
- FFmpeg function
- av_register_all
- avformat_network_init
- avcodec_register_all
- av_find_best_stream
- avio_open
- avio_open2
- av_packet_rescale_ts
- avformat_write_header
- av_write_frame
- av_interleaved_write_frame
- av_write_trailer
- av_read_frame
- av_guess_format
- av_oformat_netx
- av_match_name
- av_match_ext
- av_dump_format
- av_image_fill_arrays
- av_image_get_buffer_size
FFmpeg
FFmpeg struct
AVFrameContext
保存需要读入的文件的格式信息,比如流的个数以及流数据等,AVFormat中实现了目前多媒体领域中的绝大多数媒体封装格式,包括封装和解封装,如MP4,FLV,KV,TS等文件封装格式,RTMP,RTSP,MMS,HLS等网络协议封装格式。FFmpeg是否支持某种媒体封装格式,取决于编译时是否包含了该格式的封装库。根据实际需求,可进行媒体封装格式的扩展,增加自己定制的封装格式,即在AVFormat中增加自己的封装处理模块。
- duration:
avformat_alloc_context
初始化上下文
avformat_alloc_output_context2
可以初始化一个用于输出的AVFormatContext结构体。它的声明位于libavformat\avformat.h
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,
const char *format_name, const char *filename);
- ctx:函数调用成功之后创建的AVFormatContext结构体。
- oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由FFmpeg猜测输出格式。
PS:使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。 - format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。
- filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。
函数执行成功的话,其返回值大于等于0。
avformat_open_input
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options)
打开一个文件,第一个参数是一个AVFormatContext指针变量的地址,它会根据打开的文件信息填充AVFormatContext,需要注意的是,此处的pFormatContext必须为NULL或由avformat_alloc_context分配得到,这也是上一节中将其初始化为NULL的原因,否则此函数调用会出问题。第二个参数是打开的文件名,通过argv[1]指定,也就是命令行的第一个参数。后两个参数分别用于指定特定的输入格式(AVInputFormat)以及指定文件打开额外参数的AVDictionary结构,这里均留作NULL。
avformat_close_input
用于关闭一个AVFormatContext,一般情况下是和avformat_open_input()成对使用的。
avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
avformat_open_input函数只是读文件头,并不会填充流信息,因此我们需要接下来调用avformat_find_stream_info获取文件中的流信息,此函数会读取packet,并确定文件中所有的流信息,设置pFormatCtx->streams指向文件中的流,但此函数并不会改变文件指针,读取的packet会给后面的解码进行处理。
avformat_free_context
销毁释放AVFormatContext结构。
AVCodecContext
保存了相应流的详细编码信息,比如视频的宽、高,编码类型等。
avcodec_free_context
释放AVCodecContext
avcode_alloc_context3
创建AVCodecContext 上下文
avcodec_copy_context
int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);
旧的API函数:avcodec_copy_context,主要的功能就是编码参数上下文的拷贝。
在最后释放的时候,居然出错了:
avcodec_close(ctx);
avcodec_close(ctx_video);//释放它的时候出错,出现部分double free
跟踪avcodec_close发现,是在释放AVCodecContext结构体中的nb_coded_side_data变量引起的错误
所以,在copy上下文之后,加了下面两行代码:
ctx_video->coded_side_data = NULL;
ctx_video->nb_coded_side_data = 0;
不太清楚这个变量的功能。这样操作后,编码正常,且在调用avcodec_close的时候,也没出现double free的错误,暂且这样处理吧。
AVInputFormat
存储输入音视频使用的封装格式。
AVOutputFormat
存储输出音视频使用的封装格式。
AVIOContext
AVIOContext是FFMPEG管理输入输出数据的结构体。本文将会详细分析一下该结构体里每个变量的含义和作用。
-
unsigned char *buffer:缓存开始位置
-
int buffer_size:缓存大小(默认32768)
-
unsigned char *buf_ptr:当前指针读取到的位置
-
unsigned char *buf_end:缓存结束的位置
-
void *opaque:URLContext结构体
在解码的情况下,buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候,先把数据从硬盘读入buffer,然后在送给解码器用于解码。
其中opaque指向了URLContext。注意,这个结构体并不在FFMPEG提供的头文件中,而是在FFMPEG的源代码中。从FFMPEG源代码中翻出的定义如下所示:
URLContext
存储视音频使用的协议的类型以及状态
URLProtocol
存储输入视音频使用的封装格式
AVStream
/**
* Stream structure.
* New fields can be added to the end with minor version bumps.
* Removal, reordering and changes to existing fields require a major
* version bump.
* sizeof(AVStream) must not be used outside libav*.
*/
typedef struct AVStream {
int index; /**< stream index in AVFormatContext */
/**
* Format-specific stream ID.
* decoding: set by libavformat
* encoding: set by the user, replaced by libavformat if left unset
*/
int id;
#if FF_API_LAVF_AVCTX
/**
* @deprecated use the codecpar struct instead
*/
attribute_deprecated
AVCodecContext *codec;
#endif
void *priv_data;
/**
* This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented.
*
* decoding: set by libavformat
* encoding: May be set by the caller before avformat_write_header() to
* provide a hint to the muxer about the desired timebase. In
* avformat_write_header(), the muxer will overwrite this field
* with the timebase that will actually be used for the timestamps
* written into the file (which may or may not be related to the
* user-provided one, depending on the format).
*/
AVRational time_base;
/**
* Decoding: pts of the first frame of the stream in presentation order, in stream time base.
* Only set this if you are absolutely 100% sure that the value you set
* it to really is the pts of the first frame.
* This may be undefined (AV_NOPTS_VALUE).
* @note The ASF header does NOT contain a correct start_time the ASF
* demuxer must NOT set this.
*/
int64_t start_time;
/**
* Decoding: duration of the stream, in stream time base.
* If a source file does not specify a duration, but does specify
* a bitrate, this value will be estimated from bitrate and file size.
*
* Encoding: May be set by the caller before avformat_write_header() to
* provide a hint to the muxer about the estimated duration.
*/
int64_t duration;
int64_t nb_frames; ///< number of frames in this stream if known or 0
int disposition; /**< AV_DISPOSITION_* bit field */
enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed.
/**
* sample aspect ratio (0 if unknown)
* - encoding: Set by user.
* - decoding: Set by libavformat.
*/
AVRational sample_aspect_ratio;
AVDictionary *metadata;
/**
* Average framerate
*
* - demuxing: May be set by libavformat when creating the stream or in
* avformat_find_stream_info().
* - muxing: May be set by the caller before avformat_write_header().
*/
AVRational avg_frame_rate;
/**
* For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet
* will contain the attached picture.
*
* decoding: set by libavformat, must not be modified by the caller.
* encoding: unused
*/
AVPacket attached_pic;
/**
* An array of side data that applies to the whole stream (i.e. the
* container does not allow it to change between packets).
*
* There may be no overlap between the side data in this array and side data
* in the packets. I.e. a given side data is either exported by the muxer
* (demuxing) / set by the caller (muxing) in this array, then it never
* appears in the packets, or the side data is exported / sent through
* the packets (always in the first packet where the value becomes known or
* changes), then it does not appear in this array.
*
* - demuxing: Set by libavformat when the stream is created.
* - muxing: May be set by the caller before avformat_write_header().
*
* Freed by libavformat in avformat_free_context().
*
* @see av_format_inject_global_side_data()
*/
AVPacketSideData *side_data;
/**
* The number of elements in the AVStream.side_data array.
*/
int nb_side_data;
/**
* Flags for the user to detect events happening on the stream. Flags must
* be cleared by the user once the event has been handled.
* A combination of AVSTREAM_EVENT_FLAG_*.
*/
int event_flags;
#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.
/**
* Real base framerate of the stream.
* This is the lowest framerate with which all timestamps can be
* represented accurately (it is the least common multiple of all
* framerates in the stream). Note, this value is just a guess!
* For example, if the time base is 1/90000 and all frames have either
* approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
*/
AVRational r_frame_rate;
#if FF_API_LAVF_FFSERVER
/**
* String containing pairs of key and values describing recommended encoder configuration.
* Pairs are separated by ','.
* Keys are separated from values by '='.
*
* @deprecated unused
*/
attribute_deprecated
char *recommended_encoder_configuration;
#endif
/**
* Codec parameters associated with this stream. Allocated and freed by
* libavformat in avformat_new_stream() and avformat_free_context()
* respectively.
*
* - demuxing: filled by libavformat on stream creation or in
* avformat_find_stream_info()
* - muxing: filled by the caller before avformat_write_header()
*/
AVCodecParameters *codecpar;
/*****************************************************************
* All fields below this line are not part of the public API. They
* may not be used outside of libavformat and can be changed and
* removed at will.
* Internal note: be aware that physically removing these fields
* will break ABI. Replace removed fields with dummy fields, and
* add new fields to AVStreamInternal.
*****************************************************************
*/
#define MAX_STD_TIMEBASES (30*12+30+3+6)
/**
* Stream information used internally by avformat_find_stream_info()
*/
struct {
int64_t last_dts;
int64_t duration_gcd;
int duration_count;
int64_t rfps_duration_sum;
double (*duration_error)[2][MAX_STD_TIMEBASES];
int64_t codec_info_duration;
int64_t codec_info_duration_fields;
int frame_delay_evidence;
/**
* 0 -> decoder has not been searched for yet.
* >0 -> decoder found
* <0 -> decoder with codec_id == -found_decoder has not been found
*/
int found_decoder;
int64_t last_duration;
/**
* Those are used for average framerate estimation.
*/
int64_t fps_first_dts;
int fps_first_dts_idx;
int64_t fps_last_dts;
int fps_last_dts_idx;
} *info;
int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */
// Timestamp generation support:
/**
* Timestamp corresponding to the last dts sync point.
*
* Initialized when AVCodecParserContext.dts_sync_point >= 0 and
* a DTS is received from the underlying container. Otherwise set to
* AV_NOPTS_VALUE by default.
*/
int64_t first_dts;
int64_t cur_dts;
int64_t last_IP_pts;
int last_IP_duration;
/**
* Number of packets to buffer for codec probing
*/
int probe_packets;
/**
* Number of frames that have been demuxed during avformat_find_stream_info()
*/
int codec_info_nb_frames;
/* av_read_frame() support */
enum AVStreamParseType need_parsing;
struct AVCodecParserContext *parser;
/**
* last packet in packet_buffer for this stream when muxing.
*/
struct AVPacketList *last_in_packet_buffer;
AVProbeData probe_data;
#define MAX_REORDER_DELAY 16
int64_t pts_buffer[MAX_REORDER_DELAY+1];
AVIndexEntry *index_entries; /**< Only used if the format does not
support seeking natively. */
int nb_index_entries;
unsigned int index_entries_allocated_size;
/**
* Stream Identifier
* This is the MPEG-TS stream identifier +1
* 0 means unknown
*/
int stream_identifier;
/**
* Details of the MPEG-TS program which created this stream.
*/
int program_num;
int pmt_version;
int pmt_stream_idx;
int64_t interleaver_chunk_size;
int64_t interleaver_chunk_duration;
/**
* stream probing state
* -1 -> probing finished
* 0 -> no probing requested
* rest -> perform probing with request_probe being the minimum score to accept.
*/
int request_probe;
/**
* Indicates that everything up to the next keyframe
* should be discarded.
*/
int skip_to_keyframe;
/**
* Number of samples to skip at the start of the frame decoded from the next packet.
*/
int skip_samples;
/**
* If not 0, the number of samples that should be skipped from the start of
* the stream (the samples are removed from packets with pts==0, which also
* assumes negative timestamps do not happen).
* Intended for use with formats such as mp3 with ad-hoc gapless audio
* support.
*/
int64_t start_skip_samples;
/**
* If not 0, the first audio sample that should be discarded from the stream.
* This is broken by design (needs global sample count), but can't be
* avoided for broken by design formats such as mp3 with ad-hoc gapless
* audio support.
*/
int64_t first_discard_sample;
/**
* The sample after last sample that is intended to be discarded after
* first_discard_sample. Works on frame boundaries only. Used to prevent
* early EOF if the gapless info is broken (considered concatenated mp3s).
*/
int64_t last_discard_sample;
/**
* Number of internally decoded frames, used internally in libavformat, do not access
* its lifetime differs from info which is why it is not in that structure.
*/
int nb_decoded_frames;
/**
* Timestamp offset added to timestamps before muxing
*/
int64_t mux_ts_offset;
/**
* Internal data to check for wrapping of the time stamp
*/
int64_t pts_wrap_reference;
/**
* Options for behavior, when a wrap is detected.
*
* Defined by AV_PTS_WRAP_ values.
*
* If correction is enabled, there are two possibilities:
* If the first time stamp is near the wrap point, the wrap offset
* will be subtracted, which will create negative time stamps.
* Otherwise the offset will be added.
*/
int pts_wrap_behavior;
/**
* Internal data to prevent doing update_initial_durations() twice
*/
int update_initial_durations_done;
/**
* Internal data to generate dts from pts
*/
int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];
/**
* Internal data to analyze DTS and detect faulty mpeg streams
*/
int64_t last_dts_for_order_check;
uint8_t dts_ordered;
uint8_t dts_misordered;
/**
* Internal data to inject global side data
*/
int inject_global_side_data;
/**
* display aspect ratio (0 if unknown)
* - encoding: unused
* - decoding: Set by libavformat to calculate sample_aspect_ratio internally
*/
AVRational display_aspect_ratio;
/**
* An opaque field for libavformat internal usage.
* Must not be accessed in any way by callers.
*/
AVStreamInternal *internal;
} AVStream;
avformat_new_stream
创建新流媒体
AVCodecParameters
avcodec_parameters_to_context
int avcodec_parameters_to_context(AVCodecContext *codec,
const AVCodecParameters *par);
参数配置拷贝。
avcodec_parameters_copy
拷贝编器参数
avcodec_parameters_free
AVRational
AVCodec
真正的编解码器,其中有编解码需要调用的函数
avcodec_find_decoder
AVCodec *avcodec_find_decoder(enum AVCodecID id);
查找解码器
avcodec_open2
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
打开解码器2
avcodec_close
int avcodec_close(AVCodecContext *avctx);
关闭解码器
avcodec_send_packet
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
将packet包发送到多线程解码器,此函数与avcode_receive_frame()对应。按 dts 递增的顺序向解码器送入编码帧 packet,解码器按 pts 递增的顺序输出原始帧 frame,实际上解码器不关注输入 packe t的 dts(错值都没关系),它只管依次处理收到的 packet,按需缓冲和解码。
avcodec_send_packet() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF
avcodec_send_packet() 多次发送 NULL 并不会导致解码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉解码器中缓存帧。因此播放完毕时应 avcodec_send_packet(NULL) 来取完缓存的帧,而 SEEK 操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧。
解码器通常的冲洗方法:调用一次 avcodec_send_packet(NULL)(返回成功),然后不停调用 avcodec_receive_frame() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_frame() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志。
avcodec_receive_frame
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
从多线程中接收已解码的frame。avcodec_receive_frame输出 frame 时,会根据各种因素设置好 frame->best_effort_timestamp(文档明确说明),实测 frame->pts 也会被设置(通常直接拷贝自对应的 packet.pts,文档未明确说明)用户应确保 avcodec_send_packet() 发送的 packet 具有正确的 pts,编码帧 packet 与原始帧 frame 间的对应关系通过 pts 确定。
输出 frame 时,frame->pkt_dts 拷贝自当前avcodec_send_packet() 发送的 packet 中的 dts,如果当前 packet 为 NULL(flush packet),解码器进入 flush 模式,当前及剩余的 frame->pkt_dts 值总为 AV_NOPTS_VALUE。因为解码器中有缓存帧,当前输出的 frame 并不是由当前输入的 packet 解码得到的,所以这个 frame->pkt_dts 没什么实际意义,可以不必关注。
avcodec_send_frame
avcodec_send_frame() 发送 NULL frame 时,编码器进入 flush 模式。
avcodec_send_frame() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF。
avcodec_send_frame() 多次发送 NULL 并不会导致编码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉编码器中缓存帧。因此编码完毕时应使用 avcodec_send_frame(NULL) 来取完缓存的帧,而SEEK操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧。
编码器通常的冲洗方法:调用一次 avcodec_send_frame(NULL)(返回成功),然后不停调用 avcodec_receive_packet() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_packet() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志。
对音频来说,如果 AV_CODEC_CAP_VARIABLE_FRAME_SIZE(在 AVCodecContext.codec.capabilities 变量中,只读)标志有效,表示编码器支持可变尺寸音频帧,送入编码器的音频帧可以包含任意数量的采样点。如果此标志无效,则每一个音频帧的采样点数目(frame->nb_samples)必须等于编码器设定的音频帧尺寸(avctx->frame_size),最后一帧除外,最后一帧音频帧采样点数可以小于 avctx->frame_size。
avcodec_receive_packet
按 pts 递增的顺序向编码器送入原始帧 frame,编码器按 dts 递增的顺序输出编码帧 packet,实际上编码器关注输入 frame 的 pts 不关注其 dts,它只管依次处理收到的 frame,按需缓冲和编码。
avcodec_receive_packet() 输出 packet 时,会设置 packet.dts,从 0 开始,每次输出的 packet 的 dts 加 1,这是视频层的 dts,用户写输出前应将其转换为容器层的 dts。
avcodec_receive_packet() 输出 packet 时,packet.pts 拷贝自对应的 frame.pts,这是视频层的 pts,用户写输出前应将其转换为容器层的 pts。
avcodec_decode_video2
解码视频数据
avcodec_encode_video2
int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet_ptr);
编码一帧视频数据,参数描述如下:
- avctx:编码器的AVCodecContext
- avpkt:编码输出的AVPacket
- frame:编输入的AVFrame
- got_packet_ptr:成功编码一个AVPacket的时候设置为1
avcodec_flush_buffers
void avcodec_flush_buffers(AVCodecContext *avctx);
立即丢掉解码器中缓存帧。
AVFrame
用于保存数据帧的数据结构,这里的两个帧分别是保存颜色转换前后的两帧图像。AVFrame中存储的是经过解码后的原始数据。在解码中,AVFrame是解码器的输出;在编码中,AVFrame是编码器的输入。
AVFrame的用法:
- AVFrame对象必须调用av_frame_alloc()在堆上分配,注意此处指的是AVFrame对象本身,AVFrame对象必须调用av_frame_free()进行销毁。
- AVFrame中包含的数据缓冲区是
- AVFrame通常只需分配一次,然后可以多次重用,每次重用前应调用av_frame_unref()将frame复位到原始的干净可用的状态。
存储原始帧数据(未编码的原始图像或音频格式,作为解码器的输出或编码器的输入)。
-
data是一个指针数组,数组的每一个元素是一个指针,指向视频中图像的某一plane或音频中某一声道的plane。
关于图像plane的详细说明参考“色彩空间与像素格式”,音频plane的详细说明参数“ffplay源码解析6-音频重采样 6.1.1节”。下面简单说明:
对于packet格式,一幅YUV图像的Y、U、V交织存储在一个plane中,形如YUVYUV...,data[0]指向这个plane;
一个双声道的音频帧其左声道L、右声道R交织存储在一个plane中,形如LRLRLR...,data[0]指向这个plane。
对于planar格式,一幅YUV图像有Y、U、V三个plane,data[0]指向Y plane,data[1]指向U plane,data[2]指向V plane;
一个双声道的音频帧有左声道L和右声道R两个plane,data[0]指向L plane,data[1]指向R plane。 -
linesize 对于视频来说,linesize是每行图像的大小(字节数)。注意有对齐要求。
对于音频来说,linesize是每个plane的大小(字节数)。音频只使用linesize[0]。对于planar音频来说,每个plane的大小必须一样。
linesize可能会因性能上的考虑而填充一些额外的数据,因此linesize可能比实际对应的音视频数据尺寸要大。 -
extended_data 对于视频来说,直接指向data[]成员。
对于音频来说,packet格式音频只有一个plane,一个音频帧中各个声道的采样点交织存储在此plane中;planar格式音频每个声道一个plane。在多声道planar格式音频中,必须使用extended_data才能访问所有声道,什么意思?
在有效的视频/音频frame中,data和extended_data两个成员都必须设置有效值。 -
width,height视频帧宽和高(像素)
-
nb_samples 音频帧中单个声道中包含的采样点数
-
format 帧格式,如果未知格式或未设置,则为-1.对于视频帧,此值对应于"enum AVPixelFormat"结构。
enum AVPixelFormat {
AV_PIX_FMT_NONE = -1,
AV_PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
AV_PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
AV_PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB...
AV_PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR...
......
}
对于音频帧,此值对应于"enum AVSampleFormat"格式
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
- key_frame 视频帧是否是关键帧的标识,1->关键帧,0->非关键帧。
- pict_type 视频帧类型(I、B、P等)。如下:
enum AVPictureType {
AV_PICTURE_TYPE_NONE = 0, ///< Undefined
AV_PICTURE_TYPE_I, ///< Intra
AV_PICTURE_TYPE_P, ///< Predicted
AV_PICTURE_TYPE_B, ///< Bi-dir predicted
AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG-4
AV_PICTURE_TYPE_SI, ///< Switching Intra
AV_PICTURE_TYPE_SP, ///< Switching Predicted
AV_PICTURE_TYPE_BI, ///< BI type
};
-
sample_aspect_ratio 视频帧的宽高比。
-
pts 显示时间戳。单位是time_base。
-
pkt_pts 此frame对应的packet中的显示时间戳。是从对应packet(解码生成此frame)中拷贝PTS得到此值。
此frame对应的packet中的解码时间戳。是从对应packet(解码生成此frame)中拷贝DTS得到此值。
如果对应的packet中只有dts而未设置pts,则此值也是此frame的pts。 -
coded_picture_number 在编码流中当前图像的序号。
-
display_picture_number 在显示序列中当前图像的序号。
-
interlaced_frame 图像逐行/隔行模式标识。
-
sample_rate 音频采样率。
-
channel_layout 音频声道布局。每bit代表一个特定的声道,参考channel_layout.h中的定义,一目了然。
-
buf
此帧的数据可以由AVBufferRef管理,AVBufferRef提供AVBuffer引用机制。这里涉及到缓冲区引用计数概念:
AVBuffer是FFmpeg中很常用的一种缓冲区,缓冲区使用引用计数(reference-counted)机制。
AVBufferRef则对AVBuffer缓冲区提供了一层封装,最主要的是作引用计数处理,实现了一种安全机制。用户不应直接访问AVBuffer,应通过AVBufferRef来访问AVBuffer,以保证安全。
FFmpeg中很多基础的数据结构都包含了AVBufferRef成员,来间接使用AVBuffer缓冲区。
相关内容参考“FFmpeg数据结构AVBuffer”
帧的数据缓冲区AVBuffer就是前面的data成员,用户不应直接使用data成员,应通过buf成员间接使用data成员。那extended_data又是做什么的呢??如果buf[]的所有元素都为NULL,则此帧不会被引用计数。必须连续填充buf[] - 如果buf[i]为非NULL,则对于所有j<i,buf[j]也必须为非NULL。
每个plane最多可以有一个AVBuffer,一个AVBufferRef指针指向一个AVBuffer,一个AVBuffer引用指的就是一个AVBufferRef指针。
对于视频来说,buf[]包含所有AVBufferRef指针。对于具有多于AV_NUM_DATA_POINTERS个声道的planar音频来说,可能buf[]存不下所有的AVBbufferRef指针,多出的AVBufferRef指针存储在extended_buf数组中。 -
extended_buf&nb_extended_buf
对于具有多于AV_NUM_DATA_POINTERS个声道的planar音频来说,可能buf[]存不下所有的AVBbufferRef指针,多出的AVBufferRef指针存储在extended_buf数组中。
注意此处的extended_buf和AVFrame.extended_data的不同,AVFrame.extended_data包含所有指向各plane的指针,而extended_buf只包含AVFrame.buf中装不下的指针。
extended_buf是构造frame时av_frame_alloc()中自动调用av_malloc()来分配空间的。调用av_frame_unref会释放掉extended_buf。
nb_extended_buf是extended_buf中的元素数目。 -
best_effort_timestamp
-
pkt_pos 记录最后一个扔进解码器的packet在输入文件中的位置偏移量。
-
pkt_duration 对应packet的时长,单位是AVStream->time_base。
-
channels 音频声道数量。
-
pkt_size 对应packet的大小。
-
crop_ 用于视频帧图像裁切。四个值分别为从frame的上/下/左/右边界裁切的像素数。
size_t crop_top; size_t crop_bottom; size_t crop_left; size_t crop_right;
av_frame_alloc
AVFrame *av_frame_alloc(void);
构造一个frame,对象各成员被设为默认值。
此函数只分配AVFrame对象本身,而不分配AVFrame中的数据缓冲区。
av_frame_free
void av_frame_free(AVFrame **frame);
释放一个frame。
av_frame_ref
int av_frame_ref(AVFrame *dst, const AVFrame *src);
为src中的数据建立一个新的引用。
将src中帧的各属性拷到dst中,并且为src中每个AVBufferRef创建一个新的引用。
如果src未使用引用计数,则dst中会分配新的数据缓冲区,将将src中缓冲区的数据拷贝到dst中的缓冲区。
av_frame_clone
AVFrame *av_frame_clone(const AVFrame *src);
创建一个新的frame,新的frame和src使用同一数据缓冲区,缓冲区管理使用引用计数机制。
本函数相当于av_frame_alloc()+av_frame_ref()。
av_frame_unref
void av_frame_unref(AVFrame *frame);
解除本frame对本frame中所有缓冲区的引用,并复位frame中各成员。
av_frame_move_ref
void av_frame_move_ref(AVFrame *dst, AVFrame *src);
将src中所有数据拷贝到dst中,并复位src。
为避免内存泄漏,在调用av_frame_move_ref(dst, src)
之前应先调用av_frame_unref(dst)
。
av_frame_get_buffer
int av_frame_get_buffer(AVFrame *frame, int align);
为音频或视频数据分配新的缓冲区。
调用本函数前,帧中的如下成员必须先设置好:
- format (视频像素格式或音频采样格式)
- width、height(视频画面和宽和高)
- nb_samples、channel_layout(音频单个声道中的采样点数目和声道布局)
本函数会填充AVFrame.data和AVFrame.buf数组,如果有需要,还会分配和填充AVFrame.extended_data和AVFrame.extended_buf。
对于planar格式,会为每个plane分配一个缓冲区。
av_frame_copy
int av_frame_copy(AVFrame *dst, const AVFrame *src);
将src中的帧数据拷贝到dst中。
本函数并不会有任何分配缓冲区的动作,调用此函数前dst必须已经使用了和src同样的参数完成了初始化。
本函数只拷贝帧中的数据缓冲区的内容(data/extended_data数组中的内容),而不涉及帧中任何其他的属性。
AVPacket
解析文件时会将音/视频帧读入到packet中,对于视频而言,一个AVPacket通常只包含一个压缩视频帧。而对于音频而言,一个AVPacket可能包含多个完整的音频压缩帧。AVPacket也可以不包含压缩编码数据,而只包含side data,这种包可以称为空packet。例如,编码结束后只需要更新一些参数时就可以发空packet。
AVPacket对象可以在栈上分配,注意此处指的是AVPacket对象本身。而AVPacket中包含的数据缓冲区是通过av_malloc()在堆上分配的。
音视频数据缓冲区
uint8_t *data
:int size
:
数据缓冲区地址与大小。音视频编码压缩数据存储于此片内存区域。此内存区域由AVBufferRef *buf
管理。AVBufferRef *buf
:
数据缓冲区引用,也可叫引用计数缓冲区。对上一字段uint8_t *data
指向的内存区域提供引用计数等管理机制。
AVBufferRef对数据缓冲区提供了管理机制,用户不应直接访问数据缓冲区。参考“FFmpeg数据结构AVBuffer”
如果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
额外类型数据
AVPacketSideData *side_data
int side_data_elems
由容器(container)提供的额外包数据。TODO: 待研究
packet属性
-
AVBufferRef *buf
用来管理data指针引用的数据缓存,其使用在后面介绍。
-
AVPacketSideData *side_data
容器提供的一些附加数据
-
int64_t pts
:
显示时间戳。单位time_base,帧率的倒数。 -
int64_t dts
:
解码时间戳。单位time_base,帧率的倒数。 -
uint8_t *data
指向保存压缩数据的指针,这就是AVPacket的实际数据。
-
int size
data的大小。
-
int stream_index
:
当前包(packet)所有流(stream)的索引(index)。 -
int flags
:
packet标志位。比如是否关键帧等。结合AV_PKT_FLAG使用,其中最低为1表示该数据是一个关键帧。
#define AV_PKT_FLAG_KEY 0x0001 //关键帧
#define AV_PKT_FLAG_CORRUPT 0x0002 //损坏的数据
#define AV_PKT_FLAG_DISCARD 0x0004 /丢弃的数据
-
int side_data_elems:
边缘数据元数个数。
-
int64_t duration
:
当前包解码后的帧播放持续的时长。单位timebase。值等于下一帧pts减当前帧pts。 -
int64_t pos
:
当前包在流中的位置,单位字节。未知则值 默认为-1。 -
int64_t convergence_duration
该字段已deprecated,不在使用。
av_packet_alloc
创建AVPacket空间初始化
av_init_packet
初始化AVPacket
av_packet_ref
av_packet_ref()作了处理如下:
- 如果src->buf为NULL,则将src缓冲区拷贝到新创建的dst缓冲区,注意src缓冲区不支持引用计数,但新建的dst缓冲区是支持引用计数的,因为dst->buf不为NULL。
- 如果src->buf不为空,则dst与src共用缓冲区,调用
av_buffer_ref()
增加缓冲区引用计数即可。av_buffer_ref()
分析参考“FFmpeg数据结构AVBuffer”
av_packet_clone
创建AVPacket复制,先创建一个新的AVPacket,然后再进行计数引用+数据拷贝,使得新的AVPacket指向老的AVPacket同一个data。
av_copy_packet
复制一个新的packet,包括数据缓存。
av_packet_unref
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()
分析
av_packet_free
void av_packet_free(AVPacket **pkt);
av_packet_free是先把pkt中的内容清空,然后再把指针清空,让pkt彻底无法使用了,如果需要重新使用,需要重新分配内存
av_free_packet
void av_free_packet(AVPacket *pkt);
av_free_packet其实就是清空pkt中data以及buf的内容,并没有把pkt的指针清空,我们可以看到其函数内部调用了av_buffer_unref。释放packet,包括其data引用的数据缓存,现在可以使用av_packet_unref
代替。
av_packet_from_data
av_packet_from_data(AVPacket *pkt, uint8_t *data, int size)
初始化一个引用计数的packet,并指定了其数据缓存。
av_dup_packet
通过调用 av_malloc、memcpy、memset等函数, 将shared buffer 的AVPacket duplicate(复制)到独立的buffer中。并且修改AVPacket的析构函数指针av_destruct_pkt。(此函数已被弃用),是复制src->data引用的数据缓存,赋值给dst。也就是创建两个独立packet,这个功能现在可用使用函数av_packet_ref
来代替。
av_grow_packet
int av_grow_packet(AVPacket *pkt, int grow_by);
增大Packet->data指向的数据缓存。
av_shrink_packet
void av_shrink_packet(AVPacket *pkt, int size)
减小Packet->data指向的数据缓存。
AVDictionary
av_dict_free
void av_dict_free(AVDictionary **mm)
释放AVDictionary.
AVIOContext
其中AVIOContext是FFMPEG管理输入输出数据的结构体。(位于avio.h)。AVIOContext中有以下几个变量比较重要:
unsigned char *buffer:缓存开始位置
int buffer_size:缓存大小(默认32768)
unsigned char *buf_ptr:当前指针读取到的位置
unsigned char *buf_end:缓存结束的位置
void *opaque:URLContext结构体
avio_alloc_context
SwsContext
swr_init
初始化SwsContext
swr_alloc
申请空间,音频重采样上下文
swr_alloc_set_opts
设置音频重采样
swr_free
释放SwsContext空间
swr_convert
重采样
sws_scale
sws_scale函数主要是用来做视频像素格式和分辨率的转换,其优势在于:可以在同一个函数里实现:1.图像色彩空间转换, 2:分辨率缩放,3:前后图像滤波处理。不足之处在于:效率相对较低,不如libyuv或OpenGL_Shader。
sws_getContext
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
sws_getCachedContext
struct SwsContext *sws_getCachedContext(struct SwsContext *context,
int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
AVFilter
AVFilter库提供了一个通用的音频,视频,字幕等滤镜处理框架。在AVFilter中,有多个输入和多个输出。
AVBuffer
struct AVBuffer定义于“libavutil/buffer_internal.h”,buffer_internal.h位于FFmpeg工程源码中,而FFmpeg提供的开发库头文件中并无此文件,因此这是一个内部数据结构,不向用户开放,用户不应直接访问AVBuffer,应通过AVBufferRef来访问AVBuffer,以保证安全。
struct AVBuffer {
uint8_t *data; /**< data described by this buffer */
int size; /**< size of data in bytes */
/**
* number of existing AVBufferRef instances referring to this buffer
*/
atomic_uint refcount;
/**
* a callback for freeing the data
*/
void (*free)(void *opaque, uint8_t *data);
/**
* an opaque pointer, to be used by the freeing callback
*/
void *opaque;
/**
* A combination of BUFFER_FLAG_*
*/
int flags;
};
- data: 缓冲区地址
- size: 缓冲区大小
- refcount: 引用计数值
- free: 用于释放缓冲区内存的回调函数
- opaque: 提供给free回调函数的参数
- flags: 缓冲区标志
AVBufferRef
struct AVBufferRef定义于buffer.h中:
/**
* A reference to a data buffer.
*
* The size of this struct is not a part of the public ABI and it is not meant
* to be allocated directly.
*/
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;
- buffer: AVBuffer
- data: 缓冲区地址,实际等于buffer->data
- size: 缓冲区大小,实际等于buffer->size
av_buffer_alloc
AVBufferRef *av_buffer_alloc(int size)
{
AVBufferRef *ret = NULL;
uint8_t *data = NULL;
data = av_malloc(size);
if (!data)
return NULL;
ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
if (!ret)
av_freep(&data);
return ret;
}
av_buffer_alloc()作了如下处理:
a) 使用av_malloc分配缓冲区
b) 调用av_buffer_create()创建AVBuffer AVBufferRef::*buffer
成员,用于管理AVBuffer缓冲区
c) 返回AVBufferRef *
对象
av_buffer_create
AVBufferRef *av_buffer_create(uint8_t *data, int size,
void (*free)(void *opaque, uint8_t *data),
void *opaque, int flags)
{
AVBufferRef *ref = NULL;
AVBuffer *buf = NULL;
buf = av_mallocz(sizeof(*buf));
if (!buf)
return NULL;
buf->data = data;
buf->size = size;
buf->free = free ? free : av_buffer_default_free;
buf->opaque = opaque;
atomic_init(&buf->refcount, 1);
if (flags & AV_BUFFER_FLAG_READONLY)
buf->flags |= BUFFER_FLAG_READONLY;
ref = av_mallocz(sizeof(*ref));
if (!ref) {
av_freep(&buf);
return NULL;
}
ref->buffer = buf;
ref->data = data;
ref->size = size;
return ref;
}
av_buffer_create()是一个比较核心的函数,从其实现代码很容易看出AVBufferRef和AVBuffer这间的关系。
函数主要功能就是初始化AVBuffer AVBufferRef::*buffer
成员,即为上述清单ref->buffer
各字段赋值,最终,AVBufferRef *ref
全部构造完毕,将之返回。
其中void (*free)(void *opaque, uint8_t *data)
参数赋值为av_buffer_default_free
,实现如下。其实就是直接调用了av_free
回收内存。
void av_buffer_default_free(void *opaque, uint8_t *data)
{
av_free(data);
}
av_buffer_ref
AVBufferRef *av_buffer_ref(AVBufferRef *buf)
{
AVBufferRef *ret = av_mallocz(sizeof(*ret));
if (!ret)
return NULL;
*ret = *buf;
atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);
return ret;
}
av_buffer_ref()处理如下:
a) *ret = *buf;
一句将buf各成员值赋值给ret中对应成员,buf和ret将共用同一份AVBuffer缓冲区
b) atomic_fetch_add_explicit(...);
一句将AVBuffer缓冲区引用计数加1
注意此处的关键点:共用缓冲区(缓冲区不拷贝),缓冲区引用计数加1
av_buffer_unref
static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
{
AVBuffer *b;
b = (*dst)->buffer;
if (src) {
**dst = **src;
av_freep(src);
} else
av_freep(dst);
if (atomic_fetch_add_explicit(&b->refcount, -1, memory_order_acq_rel) == 1) {
b->free(b->opaque, b->data);
av_freep(&b);
}
}
void av_buffer_unref(AVBufferRef **buf)
{
if (!buf || !*buf)
return;
buffer_replace(buf, NULL);
}
av_buffer_unref()处理如下:
a) 回收AVBufferRef **buf
内存
b) 将(*buf)->buffer
(即AVBAVBufferRef的成员AVBuffer)的引用计数减1,若引用计数为0,则通过b->free(b->opaque, b->data);
调用回调函数回收AVBuffer缓冲区内存
注意此处的关键点:销毁一个AVBufferRef时,将其AVBuffer缓冲区引用计数减1,若缓冲区引用计数变为0,则将缓冲区也回收,这很容易理解,只有当缓冲区不被任何对象引用时,缓冲区才能被销毁
AVUtil
AVUtil是一个实用库,用于辅助多媒体编程。此库包含安全的可移植字符串函数、随机数生成器、数据结构、附加数学函数、加密和多媒体相关功能(如像素和样本格式的枚举)。libavcodec和libavformat并不依赖此库。
FFmpeg function
av_register_all
定义在 libavformat 里,调用它用以注册所有支持的文件格式以及编解码器,从其实现代码里可以看到它会调用
avcodec_register_all,因此之后就可以用所有ffmpeg支持的codec了。
avformat_network_init
初始化网络
avcodec_register_all
注册所有解码器
av_find_best_stream
int av_find_best_stream(AVFormatContext *ic,
enum AVMediaType type,
int wanted_stream_nb,
int related_stream,
AVCodec **decoder_ret,
int flags);
获取音视频对应的stream_index。其实,还有另外一种方法来获得音视频对应的stream_index
avio_open
int avio_open(AVIOContext **s, const char *filename, int flags)
{
return avio_open2(s, filename, flags, NULL, NULL);
}
是avio_open2的早期版本,比avio_open2少了最后2个参数。而它前面几个参数的含义和avio_open2是一样的。从源代码中可以看出,avio_open内部调用了avio_open2,并且把avio_open2的后2个参数设置成了NULL,因此它的功能实际上和avio_open2是一样的。
avio_open2
int avio_open2(AVIOContext **s, const char *url, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options);
该函数用于打开FFmpeg的输入输出文件。avio_open2()的声明位于libavformat\avio.h文件中。
- s:函数调用成功之后创建的AVIOContext结构体。
- url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
- flags:打开地址的方式。可以选择只读,只写,或者读写。取值如下。
- AVIO_FLAG_READ:只读。
- AVIO_FLAG_WRITE:只写。
- AVIO_FLAG_READ_WRITE:读写。
- int_cb:目前还没有用过。
- options:目前还没有用过
av_packet_rescale_ts
avformat_write_header
写入头文件信息。
av_write_frame
av_interleaved_write_frame
av_write_trailer
int av_write_trailer(AVFormatContext *s);
用于输出 文件尾,它的声明位于libavformat\avformat.h
av_read_frame
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
从AVFormatContext中读取数据到pkt中。
av_guess_format
AVOutputFormat *av_guess_format(const char *short_name,
const char *filename,
const char *mime_type);
- short_name:格式的名称。
- filename:文件的名称。
- mime_type:MIME类型。
返回最匹配的AVOutputFormat,如果没有很匹配的AVOutputFormat,则返回NULL。
av_oformat_netx
AVOutputFormat *av_oformat_next(const AVOutputFormat *f);
参数不为NULL的时候用于获得下一个AVOutputFormat否则获得第一个AVOutputFormat.
av_match_name
int av_match_name(const char *name, const char *names);
用于比较两个格式的名称。简单地说就是比较字符串。注意该函数的字符串是不区分大小写的:字符都转换为小写进行比较。
av_match_ext
int av_match_ext(const char *filename, const char *extensions);
用于比较文件的后缀。该函数首先通过反向查找的方式找到输入文件名中的“.”,就可以通过获取“.”后面的字符串来得到该文件的后缀。然后调用av_match_name(),采用和比较格式名称的方法比较两个后缀。
av_dump_format
最后调用一个帮助函数av_dump_format,输出文件的信息,也就是我们在使用ffmpeg时能看到的文件详细信息。第二个参数指定输出哪条流的信息,-1表示给ffmpeg自己选择。最后一个参数用于指定dump的是不是输出文件,我们dump的是输入文件,因此一定要是0。
av_image_fill_arrays
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
const uint8_t *src,
enum AVPixelFormat pix_fmt, int width, int height, int align);
av_image_get_buffer_size
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
获取计算得到的buffer空间size.