FFMPEG完美入門資料---003---FFmpeg 架構(gòu)

寫在前面

如果對(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)系如圖所示:
ffmpeg 轉(zhuǎn)碼數(shù)據(jù) IO 流程

對(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)系如圖所示。


libavformat 庫(kù)中 IO 相關(guān)數(shù)據(jù)結(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)訂閱我的專題:音視頻專輯

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容