欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

ffmpeg架構(gòu)和解碼流程分析

系統(tǒng) 1744 0

?

轉(zhuǎn)

一,ffmpeg架構(gòu)

1. 簡(jiǎn)介

FFmpeg 是一個(gè)集錄制、轉(zhuǎn)換、音 / 視頻編碼解碼功能為一體的完整的開源解決方案。 FFmpeg

開發(fā)是基于 Linux 操作系統(tǒng),但是可以在大多數(shù)操作系統(tǒng)中編譯和使用。 FFmpeg 支持 MPEG

DivX MPEG4 AC3 DV FLV 40 多種編碼, AVI MPEG OGG Matroska ASF 90 多種解碼 .

TCPMP, VLC, MPlayer 等開源播放器都用到了 FFmpeg

FFmpeg 主目錄下主要有 libavcodec libavformat libavutil 等子目錄。其中 libavcodec

于存放各個(gè) encode/decode 模塊, libavformat 用于存放 muxer/demuxer 模塊, libavutil 用于

存放內(nèi)存操作等輔助性模塊。

flash movie flv 文件格式為例, muxer/demuxer flvenc.c flvdec.c 文件在

libavformat 目錄下, encode/decode mpegvideo.c h263de.c libavcodec 目錄下。

?

2. muxer/demuxer encoder/decoder 定義與初始化

muxer/demuxer encoder/decoder FFmpeg 中的實(shí)現(xiàn)代碼里,有許多相同的地方,而二者最

大的差別是 muxer demuxer 分別是不同的結(jié)構(gòu) AVOutputFormat AVInputFormat ,而 encoder

decoder 都是用的 AVCodec 結(jié)構(gòu)。

?

muxer/demuxer encoder/decoder FFmpeg 中相同的地方有:

??? 二者都是在 main() 開始的 av_register_all() 函數(shù)內(nèi)初始化的

??? 二者都是以鏈表的形式保存在全局變量中的

??????? muxer/demuxer 是分別保存在全局變量 AVOutputFormat *first_oformat

??????? AVInputFormat *first_iformat 中的。

??????? encoder/decoder 都是保存在全局變量 AVCodec *first_avcodec 中的。

??? 二者都用函數(shù)指針的方式作為開放的公共接口

???

demuxer 開放的接口有:

??? int (*read_probe)(AVProbeData *);

??? int (*read_header)(struct AVFormatContext *, AVFormatParameters *ap);

??? int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);

??? int (*read_close)(struct AVFormatContext *);

??? int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);

???

muxer 開放的接口有:

??? int (*write_header)(struct AVFormatContext *);

??? int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);

??? int (*write_trailer)(struct AVFormatContext *);

?

encoder/decoder 的接口是一樣的,只不過(guò)二者分別只實(shí)現(xiàn) encoder decoder 函數(shù):

??? int (*init)(AVCodecContext *);

??? int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);

??? int (*close)(AVCodecContext *);

??? int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, uint8_t *buf, int buf_size);

?

仍以 flv 文件為例來(lái)說(shuō)明 muxer/demuxer 的初始化。

libavformat\allformats.c 文件的 av_register_all(void) 函數(shù)中,通過(guò)執(zhí)行

REGISTER_MUXDEMUX(FLV, flv);

將支持 flv 格式的 flv_muxer flv_demuxer 變量分別注冊(cè)到全局變量 first_oformat first_iformat 鏈表的最后位置。

其中 flv_muxer libavformat\flvenc.c 中定義如下:

AVOutputFormat flv_muxer = {

??? "flv",

??? "flv format",

??? "video/x-flv",

??? "flv",

??? sizeof(FLVContext),

#ifdef CONFIG_LIBMP3LAME

??? CODEC_ID_MP3,

#else // CONFIG_LIBMP3LAME

??? CODEC_ID_NONE,

??? CODEC_ID_FLV1,

??? flv_write_header,

??? flv_write_packet,

??? flv_write_trailer,

??? .codec_tag= (const AVCodecTag*[]){flv_video_codec_ids, flv_audio_codec_ids, 0},

}

AVOutputFormat 結(jié)構(gòu)的定義如下:

typedef struct AVOutputFormat {

??? const char *name;

??? const char *long_name;

??? const char *mime_type;

??? const char *extensions;

???

??? int priv_data_size;

???

??? enum CodecID audio_codec;

??? enum CodecID video_codec;

??? int (*write_header)(struct AVFormatContext *);

??? int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);

??? int (*write_trailer)(struct AVFormatContext *);

???

??? int flags;

???

??? int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);

??? int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush);

?

???

??? const struct AVCodecTag **codec_tag;

???

??? struct AVOutputFormat *next;

} AVOutputFormat;

AVOutputFormat 結(jié)構(gòu)的定義可知, flv_muxer 變量初始化的第一、二個(gè)成員分別為該 muxer

的名稱與長(zhǎng)名稱,第三、第四個(gè)成員為所對(duì)應(yīng) MIMIE Type 和后綴名,第五個(gè)成員是所對(duì)應(yīng)的

私有結(jié)構(gòu)的大小,第六、第七個(gè)成員為所對(duì)應(yīng)的音頻編碼和視頻編碼類型 ID ,接下來(lái)就是三

個(gè)重要的接口函數(shù),該 muxer 的功能也就是通過(guò)調(diào)用這三個(gè)接口實(shí)現(xiàn)的。

?

flv_demuxer libavformat\flvdec.c 中定義如下 , flv_muxer 類似,在這兒主要也是設(shè)置

5 個(gè)接口函數(shù),其中 flv_probe 接口用途是測(cè)試傳入的數(shù)據(jù)段是否是符合當(dāng)前文件格式,這

個(gè)接口在匹配當(dāng)前 demuxer 時(shí)會(huì)用到。

AVInputFormat flv_demuxer = {

??? "flv",

??? "flv format",

??? 0,

??? flv_probe,

??? flv_read_header,

??? flv_read_packet,

??? flv_read_close,

??? flv_read_seek,

??? .extensions = "flv",

??? .value = CODEC_ID_FLV1,

};

?

在上述 av_register_all(void) 函數(shù)中通過(guò)執(zhí)行 libavcodec\allcodecs.c 文件里的

avcodec_register_all(void) 函數(shù)來(lái)初始化全部的 encoder/decoder

?

因?yàn)椴皇敲糠N編碼方式都支持 encode decode ,所以有以下三種注冊(cè)方式:

#define REGISTER_ENCODER(X,x) \

??? if(ENABLE_##X##_ENCODER) register_avcodec(&x##_encoder)

#define REGISTER_DECODER(X,x) \

??? if(ENABLE_##X##_DECODER) register_avcodec(&x##_decoder)

#define REGISTER_ENCDEC(X,x) REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)

?

如支持 flv flv_encoder flv_decoder 變量就分別是在 libavcodec\mpegvideo.c libavcodec\h263de.c 中創(chuàng)建的。

3. 當(dāng)前 muxer/demuxer 的匹配

FFmpeg 的文件轉(zhuǎn)換過(guò)程中,首先要做的就是根據(jù)傳入文件和傳出文件的后綴名 [FIXME] 匹配

合適的 demuxer muxer 。匹配上的 demuxer muxer 都保存在如下所示,定義在 ffmpeg.c 里的

全局變量 file_iformat file_oformat 中:

? ? static AVInputFormat *file_iformat;

? ? static AVOutputFormat *file_oformat;

3.1 demuxer 匹配

libavformat\utils.c 中的 static AVInputFormat *av_probe_input_format2(

AVProbeData *pd, int is_opened, int *score_max) 函數(shù)用途是根據(jù)傳入的 probe data 數(shù)據(jù)

,依次調(diào)用每個(gè) demuxer read_probe 接口,來(lái)進(jìn)行該 demuxer 是否和傳入的文件內(nèi)容匹配的

判斷。其調(diào)用順序如下:

void parse_options(int argc, char **argv, const OptionDef *options,

void (* parse_arg_function)(const char *));

static void opt_input_file(const char *filename)

int av_open_input_file(…… )

AVInputFormat *av_probe_input_format(AVProbeData *pd,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int is_opened)

static AVInputFormat *av_probe_input_format2(……)

opt_input_file 函數(shù)是在保存在 const OptionDef options[] 數(shù)組中,用于

void parse_options(int argc, char **argv, const OptionDef *options) 中解析 argv 里的

“-i” 參數(shù),也就是輸入文件名時(shí)調(diào)用的。

3.2 muxer 匹配

demuxer 的匹配不同, muxer 的匹配是調(diào)用 guess_format 函數(shù),根據(jù) main() 函數(shù)的 argv 里的

輸出文件后綴名來(lái)進(jìn)行的。

void parse_options(int argc, char **argv, const OptionDef *options,

? ? ? ? ? void (* parse_arg_function)(const char *));

void parse_arg_file(const char *filename)

static void opt_output_file(const char *filename)

AVOutputFormat *guess_format(const char *short_name,

? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *filename,

? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *mime_type)

3.3 當(dāng)前 encoder/decoder 的匹配

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 變量。

4. 其他主要數(shù)據(jù)結(jié)構(gòu)

4.1 AVFormatContext

AVFormatContext FFMpeg 格式轉(zhuǎn)換過(guò)程中實(shí)現(xiàn)輸入和輸出功能、保存相關(guān)數(shù)據(jù)的主要結(jié)構(gòu)。

每一個(gè)輸入和輸出文件,都在如下定義的指針數(shù)組全局變量中有對(duì)應(yīng)的實(shí)體。

? ? static AVFormatContext *output_files[MAX_FILES];

? ? static AVFormatContext *input_files[MAX_FILES];

對(duì)于輸入和輸出,因?yàn)楣灿玫氖峭粋€(gè)結(jié)構(gòu)體,所以需要分別對(duì)該結(jié)構(gòu)中如下定義的 iformat

oformat 成員賦值。

? ? struct AVInputFormat *iformat;

? ? struct AVOutputFormat *oformat;

對(duì)一個(gè) AVFormatContext 來(lái)說(shuō),這二個(gè)成員不能同時(shí)有值,即一個(gè) AVFormatContext 不能同時(shí)

含有 demuxer muxer 。在 main( ) 函數(shù)開頭的 parse_options( ) 函數(shù)中找到了匹配的 muxer

demuxer 之后,根據(jù)傳入的 argv 參數(shù),初始化每個(gè)輸入和輸出的 AVFormatContext 結(jié)構(gòu),并保

存在相應(yīng)的 output_files input_files 指針數(shù)組中。在 av_encode( ) 函數(shù)中, output_files

input_files 是作為函數(shù)參數(shù)傳入后,在其他地方就沒(méi)有用到了。

4.2 AVCodecContext

保存 AVCodec 指針和與 codec 相關(guān)數(shù)據(jù),如 video width height audio sample rate 等。

AVCodecContext 中的 codec_type codec_id 二個(gè)變量對(duì)于 encoder/decoder 的匹配來(lái)說(shuō),最為

重要。

? ? enum CodecType codec_type;? ?

? ? enum CodecID codec_id;? ? ? ?

如上所示, codec_type 保存的是 CODEC_TYPE_VIDEO CODEC_TYPE_AUDIO 等媒體類型,

codec_id 保存的是 CODEC_ID_FLV1 CODEC_ID_VP6F 等編碼方式。

以支持 flv 格式為例,在前述的 av_open_input_file(…… ) 函數(shù)中,匹配到正確的

AVInputFormat demuxer 后,通過(guò) av_open_input_stream( ) 函數(shù)中調(diào)用 AVInputFormat

read_header 接口來(lái)執(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 值是在解碼過(guò)程中 flv_read_packet( )

數(shù)執(zhí)行時(shí)根據(jù)每一個(gè) packet 頭中的數(shù)據(jù)來(lái)設(shè)置的。

4.3 AVStream

AVStream 結(jié)構(gòu)保存與數(shù)據(jù)流相關(guān)的編解碼器,數(shù)據(jù)段等信息。比較重要的有如下二個(gè)成員:

? ? AVCodecContext *codec;

? ? void *priv_data;

其中 codec 指針保存的就是上節(jié)所述的 encoder decoder 結(jié)構(gòu)。 priv_data 指針保存的是和具

體編解碼流相關(guān)的數(shù)據(jù),如下代碼所示,在 ASF 的解碼過(guò)程中, priv_data 保存的就是

ASFStream 結(jié)構(gòu)的數(shù)據(jù)。

? ? AVStream *st;

? ? ASFStream *asf_st;?

? ? … …

? ? st->priv_data = asf_st;

4.4 AVInputStream/ AVOutputStream

根據(jù)輸入和輸出流的不同,前述的 AVStream 結(jié)構(gòu)都是封裝在 AVInputStream AVOutputStream

結(jié)構(gòu)中,在 av_encode( ) 函數(shù)中使用。 AVInputStream 中還保存的有與時(shí)間有關(guān)的信息。

AVOutputStream 中還保存有與音視頻同步等相關(guān)的信息。

4.5 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 (0 if not available)

? ? 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 成員中。

---------------------------------------------------------------------

FFMPEG 是目前被應(yīng)用最廣泛的編解碼軟件庫(kù),支持多種流行的編解碼器,它是 C 語(yǔ)言實(shí)現(xiàn)的,不僅被集成到各種 PC 軟件,也經(jīng)常被移植到多種嵌入式設(shè)備中。使用面向?qū)ο蟮霓k法來(lái)設(shè)想這樣一個(gè)編解碼庫(kù),首先讓人想到的是構(gòu)造各種編解碼器的類,然后對(duì)于它們的抽象基類確定運(yùn)行數(shù)據(jù)流的規(guī)則,根據(jù)算法轉(zhuǎn)換輸入輸出對(duì)象。

在實(shí)際的代碼,將這些編解碼器分成 encoder/decoder muxer/demuxer device 三種對(duì)象,分別對(duì)應(yīng)于編解碼,輸入輸 出格式和設(shè)備。在 main 函數(shù)的開始,就是初始化這三類對(duì)象。在 avcodec_register_all 中,很多編解碼器被注冊(cè),包括視頻的 H.264 解碼器和 X264 編碼器等,

REGISTER_DECODER (H264, h264);

REGISTER_ENCODER (LIBX264, libx264);

找到相關(guān)的宏代碼如下

#define REGISTER_ENCODER(X,x) { \

????????? extern AVCodec x##_encoder; \

????????? if(CONFIG_##X##_ENCODER)? avcodec_register(&x##_encoder); }

#define REGISTER_DECODER(X,x) { \

????????? extern AVCodec x##_decoder; \

????????? if(CONFIG_##X##_DECODER)? avcodec_register(&x##_decoder); }

這樣就實(shí)際在代碼中根據(jù) CONFIG_##X##_ENCODER 這樣的編譯選項(xiàng)來(lái)注冊(cè) libx264_encoder h264_decoder ,注冊(cè)的過(guò)程發(fā)生在 avcodec_register(AVCodec *codec) 函數(shù)中,實(shí)際上就是向全局鏈表 first_avcodec 中加入 libx264_encoder h264_decoder 特定的編解碼 器,輸入?yún)?shù) AVCodec 是一個(gè)結(jié)構(gòu)體,可以理解為編解碼器的基類,其中不僅包含了名稱, id 等屬性,而且包含了如下函數(shù)指針,讓每個(gè)具體的編解碼器擴(kuò)展類實(shí)現(xiàn)。

??? int (*init)(AVCodecContext *);

??? int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);

??? int (*close)(AVCodecContext *);

??? int (*decode)(AVCodecContext *, void *outdata, int *outdata_size,

????????????????? const uint8_t *buf, int buf_size);

?? ?void (*flush)(AVCodecContext *);

繼續(xù)追蹤 libx264 ,也就是 X264 的靜態(tài)編碼庫(kù),它在 FFMPEG 編譯的時(shí)候被引入作為 H.264 編碼器。在 libx264.c 中有如下代碼

AVCodec libx264_encoder = {

??? .name = "libx264",

??? .type = CODEC_TYPE_VIDEO,

??? .id = CODEC_ID_H264,

??? .priv_data_size = sizeof(X264Context),

??? .init = X264_init,

??? .encode = X264_frame,

??? .close = X264_close,

??? .capabilities = CODEC_CAP_DELAY,

??? .pix_fmts = (enum PixelFormat[]) { PIX_FMT_YUV420P, PIX_FMT_NONE },

??? .long_name = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),

};

這里具體對(duì)來(lái)自 AVCodec 得屬性和方法賦值。其中

??? .init = X264_init,

??? .encode = X264_frame,

??? .close = X264_close,

將函數(shù)指針指向了具體函數(shù),這三個(gè)函數(shù)將使用 libx264 靜態(tài)庫(kù)中提供的 API ,也就是 X264 的主要接口函數(shù)進(jìn)行具體實(shí)現(xiàn)。 pix_fmts 定義了所支持的輸入格式,這里 4 2 0

PIX_FMT_YUV420P,?? ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)

上面看到的 X264Context 封裝了 X264 所需要的上下文管理數(shù)據(jù),

typedef struct X264Context {

??? x264_param_t params;

??? x264_t *enc;

??? x264_picture_t pic;

??? AVFrame out_pic;

} X264Context;

它 屬于結(jié)構(gòu)體 AVCodecContext void *priv_data 變量,定義了每種編解碼器私有的上下文屬性, AVCodecContext 也類似上下文基類一樣,還提供其他表示屏幕解析率、量化范圍等的上下文屬性和 rtp_callback 等函數(shù)指針供編解碼使用。

回到 main 函數(shù),可以看到完成了各類編解碼器,輸入輸出格式和設(shè)備注冊(cè)以后,將進(jìn)行上下文初始化和編解碼參數(shù)讀入,然后調(diào)用 av_encode ()函數(shù)進(jìn)行具體的編解碼工作。根據(jù)該函數(shù)的注釋一路查看其過(guò)程:

1. 輸入輸出流初始化。

2. 根據(jù)輸入輸出流確定需要的編解碼器,并初始化。

3. 寫輸出文件的各部分

重點(diǎn)關(guān)注一下 step2 3 ,看看怎么利用前面分析的編解碼器基類來(lái)實(shí)現(xiàn)多態(tài)。大概查看一下這段代碼的關(guān)系,發(fā)現(xiàn)在 FFMPEG 里,可以用類圖來(lái)表示大概的編解碼器組合。

http://1824.img.pp.sohu.com.cn/images/blog/2009/7/22/15/29/1234e7d516dg215.jpg

可以參考【 3 】來(lái)了解這些結(jié)構(gòu)的含義(見附錄)。在這里會(huì)調(diào)用一系列來(lái)自 utils.c 的函數(shù),這里的 avcodec_open ()函數(shù),在打開編解碼器都會(huì)調(diào)用到,它將運(yùn)行如下代碼:

??? avctx->codec = codec;

??? avctx->codec_id = codec->id;

??? avctx->frame_number = 0;

??? if(avctx->codec->init){

??????? ret = avctx->codec->init(avctx);

進(jìn)行具體適配的編解碼器初始化,而這里的 avctx->codec->init(avctx) 就是調(diào)用 AVCodec 中函數(shù)指針定義的具體初始化函數(shù),例如 X264_init

avcodec_encode_video ()和 avcodec_encode_audio ()被 output_packet ()調(diào)用進(jìn)行音視頻編碼,將 同樣利用函數(shù)指針 avctx->codec->encode ()調(diào)用適配編碼器的編碼函數(shù),如 X264_frame 進(jìn)行具體工作。

從上面的分析,我們可以看到 FFMPEG 怎么利用面向?qū)ο髞?lái)抽象編解碼器行為,通過(guò)組合和繼承關(guān)系具體化每個(gè)編解碼器實(shí)體。設(shè)想要在 FFMPEG 中加入新的解碼器 H265 ,要做的事情如下:

1. config 編譯配置中加入 CONFIG_H265_DECODER

2. 利用宏注冊(cè) H265 解碼器

3. 定義 AVCodec 265_decoder 變量,初始化屬性和函數(shù)指針

4. 利用解碼器 API 具體化 265_decoder init 等函數(shù)指針

完成以上步驟,就可以把新的解碼器放入 FFMPEG ,外部的匹配和運(yùn)行規(guī)則由基類的多態(tài)實(shí)現(xiàn)了。

4. X264 架構(gòu)分析

X264 是一款從 2004 年有法國(guó)大學(xué)生發(fā)起的開源 H.264 編碼器,對(duì) PC 進(jìn)行匯編級(jí)代碼優(yōu)化,舍棄了片組和多參考幀等性能效率比不高的功能來(lái)提高編碼效率,它被 FFMPEG 作為引入的 .264 編碼庫(kù),也被移植到很多 DSP 嵌入平臺(tái)。前面第三節(jié)已經(jīng)對(duì) FFMPEG 中的 X264 進(jìn)行舉例分析,這里將繼續(xù)結(jié)合 X264 框架加深相關(guān)內(nèi)容的了解。

查看代碼前,還是思考一下對(duì)于一款具體的編碼器,怎么面向?qū)ο蠓治瞿兀繉?duì)熵編碼部分對(duì)不同算法的抽象,還有幀內(nèi)或幀間編碼各種估計(jì)算法的抽象,都可以作為類來(lái)構(gòu)建。

X264 中,我們看到的對(duì)外 API 和上下文變量都聲明在 X264.h 中, API 函數(shù)中,關(guān)于輔助功能的函數(shù)在 common.c 中定義

void x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height );

void x264_picture_clean( x264_picture_t *pic );

int x264_nal_encode( void *, int *, int b_annexeb, x264_nal_t *nal );

而編碼功能函數(shù)定義在 encoder.c

x264_t *x264_encoder_open?? ( x264_param_t * );

int???? x264_encoder_reconfig( x264_t *, x264_param_t * );

int???? x264_encoder_headers( x264_t *, x264_nal_t **, int * );

int???? x264_encoder_encode ( x264_t *, x264_nal_t **, int *, x264_picture_t *, x264_picture_t * );

void??? x264_encoder_close? ( x264_t * );

x264.c 文件中,有程序的 main 函數(shù),可以看作做 API 使用的例子,它也是通過(guò)調(diào)用 X264.h 中的 API 和上下文變量來(lái)實(shí)現(xiàn)實(shí)際功能。

X264 最重要的記錄上下文數(shù)據(jù)的結(jié)構(gòu)體 x264_t 定義在 common.h 中,它包含了從線程控制變量到具體的 SPS PPS 、量化矩陣、 cabac 上下文等所有的 H.264 編碼相關(guān)變量。其中包含如下的結(jié)構(gòu)體

??? x264_predict_t????? predict_16x16[4+3];

??? x264_predict_t????? predict_8x8c[4+3];

??? x264_predict8x8_t?? predict_8x8[9+3];

??? x264_predict_t????? predict_4x4[9+3];

??? x264_predict_8x8_filter_t predict_8x8_filter;

??? x264_pixel_function_t pixf;

??? x264_mc_functions_t?? mc;

??? x264_dct_function_t?? dctf;

??? x264_zigzag_function_t zigzagf;

??? x264_quant_function_t quantf;

??? x264_deblock_function_t loopf;

跟蹤查看可以看到它們或是一個(gè)函數(shù)指針,或是由函數(shù)指針組成的結(jié)構(gòu),這樣的用法很想面向?qū)ο笾械? interface 接口聲明。這些函數(shù)指針將在 x264_encoder_open ()函數(shù)中被初始化,這里的初始化首先根據(jù) CPU 的不同提供不同的函數(shù)實(shí)現(xiàn)代碼段,很多與可能是匯編實(shí)現(xiàn),以提高代碼運(yùn)行效率。其次把功能相似的函數(shù)集中管理,例如類似 intra16 4 種和 intra4 的九種預(yù)測(cè)函數(shù)都被用函數(shù)指針數(shù)組管理起來(lái)。

x264_encoder_encode ()是負(fù)責(zé)編碼的主要函數(shù),而其內(nèi)包含的 x264_slice_write ()負(fù)責(zé)片層一下的具體編碼,包括了幀內(nèi)和幀間宏塊編碼。在這里, cabac cavlc 的行為是根據(jù) h->param.b_cabac 來(lái)區(qū)別的,分別運(yùn)行 x264_macroblock_write_cabac ()和 x264_macroblock_write_cavlc ()來(lái)寫碼流,在這一部分,功能函數(shù)按文件定義歸類,基本按照編碼流程圖運(yùn)行,看起來(lái)更像面向過(guò)程的寫法,在已經(jīng)初始化了具體的函數(shù)指針,程序就一直按編碼過(guò)程的邏輯實(shí)現(xiàn)。如果從整體架構(gòu)來(lái)看, x264 利用這種類似接口的形式實(shí)現(xiàn)了弱耦合和可重用, 利用 x264_t 這個(gè)貫穿始終的上下文,實(shí)現(xiàn)信息封裝和多態(tài)。

本文大概分析了 FFMPEG/X264 的代碼架構(gòu),重點(diǎn)探討用 C 語(yǔ)言來(lái)實(shí)現(xiàn)面向?qū)ο缶幋a,雖不至于強(qiáng)行向 C++ 靠攏,但是也各有實(shí)現(xiàn)特色,保證實(shí)用性。值得規(guī)劃 C 語(yǔ)言軟件項(xiàng)目所借鑒。 ?

?

【參考文獻(xiàn)】

1.“ 用例子說(shuō)明面向?qū)ο蠛兔嫦蜻^(guò)程的區(qū)別

2.?liyuming1978 liyuming1978 的專欄

3. “FFMpeg 框架代碼閱讀

?

Using libavformat and libavcodec

Martin B?hme (boehme@inb.uni-luebeckREMOVETHIS.de)

February 18, 2004

Update (January 23 2009): By now, these articles are quite out of date... unfortunately, I haven't found the time to update them, but thankfully, others have jumped in. Stephen Dranger has a more recent tutorial , ryanfb of cryptosystem.org has an updated version of the code, and David Hoerl has a more recent update .

Update (July 22 2004): I discovered that the code I originally presented contained a memory leak (av_free_packet() wasn't being called). My apologies - I've updated the demo program and the code in the article to eliminate the leak.

Update (July 21 2004): There's a new prerelease of ffmpeg (0.4.9-pre1). I describe the changes to the libavformat / libavcodec API in this article .

The libavformat and libavcodec libraries that come with ffmpeg are a great way of accessing a large variety of video file formats. Unfortunately, there is no real documentation on using these libraries in your own programs (at least I couldn't find any), and the example programs aren't really very helpful either.

This situation meant that, when I used libavformat/libavcodec on a recent project, it took quite a lot of experimentation to find out how to use them. Here's what I learned - hopefully I'll be able to save others from having to go through the same trial-and-error process. There's also a small demo program that you can download. The code I'll present works with libavformat/libavcodec as included in version 0.4.8 of ffmpeg (the most recent version as I'm writing this). If you find that later versions break the code, please let me know.

In this document, I'll only cover how to read video streams from a file; audio streams work pretty much the same way, but I haven't actually used them, so I can't present any example code.

In case you're wondering why there are two libraries, libavformat and libavcodec: Many video file formats (AVI being a prime example) don't actually specify which codec(s) should be used to encode audio and video data; they merely define how an audio and a video stream (or, potentially, several audio/video streams) should be combined into a single file. This is why sometimes, when you open an AVI file, you get only sound, but no picture - because the right video codec isn't installed on your system. Thus, libavformat deals with parsing video files and separating the streams contained in them, and libavcodec deals with decoding raw audio and video streams.

Opening a Video File

First things first - let's look at how to open a video file and get at the streams contained in it. The first thing we need to do is to initialize libavformat/libavcodec:

av_register_all();

This registers all available file formats and codecs with the library so they will be used automatically when a file with the corresponding format/codec is opened. Note that you only need to call av_register_all() once, so it's probably best to do this somewhere in your startup code. If you like, it's possible to register only certain individual file formats and codecs, but there's usually no reason why you would have to do that.

Next off, opening the file:

AVFormatContext *pFormatCtx;

const char ????? *filename="myvideo.mpg";

// Open video file

if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)

??? handle_error(); // Couldn't open file

The last three parameters specify the file format, buffer size and format parameters; by simply specifying NULL or 0 we ask libavformat to auto-detect the format and use a default buffer size. Replace handle_error() with appropriate error handling code for your application.

Next, we need to retrieve information about the streams contained in the file:

// Retrieve stream information

if(av_find_stream_info(pFormatCtx)<0)

??? handle_error(); // Couldn't find stream information

This fills the streams field of the AVFormatContext with valid information. As a debugging aid, we'll dump this information onto standard error, but of course you don't have to do this in a production application:

dump_format(pFormatCtx, 0, filename, false);

As mentioned in the introduction, we'll handle only video streams, not audio streams. To make things nice and easy, we simply use the first video stream we find:

int ??????????? i, videoStream;

AVCodecContext *pCodecCtx;

// Find the first video stream

videoStream=-1;

for(i=0; i<pFormatCtx->nb_streams; i++)

??? if(pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_VIDEO)

??? {

??????? videoStream=i;

??????? break;

??? }

if(videoStream==-1)

??? handle_error(); // Didn't find a video stream

// Get a pointer to the codec context for the video stream

pCodecCtx=&pFormatCtx->streams[videoStream]->codec;

OK, so now we've got a pointer to the so-called codec context for our video stream, but we still have to find the actual codec and open it:

AVCodec *pCodec;

// Find the decoder for the video stream

pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

if(pCodec==NULL)

??? handle_error(); // Codec not found

// Inform the codec that we can handle truncated bitstreams -- i.e.,

// bitstreams where frame boundaries can fall in the middle of packets

if(pCodec->capabilities & CODEC_CAP_TRUNCATED)

??? pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;

// Open codec

if(avcodec_open(pCodecCtx, pCodec)<0)

??? handle_error(); // Could not open codec

(So what's up with those "truncated bitstreams"? Well, as we'll see in a moment, the data in a video stream is split up into packets. Since the amount of data per video frame can vary, the boundary between two video frames need not coincide with a packet boundary. Here, we're telling the codec that we can handle this situation.)

One important piece of information that is stored in the AVCodecContext structure is the frame rate of the video. To allow for non-integer frame rates (like NTSC's 29.97 fps), the rate is stored as a fraction, with the numerator in pCodecCtx->frame_rate and the denominator in pCodecCtx->frame_rate_base. While testing the library with different video files, I noticed that some codecs (notably ASF) seem to fill these fields incorrectly (frame_rate_base contains 1 instead of 1000). The following hack fixes this:

// Hack to correct wrong frame rates that seem to be generated by some

// codecs

if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)

??? pCodecCtx->frame_rate_base=1000;

Note that it shouldn't be a problem to leave this fix in place even if the bug is corrected some day - it's unlikely that a video would have a frame rate of more than 1000 fps.

One more thing left to do: Allocate a video frame to store the decoded images in:

AVFrame *pFrame;

pFrame=avcodec_alloc_frame();

That's it! Now let's start decoding some video.

Decoding Video Frames

As I've already mentioned, a video file can contain several audio and video streams, and each of those streams is split up into packets of a particular size. Our job is to read these packets one by one using libavformat, filter out all those that aren't part of the video stream we're interested in, and hand them on to libavcodec for decoding. In doing this, we'll have to take care of the fact that the boundary between two frames can occur in the middle of a packet.

Sound complicated? Lucikly, we can encapsulate this whole process in a routine that simply returns the next video frame:

bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx,

??? int videoStream, AVFrame *pFrame)

{

??? static AVPacket packet;

??? static int ????? bytesRemaining=0;

??? static uint8_t ? *rawData;

??? static bool ???? fFirstTime=true;

??? int ???????????? bytesDecoded;

??? int ???????????? frameFinished;

??? // First time we're called, set packet.data to NULL to indicate it

??? // doesn't have to be freed

??? if(fFirstTime)

??? {

??????? fFirstTime=false;

??????? packet.data=NULL;

??? }

??? // Decode packets until we have decoded a complete frame

??? while(true)

??? {

??????? // Work on the current packet until we have decoded all of it

??????? while(bytesRemaining > 0)

??????? {

??????????? // Decode the next chunk of data

??????????? bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame,

??????????????? &frameFinished, rawData, bytesRemaining);

??????????? // Was there an error?

??????????? if(bytesDecoded < 0)

??????????? {

??????????????? fprintf(stderr, "Error while decoding frame\n");

??????????????? return false;

??????????? }

??????????? bytesRemaining-=bytesDecoded;

??????????? rawData+=bytesDecoded;

??????????? // Did we finish the current frame? Then we can return

??????????? if(frameFinished)

??????????????? return true;

??????? }

??????? // Read the next packet, skipping all packets that aren't for this

??????? // stream

??????? do

??????? {

??????????? // Free old packet

??????????? if(packet.data!=NULL)

??????????????? av_free_packet(&packet);

??????????? // Read new packet

??????????? if(av_read_packet(pFormatCtx, &packet)<0)

??????????????? goto loop_exit;

??????? } while(packet.stream_index!=videoStream);

??????? bytesRemaining=packet.size;

??????? rawData=packet.data;

??? }

loop_exit:

??? // Decode the rest of the last frame

??? bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,

??????? rawData, bytesRemaining);

??? // Free last packet

??? if(packet.data!=NULL)

??????? av_free_packet(&packet);

??? return frameFinished!=0;

}

Now, all we have to do is sit in a loop, calling GetNextFrame() until it returns false. Just one more thing to take care of: Most codecs return images in YUV 420 format (one luminance and two chrominance channels, with the chrominance channels samples at half the spatial resolution of the luminance channel). Depending on what you want to do with the video data, you may want to convert this to RGB. (Note, though, that this is not necessary if all you want to do is display the video data; take a look at the X11 Xvideo extension, which does YUV-to-RGB and scaling in hardware.) Fortunately, libavcodec provides a conversion routine called img_convert, which does conversion between YUV and RGB as well as a variety of other image formats. The loop that decodes the video thus becomes:

while(GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame))

{

??? img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame,

??????? pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

??? // Process the video frame (save to disk etc.)

??? DoSomethingWithTheImage(pFrameRGB);

}

The RGB image pFrameRGB (of type AVFrame *) is allocated like this:

AVFrame *pFrameRGB;

int ???? numBytes;

uint8_t *buffer;

// Allocate an AVFrame structure

pFrameRGB=avcodec_alloc_frame();

if(pFrameRGB==NULL)

??? handle_error();

// Determine required buffer size and allocate buffer

numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,

??? pCodecCtx->height);

buffer=new uint8_t[numBytes];

// Assign appropriate parts of buffer to image planes in pFrameRGB

avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,

??? pCodecCtx->width, pCodecCtx->height);

Cleaning up

OK, we've read and processed our video, now all that's left for us to do is clean up after ourselves:

// Free the RGB image

delete [] buffer;

av_free(pFrameRGB);

// Free the YUV frame

av_free(pFrame);

// Close the codec

avcodec_close(pCodecCtx);

// Close the video file

av_close_input_file(pFormatCtx);

Done!

Sample Code

A sample app that wraps all of this code up in compilable form is here . If you have any additional comments, please contact me at boehme@inb.uni-luebeckREMOVETHIS.de. Standard disclaimer: I assume no liability for the correct functioning of the code and techniques presented in this article.

?

?

?

?

?

二,解碼流程

FFMpeg的解碼流程

1. 從基礎(chǔ)談起
先給出幾個(gè)概念,以在后面的分析中方便理解
Container:在音視頻中的容器,一般指的是一種特定的文件格式,里面指明了所包含的
??? 音視頻,字幕等相關(guān)信息
Stream:這個(gè)詞有些微妙,很多地方都用到,比如TCP,SVR4系統(tǒng)等,其實(shí)在音視頻,你
??? 可以理解為單純的音頻數(shù)據(jù)或者視頻數(shù)據(jù)等
Frames:這個(gè)概念不是很好明確的表示,指的是Stream中的一個(gè)數(shù)據(jù)單元,要真正對(duì)這
??? 個(gè)概念有所理解,可能需要看一些音視頻編碼解碼的理論知識(shí)
Packet:是Stream的raw數(shù)據(jù)
Codec:Coded + Decoded
其實(shí)這些概念在在FFmpeg中都有很好的體現(xiàn),我們?cè)诤罄m(xù)分析中會(huì)慢慢看到

2.解碼的基本流程
我很懶,于是還是選擇了從<An ffmpeg and SDL Tutorial>中的流程概述:

10 OPEN video_stream FROM video.avi
20 READ packet FROM video_stream INTO frame
30 IF frame NOT COMPLETE GOTO 20
40 DO SOMETHING WITH frame
50 GOTO 20

這就是解碼的全過(guò)程,一眼看去,是不是感覺(jué)不過(guò)如此:),不過(guò),事情有深有淺,從淺
到深,然后從深回到淺可能才是一個(gè)有意思的過(guò)程,我們的故事,就從這里開始,展開
來(lái)講。

3.例子代碼
在<An ffmpeg and SDL Tutorial 1>中,給出了一個(gè)陽(yáng)春版的解碼器,我們來(lái)仔細(xì)看看
陽(yáng)春后面的故事,為了方便講述,我先貼出代碼:

#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

#include <stdio.h>

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;

// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
??? return;

// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);

// Write pixel data
for(y=0; y<height; y++)
??? fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

// Close file
fclose(pFile);
}

int main(int argc, char *argv[]) {
AVFormatContext *pFormatCtx;
int???????????? i, videoStream;
AVCodecContext *pCodecCtx;
AVCodec???????? *pCodec;
AVFrame???????? *pFrame;?
AVFrame???????? *pFrameRGB;
AVPacket??????? packet;
int???????????? frameFinished;
int???????????? numBytes;
uint8_t???????? *buffer;

if(argc < 2) {
??? printf("Please provide a movie file\n");
??? return -1;
}
// Register all formats and codecs
########################################
[1]
########################################
av_register_all();

// Open video file
########################################
[2]
########################################
if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
??? return -1; // Couldn't open file

// Retrieve stream information
########################################
[3]
########################################
if(av_find_stream_info(pFormatCtx)<0)
??? return -1; // Couldn't find stream information

// Dump information about file onto standard error
dump_format(pFormatCtx, 0, argv[1], 0);

// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
??? if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
????? videoStream=i;
????? break;
??? }
if(videoStream==-1)
??? return -1; // Didn't find a video stream

// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;

// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
??? fprintf(stderr, "Unsupported codec!\n");
??? return -1; // Codec not found
}
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
??? return -1; // Could not open codec

// Allocate video frame
pFrame=avcodec_alloc_frame();

// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
??? return -1;
????
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
????????????????? pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
???????? pCodecCtx->width, pCodecCtx->height);

// Read frames and save first five frames to disk
########################################
[4]
########################################
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
??? // Is this a packet from the video stream?
??? if(packet.stream_index==videoStream) {
????? // Decode video frame
????? avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,?
?????????????? packet.data, packet.size);
??????
????? // Did we get a video frame?
????? if(frameFinished) {
??? // Convert the image from its native format to RGB
??? img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,?
??????????????????? (AVPicture*)pFrame, pCodecCtx->pix_fmt,?
??????????????????? pCodecCtx->width,?
??????????????????? pCodecCtx->height);
????
??? // Save the frame to disk
??? if(++i<=5)
????? SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,?
??????????? i);
????? }
??? }
????
??? // Free the packet that was allocated by av_read_frame
??? av_free_packet(&packet);
}

// Free the RGB image
av_free(buffer);
av_free(pFrameRGB);

// Free the YUV frame
av_free(pFrame);

// Close the codec
avcodec_close(pCodecCtx);

// Close the video file
av_close_input_file(pFormatCtx);

return 0;
}

代碼注釋得很清楚,沒(méi)什么過(guò)多需要講解的,關(guān)于其中的什么YUV420,RGB,PPM等格式
,如果不理解,麻煩還是google一下,也可以參考:http://barrypopy.cublog.cn/里面
的相關(guān)文章

其實(shí)這部分代碼,很好了Demo了怎么樣去抓屏功能的實(shí)現(xiàn),但我們得去看看魔術(shù)師在后
臺(tái)的一些手法,而不只是簡(jiǎn)單的享受其表演。

4.背后的故事
真正的難度,其實(shí)就是上面的[1],[2],[3],[4],其他部分,都是數(shù)據(jù)結(jié)構(gòu)之間的轉(zhuǎn)換,
如果你認(rèn)真看代碼的話,不難理解其他部分。

[1]:沒(méi)什么太多好說(shuō)的,如果不明白,看我轉(zhuǎn)載的關(guān)于FFmepg框架的文章

[2]:先說(shuō)說(shuō)里面的AVFormatContext *pFormatCtx結(jié)構(gòu),字面意思理解AVFormatContext
就是關(guān)于AVFormat(其實(shí)就是我們上面說(shuō)的Container格式)的所處的Context(場(chǎng)景),自
然是保存Container信息的總控結(jié)構(gòu)了,后面你也可以看到,基本上所有的信息,都可
以從它出發(fā)而獲取到
????
我們來(lái)看看av_open_input_file()都做了些什么:
[libavformat/utils.c]
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
?????????????????????? AVInputFormat *fmt,
?????????????????????? int buf_size,
?????????????????????? AVFormatParameters *ap)
{
??? ......
??? if (!fmt) {
???????
??????? fmt = av_probe_input_format(pd, 0);
??? }

?? ......
??? err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);
?? ......
}

這樣看來(lái),只是做了兩件事情:
1). 偵測(cè)容器文件格式
2). 從容器文件獲取Stream的信息

這兩件事情,實(shí)際上就是調(diào)用特定文件的demuxer以分離Stream的過(guò)程:

具體流程如下:

av_open_input_file
??? |
??? +---->av_probe_input_format從first_iformat中遍歷注冊(cè)的所有demuxer以?
??? |???? 調(diào)用相應(yīng)的probe函數(shù)
??? |
??? +---->av_open_input_stream調(diào)用指定demuxer的read_header函數(shù)以獲取相關(guān)
????????? 流的信息ic->iformat->read_header

如果反過(guò)來(lái)再參考我轉(zhuǎn)貼的關(guān)于ffmpeg框架的文章,是否清楚一些了呢:)

[3]:簡(jiǎn)單從AVFormatContext獲取Stream的信息,沒(méi)什么好多說(shuō)的

[4]:先簡(jiǎn)單說(shuō)一些ffmpeg方面的東西,從理論角度說(shuō)過(guò)來(lái),Packet可以包含frame的部
分?jǐn)?shù)據(jù),但ffmpeg為了實(shí)現(xiàn)上的方便,使得對(duì)于視頻來(lái)說(shuō),每個(gè)Packet至少包含一
frame,對(duì)于音頻也是相應(yīng)處理,這是實(shí)現(xiàn)方面的考慮,而非協(xié)議要求.
因此,在上面的代碼實(shí)際上是這樣的:
??? 從文件中讀取packet,從Packet中解碼相應(yīng)的frame;
??? 從幀中解碼;
??? if(解碼幀完成)
??????? do something();

我們來(lái)看看如何獲取Packet,又如何從Packet中解碼frame的。

av_read_frame
??? |
??? +---->av_read_frame_internal
??????? |
??????? +---->av_parser_parse調(diào)用的是指定解碼器的s->parser->parser_parse函數(shù)以從raw packet中重構(gòu)frame

avcodec_decode_video
??? |
??? +---->avctx->codec->decode調(diào)用指定Codec的解碼函數(shù)
????
因此,從上面的過(guò)程可以看到,實(shí)際上分為了兩部分:

一部分是解復(fù)用(demuxer),然后是解碼(decode)

使用的分別是:
av_open_input_file()??????????? ---->解復(fù)用

av_read_frame()??????????? |
?????????????????????????? |??? ---->解碼????
avcodec_decode_video()???? |

5.后面該做些什么
結(jié)合這部分和轉(zhuǎn)貼的ffmepg框架的文章,應(yīng)該可以基本打通解碼的流程了,后面的問(wèn)題則是針對(duì)具體容器格式和具體編碼解碼器的分析

?

?

ffmpeg架構(gòu)和解碼流程分析


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 精品国产久| 欧美13videosex性极品 | 一区二区三区国产 | 欧美成人在线免费观看 | 91网站在线免费观看 | 免费久久网站 | 国产小视频精品 | 毛片a片| 天堂va在线高清一区 | 精品国产一区二区在线 | 欧美日韩高清不卡一区二区三区 | 91精品国产综合久久福利软件 | 日本a在线 | 欧美高清成人 | 精品久久久久久久久久久 | 一级毛片视频免费 | 国产成人综合日韩精品婷婷九月 | 天天操天天碰 | a黄视频| 亚洲日日干 | 久久久婷婷一区二区三区不卡 | 久久精品桃花综合 | 国产欧美日韩精品一区 | 欧美一级高清毛片aaa | 日本成人在线看 | 毛片免费观看视频 | 一个人看aaaa免费中文 | 国产女人成人精品视频 | 欧美视频网站在线观看 | 丰满岳妇乱一区二区三区 | 久久伊人中文字幕有码 | 亚洲欧美一区二区三区在线 | 欧美一级α片 | 99久热re在线精品99 6热视频 | 色678黄网站全部免费 | 欧美片网站免费 | 色综合区 | 成人一区二区丝袜美腿 | 欧美成人二区 | 在线天堂中文在线资源网 | 奶子吧naiziba.cc免费午夜片在线观看 |