URLProtocol,URLContext和ByteIOContext是FFMpeg操作文件(即I/O,包括网络数据流)的结构,这几个结构现实的功能类似于C++的多态继承吧,C++的多态是通过子类继承实现,而FFMpeg的“多态”是通过静态对像现实。这部分的代码非常值得C程序借鉴,我是说,如果你要在C里实现类似C++多态性的功能;比如当你要区分你老婆和情人之间的不同功能时。
好了,先来看一下这三个struct的定义吧
typedef struct URLProtocol {
const char *name; //Rotocol名称
int (*url_open)(URLContext *h, const char *url, int flags); //open函数指针对象,以下类似
int (*url_read)(URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, unsigned char *buf, int size);
int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
struct URLProtocol *next; //指向下一个URLProtocol,具体说明见备注1
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
} URLProtocol;
备注1:FFMpeg所有的Protol类型都用这个变量串成一个链表,表头为avio.c里的URLProtocol *first_protocol = NULL;
每个文件类似都有自己的一个URLProtocol静态对象,如libavformat/file.c里
URLProtocol file_protocol = {
"file",
file_open,
file_read,
file_write,
file_seek,
file_close,
.url_get_file_handle = file_get_handle,
};
再通过av_register_protocol()将他们链接成链表。在FFMpeg中所有的URLProtocol对像值都在编译时确定。
typedef struct URLContext {
#if LIBAVFORMAT_VERSION_MAJOR >= 53
const AVClass *av_class; ///< information for av_log(). Set by url_open().
#endif
struct URLProtocol *prot; //指向具体的I/0类型,在运行时通过文件URL确定,如是file类型时就是file_protocol
int flags;
int is_streamed; /**< true if streamed (no seek possible), default = false */
int max_packet_size; /**< if non zero, the stream is packetized with this max packet size */
void *priv_data; //指向具体的I/O句柄
char *filename; /**< specified URL */
} URLContext;
不同于URLProtocol对象值在编译时确定,URLContext对象值是在运行过程中根据输入的I/O类型动态确定的。这一动一静组合起到了C++的多态继承一样的作用。URLContext像是基类,为大家共同所有,而URLProtocol像是子类部分。
typedef struct {
unsigned char *buffer;
int buffer_size;
unsigned char *buf_ptr, *buf_end;
void *opaque;
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
int64_t (*seek)(void *opaque, int64_t offset, int whence);
int64_t pos; /**< position in the file of the current buffer */
int must_flush; /**< true if the next seek should flush */
int eof_reached; /**< true if eof reached */
int write_flag; /**< true if open for writing */
int is_streamed;
int max_packet_size;
unsigned long checksum;
unsigned char *checksum_ptr;
unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
int error; ///< contains the error code or 0 if no error happened
int (*read_pause)(void *opaque, int pause);
int64_t (*read_seek)(void *opaque, int stream_index,
int64_t timestamp, int flags);
} ByteIOContext;
ByteIOContext是URLContext和URLProtocol 一个扩展,也是FFMpeg提供给用户的接口,URLContext和URLProtocol对用户是透明,我们所有的操作是通过ByteIOContext现实。这几个struct的相关的关键函数有:
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap)
{
int url_fopen(ByteIOContext **s, const char *filename, int flags)
{
url_open(URLContext **puc, const char *filename, int flags)
{
URLProtocol *up;
//根据filename确定up
url_open_protocol (URLContext **puc, struct URLProtocol *up, const char *filename, int flags)
{
//初始化URLContext对像,并通过 up->url_open()将I/O打开将I/O fd赋值给URLContext的priv_data对像
}
}
url_fdopen(ByteIOContext **s, URLContext *h)
{
//初始化ByteIOContext 对像
}
}
}
我们先看一下音视频播放器的大概结构(个人想法,不保证正确):1、数据源输入(Input)->2、文件格式解析器(Demux)->3、音视频解码(Decoder)->4、颜色空间转换(仅视频)->5、渲染输出(Render Output)。前一篇介绍的几个struct是数据源输入模块里的内容,哪么这一帖所讲的就是第二个模块即文件格式解析器里用到的内容。AVInputFormat、AVOutputFormat与URLProtocol类似,每一种文件类型都有一个AVInputFormat和AVOutputFormat静态对像并通过av_register_input_format和av_register_output_format函数链成一个表,其表头在utils.c:
/** head of registered input format linked list */
AVInputFormat *first_iformat = NULL;
/** head of registered output format linked list */
AVOutputFormat *first_oformat = NULL;
AVInputFormat和AVOutputFormat的定义分别在avformat.h,代码很长,不贴出来浪费空间了。
当程序运行时,AVInputFormat对像的
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap)
{
fmt = av_probe_input_format(pd, 0);//返回该文件的AVInputFormat类型
}
至于AVOutputFormat嘛,下次再说吧,晚安!
AVFormatContext在FFMpeg里是一个非常重要的的结构,是其它输入、输出相关信息的一个容器,需要注意的是其中两个成员:
void *priv_data;//
成员变量指向具体的Stream类型对像,如AVIStream。其
AVCodecContext *actx;//记录具体的编解容器,这个下面会讲
也在这读头文件信息里初始化。
主要相关的函数有
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap)
{
av_open_input_stream(AVFormatContext **ic_ptr,ByteIOContext *pb, const char *filename,AVInputFormat *fmt, AVFormatParameters *ap)
{
fmt.read_header()//调用具体的AVInputFormat的read_header,如avi_read_header
{
//根据文件头信息初始化AVStream *streams及AVStream里的
//void *priv_data和AVCodecContext *actx;成员对像
}
}
}
他们之间的关系和URLProtocol、URLContext之间是一样的,AVCodecContext动态的记录一个解码器的上下文信息,而AVCodec是每个解码器都会拥有一个自己的静态对像,并通过avcodec_register()函数注册成一个链表,表头在utils.c里定义static AVCodec *first_avcodec = NULL;
AVCodecContext的enum CodecID codec_id成员记录者当前数据流的Codec,void *priv_data记录具体Codec所对应的上下文信息对像的指针,如MsrleContext。这三个结合起来现实数据解码的作用。我们可以傻逼的认为AVCodecContext是这个解码模块的容器类,Codec是操作函数集合,类似MsrleContext的就是操作数据对像。
他们之间关系的确立:
每一个解码类型都会有自己的Codec静态对像,Codec的int priv_data_size记录该解码器上下文的结构大小,如MsrleContext。这些都是编译时确定的,程序运行时通过avcodec_register_all()将所有的解码器注册成一个链表。在av_open_input_stream()函数中调用AVInputFormat的read_header()中读文件头信息时,会读出数据流的CodecID,即确定了他的解码器Codec。
typedef struct AVPicture {
uint8_t *data[4];
int linesize[4]; ///< number of bytes per line
} AVPicture;
typedef struct AVFrame
{
uint8_t *data[4]; // 有多重意义,其一用NULL 来判断是否被占用
int linesize[4];
uint8_t *base[4]; // 有多重意义,其一用NULL 来判断是否分配内存
//......其他的数据
} AVFrame;
从定义上可知,AVPicture是AVFrame的一个子集,他们都是数据流在编解过程中用来保存数据缓存的对像,从int av_read_frame(AVFormatContext *s, AVPacket *pkt)函数看,从数据流读出的数据首先是保存在AVPacket里,也可以理解为一个AVPacket最多只包含一个AVFrame,而一个AVFrame可能包含好几个AVPacket,AVPacket是种数据流分包的概念。记录一些音视频相关的属性值,如pts,dts等,定义如下:
typedef struct AVPacket {
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
int duration;
void (*destruct)(struct AVPacket *);
void *priv;
int64_t pos; ///< byte position in stream, -1 if unknown
int64_t convergence_duration;
} AVPacket;