FFMpeg分析详细分析
? ? ? URLProtocol,URLContext和ByteIOContext是FFMpeg操作文件(即I/O,包括網(wǎng)絡(luò)數(shù)據(jù)流)的結(jié)構(gòu),這幾個(gè)結(jié)構(gòu)現(xiàn)實(shí)的功能類似于C++的多態(tài)繼承吧,C++的多態(tài)是通過(guò)子類繼承實(shí)現(xiàn),而FFMpeg的“多態(tài)”是通過(guò)靜態(tài)對(duì)像現(xiàn)實(shí)。這部分的代碼非常值得C程序借鑒,我是說(shuō),如果你要在C里實(shí)現(xiàn)類似C++多態(tài)性的功能;比如當(dāng)你要區(qū)分你老婆和情人之間的不同功能時(shí)。
? ? ?好了,先來(lái)看一下這三個(gè)struct的定義吧
typedef struct URLProtocol {
?
? ? const char *name; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //Rotocol名稱
? ? int (*url_open)(URLContext *h, const char *url, int flags); ? ? ?//open函數(shù)指針對(duì)象,以下類似
? ? 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; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//指向下一個(gè)URLProtocol,具體說(shuō)明見(jiàn)備注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類型都用這個(gè)變量串成一個(gè)鏈表,表頭為avio.c里的URLProtocol *first_protocol = NULL;
每個(gè)文件類似都有自己的一個(gè)URLProtocol靜態(tài)對(duì)象,如libavformat/file.c里
?
URLProtocol file_protocol = {
? ? "file",
? ? file_open,
? ? file_read,
? ? file_write,
? ? file_seek,
? ? file_close,
? ? .url_get_file_handle = file_get_handle,
};
再通過(guò)av_register_protocol()將他們鏈接成鏈表。在FFMpeg中所有的URLProtocol對(duì)像值都在編譯時(shí)確定。
?
?
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類型,在運(yùn)行時(shí)通過(guò)文件URL確定,如是file類型時(shí)就是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對(duì)象值在編譯時(shí)確定,URLContext對(duì)象值是在運(yùn)行過(guò)程中根據(jù)輸入的I/O類型動(dòng)態(tài)確定的。這一動(dòng)一靜組合起到了C++的多態(tài)繼承一樣的作用。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?一個(gè)擴(kuò)展,也是FFMpeg提供給用戶的接口,URLContext和URLProtocol對(duì)用戶是透明,我們所有的操作是通過(guò)ByteIOContext現(xiàn)實(shí)。這幾個(gè)struct的相關(guān)的關(guān)鍵函數(shù)有:
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;
? ? ?? ???? ? ?//根據(jù)filename確定up
? ? ?? ???? ? ?url_open_protocol (URLContext **puc, struct URLProtocol *up,?const char *filename, int flags)
? ? ??? ??? ? ?{
? ? ??? ??? ? ? ? ? //初始化URLContext對(duì)像,并通過(guò) up->url_open()將I/O打開(kāi)將I/O fd賦值給URLContext的priv_data對(duì)像
? ? ??? ??? ? ?}
? ??? ???}
? ??? ???url_fdopen(ByteIOContext **s, URLContext *h)
? ??? ???{
? ??? ??? ? ? ?//初始化ByteIOContext 對(duì)像
? ? ?? ??}
? ??}
}
我們先看一下音視頻播放器的大概結(jié)構(gòu)(個(gè)人想法,不保證正確):1、數(shù)據(jù)源輸入(Input)->2、文件格式解析器(Demux)->3、音視頻解碼(Decoder)->4、顏色空間轉(zhuǎn)換(僅視頻)->5、渲染輸出(Render Output)。前一篇介紹的幾個(gè)struct是數(shù)據(jù)源輸入模塊里的內(nèi)容,哪么這一帖所講的就是第二個(gè)模塊即文件格式解析器里用到的內(nèi)容。? ? ??AVInputFormat、AVOutputFormat與URLProtocol類似,每一種文件類型都有一個(gè)AVInputFormat和AVOutputFormat靜態(tài)對(duì)像并通過(guò)av_register_input_format和av_register_output_format函數(shù)鏈成一個(gè)表,其表頭在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,代碼很長(zhǎng),不貼出來(lái)浪費(fèi)空間了。
當(dāng)程序運(yùn)行時(shí),AVInputFormat對(duì)像的
?
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嘛,下次再說(shuō)吧,晚安!
?? AVFormatContext在FFMpeg里是一個(gè)非常重要的的結(jié)構(gòu),是其它輸入、輸出相關(guān)信息的一個(gè)容器,需要注意的是其中兩個(gè)成員: struct AVInputFormat *iformat;//數(shù)據(jù)輸入格式 ?struct AVOutputFormat *oformat;//數(shù)據(jù)輸出格式 這兩個(gè)成員不能同時(shí)賦值,即AVFormatContext不能同時(shí)做為輸入、輸出格式的容器。AVFormatContext和AVIContext、FLVContext等XXXContext之間像前面講的URLContext和URLProtocol的關(guān)系一樣,是一種"多態(tài)"關(guān)系,即AVFormatContext對(duì)像記錄著運(yùn)行時(shí)大家共有的信息,而各個(gè)XXXContext記錄自己文件格式的信息,如AVIContext、FLVContext等。AVInputFormat->priv_data_size記錄相對(duì)應(yīng)的XXXContext的大小,該值大小在編譯時(shí)靜態(tài)確定。AVFormatContext的void *priv_data記錄XXXContext指針。 AVFormatContext對(duì)像的初始化主要在AVInputFormat的read_header函數(shù)中進(jìn)行,read_header是個(gè)函數(shù)指針,指向 具體的文件類型的read_header,如flv_read_header(),avi_read_header()等,AVFormatContext、AVInputFormat和XXXContext組成一起共同完成數(shù)據(jù)輸入模塊,可以出來(lái)粗魯?shù)恼J(rèn)為,AVFormatContext是一個(gè)類容器,AVInputFormat是這個(gè)類的操作函數(shù)集合,XXXContext代表該類的私有數(shù)據(jù)對(duì)像。AVFormatContext還有個(gè)重要的成員?AVStream *streams[MAX_STREAMS];也是在read_header里初始化,這個(gè)等會(huì)兒再講。 ?前幾篇說(shuō)的都還是數(shù)據(jù)源文件格式解析部分,哪么解析完后呢,讀出的數(shù)據(jù)流保存在哪呢?正是現(xiàn)在講的AVStream對(duì)像,在AVInputFormat的read_header中初始化AVFormatContext對(duì)像時(shí),他會(huì)解析出該輸入文件有哪些類型的數(shù)據(jù)流,并初始化AVFormatContext的AVStream *streams[MAX_STREAMS];一個(gè)AVStream代表一個(gè)流對(duì)像,如音頻流、視頻流,nb_streams記錄流對(duì)像個(gè)數(shù)。主版本號(hào)大于53時(shí)MAX_STREAMS為100,小于53為20。AVStream也是個(gè)容器,其void *priv_data;//
成員變量指向具體的Stream類型對(duì)像,如AVIStream。其
AVCodecContext *actx;//記錄具體的編解容器,這個(gè)下面會(huì)講
也在這讀頭文件信息里初始化。
主要相關(guān)的函數(shù)有
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()//調(diào)用具體的AVInputFormat的read_header,如avi_read_header
? ?? ????{
? ? ? ? ? ? ? ?//根據(jù)文件頭信息初始化AVStream *streams及AVStream里的
? ? ? ? ? ? ? ?//void *priv_data和AVCodecContext *actx;成員對(duì)像
? ??? ???}? ? ? ??
? ??}
}
static AVCodec *first_avcodec = NULL;
AVCodecContext的enum CodecID codec_id成員記錄者當(dāng)前數(shù)據(jù)流的Codec,void *priv_data記錄具體Codec所對(duì)應(yīng)的上下文信息對(duì)像的指針,如MsrleContext。這三個(gè)結(jié)合起來(lái)現(xiàn)實(shí)數(shù)據(jù)解碼的作用。我們可以傻逼的認(rèn)為AVCodecContext是這個(gè)解碼模塊的容器類,Codec是操作函數(shù)集合,類似MsrleContext的就是操作數(shù)據(jù)對(duì)像。
他們之間關(guān)系的確立:
每一個(gè)解碼類型都會(huì)有自己的Codec靜態(tài)對(duì)像,Codec的int priv_data_size記錄該解碼器上下文的結(jié)構(gòu)大小,如MsrleContext。這些都是編譯時(shí)確定的,程序運(yùn)行時(shí)通過(guò)avcodec_register_all()將所有的解碼器注冊(cè)成一個(gè)鏈表。在av_open_input_stream()函數(shù)中調(diào)用AVInputFormat的read_header()中讀文件頭信息時(shí),會(huì)讀出數(shù)據(jù)流的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 來(lái)判斷是否被占用
? ?int linesize[4];
? ?uint8_t *base[4]; // 有多重意義,其一用NULL 來(lái)判斷是否分配內(nèi)存
? ?//......其他的數(shù)據(jù)
} AVFrame;
從定義上可知,AVPicture是AVFrame的一個(gè)子集,他們都是數(shù)據(jù)流在編解過(guò)程中用來(lái)保存數(shù)據(jù)緩存的對(duì)像,從int av_read_frame(AVFormatContext *s, AVPacket *pkt)函數(shù)看,從數(shù)據(jù)流讀出的數(shù)據(jù)首先是保存在AVPacket里,也可以理解為一個(gè)AVPacket最多只包含一個(gè)AVFrame,而一個(gè)AVFrame可能包含好幾個(gè)AVPacket,AVPacket是種數(shù)據(jù)流分包的概念。記錄一些音視頻相關(guān)的屬性值,如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;
總結(jié)
以上是生活随笔為你收集整理的FFMpeg分析详细分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: DC保存至BMP图像
- 下一篇: avs 中国音视频标准DRA 中音频标准