当前muxer/demuxer的匹配
在FFmpeg的文件转换过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的demuxer和muxer。匹配上的demuxer和muxer都保存在如下所示,定义在ffmpeg.c里的全局变量
file_iformat和
file_oformat中:
static AVInputFormat *file_iformat;
static AVOutputFormat *file_oformat;
1. demuxer匹配
在libavformat\utils.c中的
static AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的判断。其调用顺序如下:
void parse_options(int argc, char **argv, const OptionDef *options)
static void opt_input_file(const char *filename)
static void opt_input_file(const char *filename)
int av_open_input_file(…… )
AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
static AVInputFormat *av_probe_input_format2(……)
opt_input_file函数是在保存在
const OptionDef options[]数组中,用于
void parse_options(int argc, char **argv, const OptionDef *options)中解析argv里的“-i” 参数,也就是输入文件名时调用的。
2. muxer匹配
与demuxer的匹配不同,muxer的匹配是调用
guess_format函数,根据
main( )
函数的argv里的输出文件后缀名来进行的。
void parse_options(int argc, char **argv, const OptionDef *options)
void parse_arg_file(const char *filename)
static void opt_output_file(const char *filename)
AVOutputFormat *guess_format(const char *short_name, const char *filename,
const char *mime_type)
当前encoder/decoder的匹配
在
main( )函数中除了解析传入参数并初始化demuxer与muxer的
parse_options( )函数以外,其他的功能都是在
av_encode( )函数里完成的。
在libavcodec\utils.c中有如下二个函数。
AVCodec *avcodec_find_encoder(enum CodecID id)
AVCodec *avcodec_find_decoder(enum CodecID id)
他们的功能就是根据传入的CodecID,找到匹配的encoder和decoder。
在
av_encode( )函数的开头,首先初始化各个
AVInputStream和
AVOutputStream,然后分别调用上述二个函数,并将匹配上的encoder与decoder分别保存在
AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec与
AVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec变量中。
其他主要数据结构
1.
AVFormatContext
AVFormatContext是FFMpeg格式转换过程中实现输入和输出功能、保存相关数据的主要结构。每一个输入和输出文件,都在如下定义的指针数组全局变量中有对应的实体。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的
iformat或
oformat成员赋值。
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
对一个
AVFormatContext来说,二个成员不能同时有值,即一个
AVFormatContext不能同时含有demuxer和muxer。
在
main( )函数开头的
parse_options( )函数中找到了匹配的muxer和demuxer之后,根据传入的argv参数,初始化每个输入和输出的
AVFormatContext结构,并保存在相应的
output_files和
input_files指针数组中。
在
av_encode( )函数中,
output_files和
input_files是作为函数参数传入后,在其他地方就没有用到了。
2. AVCodecContext
保存
AVCodec指针和与codec相关的数据,如video的width、height,audio的sample rate等。
AVCodecContext中的
codec_type,codec_id二个变量对于encoder/decoder的匹配来说,最为重要。
enum CodecType codec_type; /* see CODEC_TYPE_xxx */
enum CodecID codec_id; /* see CODEC_ID_xxx */
如上所示,
codec_type保存的是
CODEC_TYPE_VIDEO,
CODEC_TYPE_AUDIO等媒体类型,
codec_id保存的是
CODEC_ID_FLV1,
CODEC_ID_VP6F等编码方式。
以支持flv格式为例,在前述的
av_open_input_file(…… )
函数中,匹配到正确的
AVInputFormat
demuxer后,通过
av_open_input_stream( )函数中调用
AVInputFormat的
read_header接口来执行flvdec.c中的
flv_read_header( )函数。在
flv_read_header( )函数内,根据文件头中的数据,创建相应的视频或音频
AVStream,并设置
AVStream中
AVCodecContext的正确的
codec_type值。
codec_id值是在解码过程中
flv_read_packet( )函数执行时根据每一个packet头中的数据来设置的。
3. AVStream
AVStream结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:
AVCodecContext *codec; /**< codec context */
void *priv_data;
其中
codec指针保存的就是上节所述的encoder或decoder结构。
priv_data指针保存的是和具体编解码流相关的数据,如下代码所示,在ASF的解码过程中,
priv_data保存的就是
ASFStream结构的数据。
AVStream *st;
ASFStream *asf_st;
… …
st->priv_data = asf_st;
4. AVInputStream/ AVOutputStream
根据输入和输出流的不同,前述的
AVStream结构都是封装在
AVInputStream和
AVOutputStream结构中,在
av_encode( )函数中使用。
AVInputStream中还保存的有与时间有关的信息。
AVOutputStream中还保存有与音视频同步等相关的信息。
5. AVPacket
AVPacket结构定义如下,其是用于保存读取的packet数据。
typedef struct AVPacket {
int64_t pts; ///< presentation time stamp in time_base units
int64_t dts; ///< decompression time stamp in time_base units
uint8_t *data;
int size;
int stream_index;
int flags;
int duration; ///< presentation duration in time_base units (0 if not available)
void (*destruct)(struct AVPacket *);
void *priv;
int64_t pos; ///< byte position in stream, -1 if unknown
} AVPacket;
在
av_encode( )函数中,调用
AVInputFormat的
(*read_packet)(struct AVFormatContext *, AVPacket *pkt);接口,读取输入文件的一帧数据保存在当前输入
AVFormatContext的
AVPacket成员中。
av_encode函数主要流程
av_encode( )函数是FFMpeg中最重要的函数,编解码和输出等大部分功能都在此函数内完成,因此有必要详细描述一下这个函数的主要流程。
1. input streams initializing
2. output streams initializing
3. encoders and decoders initializing
4. set meta data information from input file if required.
5. write output files header
6. loop of handling each frame
a. read frame from input file:
b. decode frame data
c. encode new frame data
d. write new frame to output file
7. write output files trailer
8. close each encoder and decoder