寫在前面
如果對(duì)FFmpeg有需要更多了解的請(qǐng)訂閱我的專題:音視頻專輯
3.1 FFmpeg 文件結(jié)構(gòu)
- libavformat
主要存放 ffmpeg 支持的 各種編解碼器 的實(shí)現(xiàn)及 ffmpeg 編解碼 功能相關(guān)的數(shù)
| 文件 | 簡(jiǎn)要說明 |
|---|---|
| allcodecs.c | 簡(jiǎn)單的注冊(cè)類函數(shù) |
| avcodec.h | 編解碼相關(guān)結(jié)構(gòu)體定義和函數(shù)原型聲明 |
| dsputil.c | 限幅數(shù)組初始化 |
| dsputil.h | 限幅數(shù)組聲明 |
| imgconvert.c | 顏色空間轉(zhuǎn)換相關(guān)函數(shù)實(shí)現(xiàn) |
| imgconvert_template.h | 顏色空間轉(zhuǎn)換相關(guān)結(jié)構(gòu)體定義和函數(shù)聲明 |
| utils_codec.c | 一些解碼相關(guān)的工具類函數(shù)的實(shí)現(xiàn) |
| mpeg4audio.c | mpeg4 音頻編解碼器的函數(shù)實(shí)現(xiàn) |
| mpeg4audio.h | mpeg4 音頻編解碼器的函數(shù)聲明 |
| mpeg4data.h | mpeg4 音視頻編解碼器的公用的函數(shù)聲明及數(shù)據(jù)結(jié)構(gòu)定義 |
| mpeg4video.c | mpeg4 視頻編解碼器的函數(shù)實(shí)現(xiàn) |
| mpeg4video.h | mpeg4 視頻編解碼器的函數(shù)的聲明及先關(guān)數(shù)據(jù)結(jié)構(gòu)的定義 |
| mpeg4videodec.c | mpeg4 視頻解碼器的函數(shù)實(shí)現(xiàn) |
| mpeg4videoenc.c | mpeg4 視頻編碼器的函數(shù)實(shí)現(xiàn) |
- libavformat
本目錄主要存 放 FFMPEG 支持 的各種媒體格 式 MUXER/DEMUXER 和數(shù)據(jù)流協(xié)議 的定義和實(shí)現(xiàn) 文件以及 ffmpeg 解復(fù)用 相關(guān)的數(shù)據(jù)結(jié) 構(gòu)及函數(shù)定義
| 文件 | 簡(jiǎn)要說明 |
|---|---|
| allformats.c | 簡(jiǎn)單注冊(cè)類函數(shù) |
| avformat.h | 文件和媒體格式相關(guān)函數(shù)聲明和數(shù)據(jù)結(jié) 構(gòu)定義 |
| avio.c | 無緩沖 IO 相關(guān)函數(shù)實(shí)現(xiàn) |
| avio.h | 無緩沖 IO 相關(guān)結(jié)構(gòu)定義和函數(shù)聲明 |
| aviobuf.c | 有緩沖數(shù)據(jù) IO 相關(guān)函數(shù)實(shí)現(xiàn) |
| cutils.c | 簡(jiǎn)單的字符串操作函數(shù) |
| utils_format.c | 文件和媒體格式相關(guān)的工具函數(shù)的實(shí)現(xiàn) |
| file.c | 文件 io 相關(guān)函數(shù) |
| …… | 其他相關(guān)媒體流 IO 的函數(shù)和數(shù)據(jù)結(jié)構(gòu)實(shí) 現(xiàn)文件。 如:rtsp、http 等。 |
| avi.c | AVI 格式的相關(guān)函數(shù)定西 |
| avi.h | AVI 格式的相關(guān)函數(shù)聲明及數(shù)據(jù)結(jié)構(gòu)定義 |
| avidec.c | AVI 格式 DEMUXER 相關(guān)函數(shù)定義 |
| avienc.c | AVI 格式 MUXER 相關(guān)函數(shù)定義 |
| …… | 其他媒體格式的 muxer/demuxer 相關(guān)函 數(shù)及數(shù)據(jù)結(jié)構(gòu)定義和聲明文件 |
*libavutil
主要存放 ffmpeg 工具類 函數(shù)的定義
| avutil.h | 簡(jiǎn)單的像素格式宏定義 |
|---|---|
| bswap.h | 簡(jiǎn)單的大小端轉(zhuǎn)換函數(shù)的實(shí)現(xiàn) |
| commom.h | 公共的宏定義和簡(jiǎn)單函數(shù)的實(shí)現(xiàn) |
| mathematics.c | 數(shù)學(xué)運(yùn)算函數(shù)實(shí)現(xiàn) |
| rational.h | 分?jǐn)?shù)相關(guān)表示的函數(shù)實(shí)現(xiàn) |
3.2 I\O 模塊分析
3.2.1 概述
ffmpeg 項(xiàng)目的數(shù)據(jù) IO 部分主要是在 libavformat 庫(kù)中實(shí)現(xiàn), 某些對(duì)于內(nèi)存的操作部分在 libavutil 庫(kù)中。數(shù)據(jù) IO 是基于文件格式(Format)以及文件傳輸協(xié)議(Protocol) 的, 與具體的編解碼標(biāo)準(zhǔn)無關(guān)。 ffmpeg 工程轉(zhuǎn)碼時(shí)數(shù)據(jù) IO 層次關(guān)系如圖所示:
對(duì)于上面的數(shù)據(jù) IO 流程, 具體可以用下面的例子來說明, 我們從一個(gè) http 服務(wù)器 獲取音視頻數(shù)據(jù), 格式是 flv 的, 需要通過轉(zhuǎn)碼后變成 avi 格式, 然后通過 udp 協(xié)議進(jìn) 行發(fā)布。 其過程就如下所示:
- 1、讀入 http 協(xié)議數(shù)據(jù)流, 根據(jù) http 協(xié)議獲取真正的文件數(shù)據(jù)(去除無關(guān)報(bào)文信 息);
- 2、根據(jù) flv 格式對(duì)數(shù)據(jù)進(jìn)行解封裝;
- 3、讀取幀進(jìn)行轉(zhuǎn)碼操作;
- 4、按照目標(biāo)格式 avi 進(jìn)行封裝;
- 5、通過 udp 協(xié)議發(fā)送出去。
3.2.2 相關(guān)數(shù)據(jù)結(jié)構(gòu)介紹
在 libavformat 庫(kù)中與數(shù)據(jù) IO 相關(guān)的數(shù)據(jù)結(jié)構(gòu)主要有 URLProtocol、URLContext、ByteIOContext、AVFormatContext 等, 各結(jié)構(gòu)之間的關(guān)系如圖所示。

1、URLProtocol 結(jié)構(gòu)
表示廣義的輸入文件, 該結(jié)構(gòu)體提供了很多的功能函數(shù), 每一種廣義的輸入文件 (如:file、pipe、tcp、rtp 等等)對(duì)應(yīng)著一個(gè) URLProtocol 結(jié)構(gòu),在 av_register_all() 中將該結(jié)構(gòu)體初始化為一個(gè)鏈表, 表頭為 avio.c 里的 URLProtocol *first_protocol = NULL;保存所有支持的輸入文件協(xié)議, 該結(jié)構(gòu)體的定義如下:
typedef struct URLProtocol
{
const char *name;
int (*url_open)(URLContext *h, const char *url, int flags);
int (*url_read)(URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
struct URLProtocol *next;
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);
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
} URLProtocol;
注意到, URLProtocol 是一個(gè)鏈表結(jié)構(gòu), 這是為了協(xié)議的統(tǒng)一管理, ffmpeg 項(xiàng)目中 將所有的用到的協(xié)議都存放在一個(gè)全局變量 first_protocol 中, 協(xié)議的注冊(cè)是在 av_register_all 中完成的, 新添加單個(gè)協(xié)議可以調(diào)用 av_register_protocol2 函數(shù)實(shí) 現(xiàn)。 而協(xié)議的注冊(cè)就是將具體的協(xié)議對(duì)象添加至 first_protocol 鏈表的末尾。
URLProtocol 在各個(gè)具體的文件協(xié)議中有一個(gè)具體的實(shí)例,如在 file 協(xié)議中定義為:
URLProtocol ff_file_protocol = {
.name = " file" ,
.url_open = file_open,
.url_read = file_read,
.url_write = file_write,
.url_seek = file_seek,
.url_close = file_close,
.url_get_file_handle = file_get_handle, .
.url_check = file_check,
};
2、URLContext 結(jié)構(gòu)
URLContext 提供了與當(dāng)前打開的具體的文件協(xié)議(URL)相關(guān)數(shù)據(jù)的描述, 在該結(jié) 構(gòu)中定義了指定當(dāng)前 URL(即 filename 項(xiàng))所要用到的具體的 URLProtocol, 即:提供 了一個(gè)在 URLprotocol 鏈表中找到具體項(xiàng)的依據(jù), 此外還有一些其它的標(biāo)志性的信息, 如 flags, is_streamed 等。 它可以看成某一種協(xié)議的載體。 其結(jié)構(gòu)定義如下:
typedef struct URLContext
{
const AVClass *av_class; ///< information for av_log(). Set by url_open().
struct URLProtocol *prot;
int flags;
int is_streamed; /**< true if streamed (no seek possible), default = false *
int max_packet_size; void *priv_data;
char *filename; /**< specified URL */
int is_connected;
} URLContext;
那么 ffmpeg 依據(jù)什么信息初始化 URLContext?然后又是如何初始化 URLContext 的呢?
在打開一個(gè) URL 時(shí), 全局函數(shù) ffurl_open 會(huì)根據(jù) filename 的前綴信息來確定 URL 所使用的具體協(xié)議, 并為該協(xié)議分配好資源, 再調(diào)用 ffurl_connect 函數(shù)打開具體協(xié)議, 即調(diào)用協(xié)議的 url_open, 調(diào)用關(guān)系如下:
int av_open_input_file(AVFormatContext **ic_ptr,
const char *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap)
int avformat_open_input(
AVFormatContext **ps ,
const char *filename ,
AVInputFormat *fmt,
AVDictionary **options)
static int init_input(AVFormatContext *s, const char *filename)

淺藍(lán)色部分的函數(shù)完成了 URLContext 函數(shù)的初始化,URLContext 使 ffmpeg 外所暴 露的接口是統(tǒng)一的,而不是對(duì)于不同的協(xié)議用不同的函數(shù),這也是面向?qū)ο笏季S的體現(xiàn)。 在此結(jié)構(gòu)中還有一個(gè)值得說的是 priv_data 項(xiàng), 這是結(jié)構(gòu)的一個(gè)可擴(kuò)展項(xiàng), 具體協(xié)議可 以根據(jù)需要添加相應(yīng)的結(jié)構(gòu), 將指針保存在這就行。
3、AVIOContext 結(jié)構(gòu)
AVIOContext(即:ByteIOContext)是由 URLProtocol 和 URLContext 結(jié)構(gòu)擴(kuò)展而 來,也是 ffmpeg 提供給用戶的接口,它將以上兩種不帶緩沖的讀取文件抽象為帶緩沖的 讀取和寫入, 為用戶提供帶緩沖的讀取和寫入操作。 數(shù)據(jù)結(jié)構(gòu)定義如下:
typedef struct
{
unsigned char *buffer; /**< Start of the buffer. */
int buffer_size; /**< Maximum buffer size */
unsigned char *buf_ptr; /**< Current position in the buffer */
unsigned char *buf_end;
void *opaque; /關(guān)聯(lián) URLContext 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; int must_flush; int eof_reached; /**< true if eof reached */
int write_flag; /**< true if open for writing */
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;
int (*read_pause)(void *opaque, int pause) int64_t (*read_seek)(void *opaque, int stream_index,int64_t timestamp, int flags);
int seekable;
} AVIOContext;
結(jié) 構(gòu) 簡(jiǎn) 單 的 為 用 戶 提 供 讀 寫 容 易 實(shí) 現(xiàn) 的 四 個(gè) 操 作 , read_packet write_packet read_pause read_seek, 極大的方便了文件的讀取, 四個(gè)函數(shù)在加了緩沖機(jī)制后被中轉(zhuǎn) 到, URLContext 指向的實(shí)際的文件協(xié)議讀寫函數(shù)中。
下面給出 0.8 版本中是如何將 AVIOContext 的讀寫操作中轉(zhuǎn)到實(shí)際文件中的。
在 avio_open()函數(shù)中調(diào)用了 ffio_fdopen()函數(shù)完成了對(duì) AVIOContex 的初始 化, 其調(diào)用過程如下:

藍(lán)色部分的函數(shù)調(diào)用完成了對(duì) AVIOContext 的初始化, 在初始化的過程中, 將 AVIOContext 的 read_packet 、 write_packet 、 seek 分 別 初 始 化 為 : ffurl_read ffurl_write ffurl_seek , 而 這 三 個(gè) 函 數(shù) 又 將 具 體 的 讀 寫 操 作 中 轉(zhuǎn) 為 : h->prot->url_read、h->prot->url_write、h->prot->url_seek, 另外兩個(gè)變量初始化 時(shí)也被相應(yīng)的中轉(zhuǎn), 如下:
(*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause;
(*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek;
所以, 可以簡(jiǎn)要的描述為:AVIOContext 的接口口是加了緩沖后的 URLProtocol 的 函數(shù)接口。
在 aviobuf.c 中定義了一系列關(guān)于 ByteIOContext 這個(gè)結(jié)構(gòu)體的函數(shù), 如下
-
put_xxx 系列:
void put_byte(ByteIOContext *s, int b); void put_buffer(ByteIOContext *s, const unsigned char *buf, int size); void put_le64(ByteIOContext *s, uint64_t val); void put_be64(ByteIOContext *s, uint64_t val); void put_le32(ByteIOContext *s, unsigned int val); void put_be32(ByteIOContext *s, unsigned int val); void put_le24(ByteIOContext *s, unsigned int val); void put_be24(ByteIOContext *s, unsigned int val); void put_le16(ByteIOContext *s, unsigned int val); void put_be16(ByteIOContext *s, unsigned int val); void put_tag(ByteIOContext *s, const char *tag);
*get_xxx 系列:
int get_buffer(ByteIOContext *s, unsigned char *buf, int size);
int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size);
int get_byte(ByteIOContext *s);
unsigned int get_le24(ByteIOContext *s);
unsigned int get_le32(ByteIOContext *s);
uint64_t get_le64(ByteIOContext *s);
unsigned int get_le16(ByteIOContext *s);
char *get_strz(ByteIOContext *s, char *buf, int maxlen);
unsigned int get_be16(ByteIOContext *s);
unsigned int get_be24(ByteIOContext *s);
unsigned int get_be32(ByteIOContext *s);
uint64_t get_be64(ByteIOContext *s);
這些 put_xxx 及 get_xxx 函數(shù)是用于從緩沖區(qū) buffer 中寫入或者讀取若干個(gè)字節(jié), 對(duì)于讀寫整型數(shù)據(jù),分別實(shí)現(xiàn)了大端和小端字節(jié)序的版本。而緩沖區(qū) buffer 中的數(shù)據(jù)又 是 從 何 而 來 呢 , 有 一 個(gè) fill_buffer 的 函 數(shù) , 在 fill_buffer 函 數(shù) 中 調(diào) 用 了 ByteIOContext 結(jié)構(gòu)的 read_packet 接口。 在調(diào)用 put_xxx 函數(shù)時(shí), 并沒有直接進(jìn)行真 正寫入操作,而是先緩存起來,直到緩存達(dá)到最大限制或調(diào)用 flush_buffer 函數(shù)對(duì)緩沖 區(qū)進(jìn)行刷新, 才使用 write_packet 函數(shù)進(jìn)行寫入操作。
3.3 Demuxer 和 muxer 模塊分析
3.3.1 概述
ffmpeg 的 demuxer 和 muxer 接口分別在 AVInputFormat 和 AVOutputFormat 兩個(gè)結(jié)
構(gòu)體中實(shí)現(xiàn), 在 av_register_all()函數(shù)中將兩個(gè)結(jié)構(gòu)分別靜態(tài)初始化為兩個(gè)鏈表, 保 存在全局變量:first_iformat 和 first_oformat 兩個(gè)變量中。在 FFmpeg 的文件轉(zhuǎn)換或 者打開過程中, 首先要做的就是根據(jù)傳入文件和傳出文件的后綴名匹配合適的 demuxer 和 muxer, 得到合適的信息后保存在 AVFormatContext 中。
3.3.2 相關(guān)數(shù)據(jù)結(jié)構(gòu)介紹
1、AVInputFormat
該結(jié)構(gòu)被稱為 demuxer, 是音視頻文件的一個(gè)解封裝器, 它的定義如下:
typedef struct AVInputFormat
{
const char *name;
const char *long_name;
int priv_data_size; //具體文件容器格式對(duì)應(yīng)的 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;
對(duì)于不同的文件格式要實(shí)現(xiàn)相應(yīng)的函數(shù)接口, 這樣每一種格式都有一個(gè)對(duì)應(yīng)的 demuxer, 所有的 demuxer 都保存在全局變量 first_iformat 中。 紅色表示提供的接口。
2、AVOutputFormat
該結(jié)構(gòu)與 AVInputFormat 類似也是在編譯時(shí)靜態(tài)初始化, 組織為一個(gè)鏈表結(jié)構(gòu), 提 供了多個(gè) muxer 的函數(shù)接口。
int (*write_header)(struct AVFormatContext *);
int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*write_trailer)(struct AVFormatContext *);
對(duì)于不同的文件格式要實(shí)現(xiàn)相應(yīng)的函數(shù)接口, 這樣每一種格式都有一個(gè)對(duì)應(yīng)的 muxer, 所有的 muxer 都保存在全局變量 first_oformat 中。
3、AVFormatContext
該結(jié)構(gòu)表示與程序當(dāng)前運(yùn)行的文件容器格式使用的上下文, 著重于所有文件容器共 有的屬性,在運(yùn)行時(shí)動(dòng)態(tài)的確定其值,是 AVInputFormat 和 AVOutputFormat 的載體,但 同一個(gè)結(jié)構(gòu)對(duì)象只能使 AVInputFormat 和 AVOutputFormat 中的某一個(gè)有效。每一個(gè)輸入 和輸出文件, 都在
static AVFormatContext *output_files[MAX_FILES]
和
static AVFormatContext *input_files[MAX_FILES];
定義的指針數(shù)組全局變量中有對(duì)應(yīng)的實(shí)體。 對(duì)于輸入和輸出, 因?yàn)楣灿玫氖峭粋€(gè)結(jié)構(gòu) 體, 所以需要分別對(duì)該結(jié)構(gòu)中如下定義的 iformat 或 oformat 成員賦值。 在轉(zhuǎn)碼時(shí)讀寫 數(shù)據(jù)是通過 AVFormatContext 結(jié)構(gòu)進(jìn)行的。 定義如下:
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; //本次打開的文件容器中流的數(shù)量
AVStream **streams; //每個(gè)流的相關(guān)描述
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 是如何被初始化的呢?文件的 格式是如何被探測(cè)到的呢?
首先我們來探討:
struct AVInputFormat *iformat; //指向具體的 demuxer
struct AVOutputFormat *oformat; //指向具體的 muxer
void *priv_data; //具體文件容器格式的 Context 如:avicontext
三個(gè)成員的初始化。
在 avformat_open_input() 函 數(shù) 中 調(diào) 用 了 init_input() 函 數(shù) , 然 后 用 調(diào) 用 了 av_probe_input_format()函數(shù)實(shí)現(xiàn)了對(duì) AVFormatContext 的初始化。其調(diào)用關(guān)系如下:
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) 函數(shù)用途是根據(jù)傳入的 probe data 數(shù)據(jù), 依次調(diào)用每個(gè) demuxer 的 read_probe 接口, 來進(jìn)行該 demuxer 是否和傳入的文件內(nèi)容匹配的判斷。 與 demuxer 的匹配不同, muxer 的匹配是調(diào)用 guess_format 函數(shù), 根據(jù) main( ) 函數(shù)的 argv 里的輸出文件后綴名來進(jìn) 行的。 至此完成了前三個(gè)重要成員的初始化, 具體的做法就不在深入分析。
下面分別給出 av_read_frame 函數(shù)以及 av_write_frame 函數(shù)的基本流程。
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
->av_read_frame_internel
->av_read_packet
->iformat->read_packet(在實(shí)現(xiàn)中會(huì)丟棄多余信息)
->av_get_packet
->get_xxx
int av_write_frame(AVFormatContext *s, AVPacket *pkt);
->oformat->write_packet
->put_xxx
由上可見, 對(duì) AVFormatContext 的讀寫操作最終是通過 ByteIOContext 來實(shí)現(xiàn)的, 這樣, AVFormatContext 與 URLContext 就由 ByteIOContext 結(jié)構(gòu)聯(lián)系到一起了。 在 AVFormat 結(jié)構(gòu)體中有一個(gè) packet 的緩沖區(qū) raw_packet_buffer, 是 AVPackList 的指針 類型, av_read_packet 函數(shù)將讀到的包添加至 raw_packet_buffer 鏈表末尾。
3.4 Decoder/Encoder 模塊
3.4.1 概述
編解碼模塊主要包含的數(shù)據(jù)結(jié)構(gòu)為:AVCodec、AVCodecContext 每一個(gè)解碼類型都 會(huì)有自己的 Codec 靜態(tài)對(duì)像, Codec 的 int priv_data_size 記錄該解碼器上下文的結(jié)構(gòu) 大 小 , 如 MsrleContext 。 這 些 都 是 編 譯 時(shí) 確 定 的 , 程 序 運(yùn) 行 時(shí) 通 過 avcodec_register_all()將所有的解碼器注冊(cè)成一個(gè)鏈表。在 av_open_input_stream() 函數(shù)中調(diào)用 AVInputFormat 的 read_header()中讀文件頭信息時(shí), 會(huì)讀出數(shù)據(jù)流的 CodecID, 即確定了他的解碼器 Codec。
在 main()函數(shù)中除了解析傳入?yún)?shù)并初始化 demuxer 與 muxer 的 parse_options( ) 函數(shù)以外, 其他的功能都是在 av_encode( )函數(shù)里完成的。 在 libavcodec\utils.c 中 有 如 下 二 個(gè) 函 數(shù) :AVCodec *avcodec_find_encoder(enum CodecID id) 和 AVCodec *avcodec_find_decoder(enum CodecID id)他們的功能就是根據(jù)傳入的 CodecID, 找到 匹配的 encoder 和 decoder。在 av_encode( )函數(shù)的開頭,首先初始化各個(gè) AVInputStream 和 AVOutputStream,然后分別調(diào)用上述二個(gè)函數(shù),并將匹配上的 encoder 與 decoder 分 別保存在:
AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec
與
AVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec
變量。
3.4.2 相關(guān)數(shù)據(jù)結(jié)構(gòu)的初始化
AVCodecContext 結(jié)構(gòu)
AVCodecContext 保存 AVCodec 指針和與 codec 相關(guān)數(shù)據(jù),如 video 的 width、height, audio 的 sample rate 等。
AVCodecContext 中的 codec_type, codec_id 二個(gè)變量對(duì)于 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(…… ) 函數(shù)中, 匹配到正確 的 AVInputFormat demuxer 后,通過 av_open_input_stream( )函數(shù)中調(diào)用 AVInputFormat 的 read_header 接口來執(zhí)行 flvdec.c 中的 flv_read_header( )函數(shù)。flv_read_header( ) 函數(shù)內(nèi), 根據(jù)文件頭中的數(shù)據(jù), 創(chuàng)建相應(yīng)的視頻或音頻 AVStream, 并設(shè)置 AVStream 中 AVCodecContext 的正確的 codec_type 值。codec_id 值是在解碼過程。flv_read_packet( ) 函數(shù)執(zhí)行時(shí)根據(jù)每一個(gè) packet 頭中的數(shù)據(jù)來設(shè)置的。
以 avidec 為例 有如下初始化,我們主要知道的就是 code_id 和 code_type 該字段關(guān) 聯(lián)具體的解碼器, 和解碼類型(音視頻或 subtitle)
if (st->codec->stream_codec_tag == AV_RL32(" Axan" ))
{
st->codec->codec_id = CODEC_ID_XAN_DPCM;
st->codec->codec_tag = 0;
}
if (amv_file_format)
{
st->codec->codec_id = CODEC_ID_ADPCM_IMA_AMV;
ast->dshow_block_align = 0;
}
break;
case AVMEDIA_TYPE_SUBTITLE:
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
st->request_probe= 1;
break;
default:
st->codec->codec_type = AVMEDIA_TYPE_DATA;
st->codec->codec_id= CODEC_ID_NONE;
st->codec->codec_tag= 0;
avio_skip(pb, size);
3.5 其他重要數(shù)據(jù)結(jié)構(gòu)的初始化
3.5.1 AVStream
AVStream 結(jié)構(gòu)保存與數(shù)據(jù)流相關(guān)的編解碼器,數(shù)據(jù)段等信息。比較重要的有如下二個(gè)成員:
AVCodecContext *codec; /**< codec context */
void *priv_data;
其中 codec 指針保存的就是上節(jié)所述的 encoder 或 decoder 結(jié)構(gòu)。 priv_data 指針 保存的是和具體編解碼流相關(guān)的數(shù)據(jù),如下代碼所示,在 ASF 的解碼過程中,priv_data 保存的就是 ASFStream 結(jié)構(gòu)的數(shù)據(jù)。
AVStream *st;
ASFStream *asf_st;
… …
st->priv_data = asf_st;
3.5.2 AVInputStream/ AVOutputStream
根據(jù)輸入和輸出流的不同, 前述的 AVStream 結(jié)構(gòu)都是封裝在 AVInputStream 和 AVOutputStream 結(jié)構(gòu)中, 在 av_encode( )函數(shù)中使用。 AVInputStream 中還保存的有與 時(shí)間有關(guān)的信息。 AVOutputStream 中還保存有與音視頻同步等相關(guān)的信息。
3.5.3 AVPacket
AVPacket 結(jié)構(gòu)定義如下, 其是用于保存讀取的 packet 數(shù)據(jù)。
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
void (*destruct)(struct AVPacket *);
void *priv;
int64_t pos; ///< byte position in stream, -1 if unknown
} AVPacket;
在 av_encode()函數(shù)中, 調(diào)用 AVInputFormat 的
(*read_packet)(struct AVFormatContext *, AVPacket *pkt)
接口, 讀取輸入文 件的一幀數(shù)據(jù)保存在當(dāng)前輸入 AVFormatContext 的 AVPacket 成員中。
寫在后面
如果對(duì)FFmpeg有需要更多了解的請(qǐng)訂閱我的專題:音視頻專輯