FFmpeg完美入门【7】-FFmpeg架构之Demuxer和muxer模块分析

http://it6655.com/2012/09/ffmpeg-7-html

1概述
ffmpeg的demuxer和muxer接口分别在AVInputFormat和AVOutputFormat两个结构体中实现,在av_register_all()函数中将两个结构分别静态初始化为两个链表,保存在全局变量:first_iformat和first_oformat两个变量中。在FFmpeg的文件转换或者打开过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的demuxer和muxer,得到合适的信息后保存在AVFormatContext中。

2相关数据结构介绍

1、AVInputFormat

该结构被称为demuxer,是音视频文件的一个解封装器,它的定义如下:

 
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef  struct AVInputFormat  {
const  char  *name ; 
const  char  *long_name ; 
int priv_data_size ;  //具体文件容器格式对应的Context的大小,如:
avicontext  int  ( *read_probe ) (AVProbeData  * ) ;
int  ( *read_header ) ( struct AVFormatContext  *, AVFormatParameters  *ap ) ; 
int  ( *read_packet ) ( struct AVFormatContext  *, AVPacket  *pkt ) ; 
int  ( *read_close ) ( struct AVFormatContext  * ) ; 
#if FF_API_READ_SEEK 
attribute_deprecated  int  ( *read_seek ) ( struct AVFormatContext  *,  int stream_index,  int64_t timestamp,  int flags ) ; 
#endif 
int64_t  ( *read_timestamp ) ( struct AVFormatContext  *s,  int stream_index,  int64_t  *pos,  int64_t pos_limit ) ;
int flags ;
const  char  *extensions ; 
int value ;  int  ( *read_play ) ( struct AVFormatContext  * ) ;
int  ( *read_pause ) ( struct AVFormatContext  * ) ; 
const  struct AVCodecTag  *  const  *codec_tag ; 
int  ( *read_seek2 ) ( struct AVFormatContext  *s,  int stream_index,  int64_t min_ts,  int64_t ts,  int64_t max_ts,  int flags ) ; 
#if FF_API_OLD_METADATA2 
const AVMetadataConv  *metadata_conv ; 
#endif 
const AVClass  *priv_class ;  ///< AVClass for the private context struct AVInputFormat *next; 
} AVInputFormat ;

对于不同的文件格式要实现相应的函数接口,这样每一种格式都有一个对应的demuxer,所有的demuxer都保存在全局变量first_iformat中。红色表示提供的接口。

2、AVOutputFormat

该结构与AVInputFormat类似也是在编译时静态初始化,组织为一个链表结构,提供了多个muxer的函数接口。

 
  
1
2
3
int  ( *write_header ) ( struct AVFormatContext  * ) ; 
int  ( *write_packet ) ( struct AVFormatContext  *,AVPacket  *pkt ) ; 
int  ( *write_trailer ) ( struct AVFormatContext  * ) ;

对于不同的文件格式要实现相应的函数接口,这样每一种格式都有一个对应的muxer,所有的muxer都保存在全局变量first_oformat中。

3、AVFormatContext
该结构表示与程序当前运行的文件容器格式使用的上下文,着重于所有文件容器共有的属性,在运行时动态的确定其值,是AVInputFormat和AVOutputFormat的载体,但同一个结构对象只能使AVInputFormat和AVOutputFormat中的某一个有效。每一个输入和输出文件,都在

 
  
1
2
static AVFormatContext  *output_files [MAX_FILES ] ;
static AVFormatContext  *input_files [MAX_FILES ] ;

定义的指针数组全局变量中有对应的实体。对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformat或oformat成员赋值。在转码时读写数据是通过AVFormatContext结构进行的。定义如下:

 

 
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef  struct AVFormatContext 
{ 
const AVClass  *av_class ; 
struct AVInputFormat  *iformat ;  //指向具体的demuxer
struct AVOutputFormat  *oformat ;  //指向具体的muxer
void  *priv_data ;  //具体文件容器格式的Context如:
avicontext AVIOContext  *pb ;  //广义的输入输出;
unsigned  int nb_streams ;  //本次打开的文件容器中流的数量 
AVStream  **streams ;  //每个流的相关描述 
char filename [ 1024 ] ;  // input or output filename */
int64_t timestamp ; 
int ctx_flags ; 
struct AVPacketList  *packet_buffer ; 
…… 
enum CodecID video_codec_id ; 
enum CodecID audio_codec_id ; 
enum CodecID subtitle_codec_id ;
unsigned  int max_index_size ; 
unsigned  int max_picture_buffer ; 
…… 
struct AVPacketList  *raw_packet_buffer ; 
struct AVPacketList  *raw_packet_buffer_end ; 
struct AVPacketList  *packet_buffer_end ; 
…… 
} AVFormatContext ;

红色部分的成员是AVFormatContext中最为重要的成员变量,这些变量的初始化是ffmpeg能正常工作的必要条件,那么,AVFormatContext是如何被初始化的呢?文件的格式是如何被探测到的呢? 首先我们来探讨:

struct AVInputFormat *iformat; //指向具体的demuxer
struct AVOutputFormat *oformat; //指向具体的muxer
void *priv_data; //具体文件容器格式的Context如:avicontext

三个成员的初始化。 在avformat_open_input()函数中调用了init_input()函数,然后用调用了av_probe_input_format()函数实现了对AVFormatContext的初始化。其调用关系如下:

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
AVInputFormat *fmt,int buf_size,AVFormatParameters *ap);
int avformat_open_input(ic_ptr, filename, fmt, &opts); static int init_input(s, filename)
av_probe_input_format(&pd, 0);

av_probe_input_format (AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的判断。与demuxer的匹配不同,muxer的匹配是调用guess_format函数,根据main( ) 函数的argv里的输出文件后缀名来进行的。至此完成了前三个重要成员的初始化,具体的做法就不在深入分析。

下面分别给出av_read_frame函数以及av_write_frame函数的基本流程。
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
->av_read_frame_internel-> av_read_packet ->iformat->read_packet(在实现中会丢弃多余信息) ->av_get_packet ->get_xxx
int av_write_frame(AVFormatContext *s, AVPacket *pkt); ->oformat->write_packet ->put_xxx 由上可见,对AVFormatContext的读写操作最终是通过ByteIOContext来实现的,这样,AVFormatContext与 URLContext就由ByteIOContext结构联系到一起了。在AVFormat结构体中有一个packet的缓冲区raw_packet_buffer,是AVPackList的指针类型,av_read_packet函数将读到的包添加至raw_packet_buffer链表末尾。


你可能感兴趣的:(【多媒体技术】,【ffmpeg】,音视频开发)