乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      對(duì)X264/FFMPEG架構(gòu)探討---感覺不錯(cuò)

       hylvsly 2011-07-27

       

      3. FFMPEG架構(gòu)分析

      FFMPEG是目前被應(yīng)用最廣泛的編解碼軟件庫(kù),支持多種流行的編解碼器,它是C語言實(shí)現(xiàn)的,不僅被集成到各種PC軟件,也經(jīng)常被移植到多種嵌入式設(shè)備中。使用面向?qū)ο蟮霓k法來設(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)來注冊(cè)libx264_encoder和h264_decoder,注冊(cè)的過程發(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ì)來自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ù)的注釋一路查看其過程:

      1. 輸入輸出流初始化。
      2. 根據(jù)輸入輸出流確定需要的編解碼器,并初始化。
      3. 寫輸出文件的各部分

       

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

      可以參考【3】來了解這些結(jié)構(gòu)的含義(見附錄)。在這里會(huì)調(diào)用一系列來自u(píng)tils.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ū)ο髞沓橄缶幗獯a器行為,通過組合和繼承關(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)化,舍棄了片組和多參考幀等性能效率比不高的功能來提高編碼效率,它被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ì)算法的抽象,都可以作為類來構(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使用的例子,它也是通過調(diào)用X264.h中的API和上下文變量來實(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ū)ο笾械膇nterface接口聲明。這些函數(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ù)組管理起來。

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

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

       

      【參考文獻(xiàn)】

      1.“用例子說明面向?qū)ο蠛兔嫦蜻^程的區(qū)別
      2. liyuming1978,“liyuming1978的專欄
      3. “FFMpeg框架代碼閱讀”

       

       

      附錄:節(jié)選自【3】

      3. 當(dāng)前muxer/demuxer的匹配
      在FFmpeg的文件轉(zhuǎn)換過程中,首先要做的就是根據(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接口,來進(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里的
      輸出文件后綴名來進(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)換過程中實(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來說,這二個(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ù)傳入后,在其他地方就沒有用到了。

      4.2 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è)置的。

      4.3 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;

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

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多