1. 程式人生 > >ffdshow 原始碼分析 8: 視訊解碼器類(TvideoCodecDec)

ffdshow 原始碼分析 8: 視訊解碼器類(TvideoCodecDec)

=====================================================

ffdshow原始碼分析系列文章列表:

=====================================================

前面兩篇文章介紹了ffdshow中libavcodec的封裝類Tlibavcodec,以及libavcodec的解碼器類TvideoCodecLibavcodec:

其中libavcodec的解碼器類TvideoCodecLibavcodec通過呼叫Tlibavcodec中的方法實現了libavcodec的dll中方法的呼叫;而它繼承了TvideoCodecDec,本文正是要分析它繼承的這個類。

TvideoCodecDec是所有視訊解碼器共有的父類。可以看一下它的繼承關係:


可見,除了TvideoCodecLibavcodec繼承了TvideoCodecDec之外,還有好幾個類繼承了TvideoCodecDec,比如說:TvideoCodecLibmpeg2,TvideoCodecXviD4等等…。突然來了興趣,我們可以看一下其他的解碼器類的定義是什麼樣的。

TvideoCodecLibmpeg2定義如下:

/* 
 *雷霄驊 
 *[email protected] 
 *中國傳媒大學/數字電視技術 
 */ 
#ifndef _TVIDEOCODECLIBMPEG2_H_
#define _TVIDEOCODECLIBMPEG2_H_

#include "TvideoCodec.h"
#include "libmpeg2/include/mpeg2.h"

class Tdll;
struct Textradata;
class TccDecoder;
//libmpeg2解碼器
class TvideoCodecLibmpeg2 : public TvideoCodecDec
{
private:
    Tdll *dll;
    uint32_t (*mpeg2_set_accel)(uint32_t accel);
    mpeg2dec_t* (*mpeg2_init)(void);
    const mpeg2_info_t* (*mpeg2_info)(mpeg2dec_t *mpeg2dec);
    mpeg2_state_t (*mpeg2_parse)(mpeg2dec_t *mpeg2dec);
    void (*mpeg2_buffer)(mpeg2dec_t *mpeg2dec, const uint8_t *start, const uint8_t *end);
    void (*mpeg2_close)(mpeg2dec_t *mpeg2dec);
    void (*mpeg2_reset)(mpeg2dec_t *mpeg2dec, int full_reset);
    void (*mpeg2_set_rtStart)(mpeg2dec_t *mpeg2dec, int64_t rtStart);
    int (*mpeg2_guess_aspect)(const mpeg2_sequence_t * sequence, unsigned int * pixel_width, unsigned int * pixel_height);

    mpeg2dec_t *mpeg2dec;
    const mpeg2_info_t *info;
    bool wait4Iframe;
    int sequenceFlag;
    REFERENCE_TIME avgTimePerFrame;
    TffPict oldpict;
    Textradata *extradata;
    TccDecoder *ccDecoder;
    Tbuffer *buffer;
    uint32_t oldflags;
    bool m_fFilm;
    int SetDeinterlaceMethod(void);

    void init(void);
    HRESULT decompressI(const unsigned char *src, size_t srcLen, IMediaSample *pIn);

protected:
    virtual bool beginDecompress(TffPictBase &pict, FOURCC infcc, const CMediaType &mt, int sourceFlags);

public:
    TvideoCodecLibmpeg2(IffdshowBase *Ideci, IdecVideoSink *Isink);
    virtual ~TvideoCodecLibmpeg2();

    static const char_t *dllname;
    virtual int getType(void) const {
        return IDFF_MOVIE_LIBMPEG2;
    }
    virtual int caps(void) const {
        return CAPS::VIS_QUANTS;
    }

    virtual void end(void);
    virtual HRESULT decompress(const unsigned char *src, size_t srcLen, IMediaSample *pIn);
    virtual bool onSeek(REFERENCE_TIME segmentStart);
    virtual HRESULT BeginFlush();
};

#endif

TvideoCodecXviD4定義如下:

/* 
 *雷霄驊 
 *[email protected] 
 *中國傳媒大學/數字電視技術 
 */ 
#ifndef _TVIDEOCODECXVID4_H_
#define _TVIDEOCODECXVID4_H_

#include "TvideoCodec.h"

class Tdll;
struct Textradata;
//xvid解碼器
class TvideoCodecXviD4 : public TvideoCodecDec
{
private:
    void create(void);
    Tdll *dll;
public:
    TvideoCodecXviD4(IffdshowBase *Ideci, IdecVideoSink *IsinkD);
    virtual ~TvideoCodecXviD4();
    int (*xvid_global)(void *handle, int opt, void *param1, void *param2);
    int (*xvid_decore)(void *handle, int opt, void *param1, void *param2);
    int (*xvid_plugin_single)(void *handle, int opt, void *param1, void *param2);
    int (*xvid_plugin_lumimasking)(void *handle, int opt, void *param1, void *param2);
    static const char_t *dllname;
private:
    void *enchandle, *dechandle;
    int psnr;
    TffPict pict;
    Tbuffer pictbuf;
    static int me_hq(int rd3), me_(int me3);
    Textradata *extradata;
    REFERENCE_TIME rtStart, rtStop;
protected:
    virtual bool beginDecompress(TffPictBase &pict, FOURCC infcc, const CMediaType &mt, int sourceFlags);
    virtual HRESULT flushDec(void);
public:
    virtual int getType(void) const {
        return IDFF_MOVIE_XVID4;
    }
    virtual int caps(void) const {
        return CAPS::VIS_QUANTS;
    }

    virtual HRESULT decompress(const unsigned char *src, size_t srcLen, IMediaSample *pIn);
};

#endif

從以上這2種解碼器類的定義,我們可以看出一些規律,比如說:

1.  都有Tdll *dll這個變數,用於載入視訊解碼器的dll
2.  都有beginDecompress()函式,用於初始化解碼器
3.  都有decompress()函式,用於解碼

好了,閒話不說,迴歸正題,來看一下這些解碼器共有的父類:TvideoCodecDec

//具體 視訊 解碼器的父類,存一些公共資訊
class TvideoCodecDec : virtual public TvideoCodec, virtual public TcodecDec
{
protected:
    bool isdvdproc;
    comptrQ<IffdshowDecVideo> deciV;
    IdecVideoSink *sinkD;
    TvideoCodecDec(IffdshowBase *Ideci, IdecVideoSink *Isink);
    Rational guessMPEG2sar(const Trect &r, const Rational &sar2, const Rational &containerSar);

    class TtelecineManager
    {
    private:
        TvideoCodecDec* parent;
        int segment_count;
        int pos_in_group;
        struct {
            int fieldtype;
            int repeat_pict;
            REFERENCE_TIME rtStart;
        } group[2]; // store information about 2 recent frames.
        REFERENCE_TIME group_rtStart;
        bool film;
        int cfg_softTelecine;
    public:
        TtelecineManager(TvideoCodecDec* Iparent);
        void get_timestamps(TffPict &pict);
        void get_fieldtype(TffPict &pict);
        void new_frame(int top_field_first, int repeat_pict, const REFERENCE_TIME &rtStart, const REFERENCE_TIME &rtStop);
        void onSeek(void);
    } telecineManager;

public:
    static TvideoCodecDec* initDec(IffdshowBase *deci, IdecVideoSink *Isink, AVCodecID codecId, FOURCC fcc, const CMediaType &mt);

    virtual ~TvideoCodecDec();

    virtual int caps(void) const {
        return CAPS::NONE;
    }
    virtual bool testMediaType(FOURCC fcc, const CMediaType &mt) {
        return true;
    }
    virtual void forceOutputColorspace(const BITMAPINFOHEADER *hdr, int *ilace, TcspInfos &forcedCsps) {
        *ilace = 0; //cspInfos of forced output colorspace, empty when entering function
    }
    enum {SOURCE_REORDER = 1};
    virtual bool beginDecompress(TffPictBase &pict, FOURCC infcc, const CMediaType &mt, int sourceFlags) = 0;
    virtual HRESULT decompress(const unsigned char *src, size_t srcLen, IMediaSample *pIn) = 0;
    virtual bool onDiscontinuity(void) {
        return false;
    }
    virtual HRESULT onEndOfStream(void) {
        return S_OK;
    }

    unsigned int quantsDx, quantsStride, quantsDy, quantBytes, quantType;
    //QP表
	void *quants;
    uint16_t *intra_matrix, *inter_matrix;
	//計算平均QP
    float calcMeanQuant(void);
	//畫運動向量
    virtual bool drawMV(unsigned char *dst, unsigned int dx, stride_t stride, unsigned int dy) const {
        return false;
    }
    virtual const char* get_current_idct(void) {
        return NULL;
    }
    virtual int useDXVA(void) {
        return 0;
    };

    virtual void setOutputPin(IPin * /*pPin*/) {}
};

TvideoCodecDec這個類中,還定義了一個類TtelecineManager。這種在類裡面再定義一個類的方式還是不太多見的。TtelecineManager這個類的作用還沒有研究,先不管它。

可以看出,TvideoCodecDec類的定義並不複雜,最主要的變數有如下幾個,這幾個變數都是子類中會用到的:

comptrQ<IffdshowDecVideo>deciV:重要性不言而喻,回頭介紹
IdecVideoSink *sinkD:重要性不言而喻,回頭介紹
void *quants:QP表(為什麼要存在這裡還沒搞清)
TvideoCodecDec類定義了幾個函式:
initDec():初始化解碼器(重要)
calcMeanQuant():計算平均QP(為什麼要在這裡計算還沒搞清)

TvideoCodecDec類還定義了一些純虛擬函式,作為介面,這些函式的實現都在TvideoCodecDec的子類中完成【這幾個函式是最重要的】:

beginDecompress();
decompress();

TvideoCodecDec類中最重要的函式只有一個,就是initDec(),作用主要是初始化解碼器。其他的很多函式大多隻是定義了一個名稱,並沒有實現,因為都是打算在具體各種解碼器類中再進行實現的。

看一下initDec()的程式碼:

TvideoCodecDec* TvideoCodecDec::initDec(IffdshowBase *deci, IdecVideoSink *sink, AVCodecID codecId, FOURCC fcc, const CMediaType &mt)
{
    // DXVA mode is a preset setting
    switch (codecId) {
        case AV_CODEC_ID_H264:
            if (deci->getParam2(IDFF_filterMode) & IDFF_FILTERMODE_VIDEODXVA) {
                if (deci->getParam2(IDFF_dec_DXVA_H264)) {
                    codecId = CODEC_ID_H264_DXVA;
                } else {
                    return NULL;
                }
            }
            break;
        case AV_CODEC_ID_VC1:
        case CODEC_ID_WMV9_LIB:
            if (deci->getParam2(IDFF_filterMode) & IDFF_FILTERMODE_VIDEODXVA) {
                if (deci->getParam2(IDFF_dec_DXVA_VC1)) {
                    codecId = CODEC_ID_VC1_DXVA;
                } else {
                    return NULL;
                }
            }
            break;
        default:
            break;
    }

    TvideoCodecDec *movie = NULL;

    if (is_quicksync_codec(codecId)) {
        movie = new TvideoCodecQuickSync(deci, sink, codecId);
    } else if (lavc_codec(codecId)) {
        movie = new TvideoCodecLibavcodec(deci, sink);
    } else if (raw_codec(codecId)) {
        movie = new TvideoCodecUncompressed(deci, sink);
    } else if (wmv9_codec(codecId)) {
        movie = new TvideoCodecWmv9(deci, sink);
    } else if (codecId == CODEC_ID_XVID4) {
        movie = new TvideoCodecXviD4(deci, sink);
    } else if (codecId == CODEC_ID_LIBMPEG2) {
        movie = new TvideoCodecLibmpeg2(deci, sink);
    } else if (codecId == CODEC_ID_AVISYNTH) {
        movie = new TvideoCodecAvisynth(deci, sink);
    } else if (codecId == CODEC_ID_H264_DXVA || codecId == CODEC_ID_VC1_DXVA) {
        movie = new TvideoCodecLibavcodecDxva(deci, sink, codecId);
    } else {
        return NULL;
    }
    if (!movie) {
        return NULL;
    }
    if (movie->ok && movie->testMediaType(fcc, mt)) {
        movie->codecId = codecId;
        return movie;
    } else if (is_quicksync_codec(codecId)) {
        // QuickSync decoder init failed, revert to internal decoder.
        switch (codecId) {
            case CODEC_ID_H264_QUICK_SYNC:
                codecId = AV_CODEC_ID_H264;
                break;
            case CODEC_ID_MPEG2_QUICK_SYNC:
                codecId = CODEC_ID_LIBMPEG2;
                break;
            case CODEC_ID_VC1_QUICK_SYNC:
                codecId = CODEC_ID_WMV9_LIB;
                break;
            default:
                ASSERT(FALSE); // this shouldn't happen!
        }

        delete movie;

        // Call this function again with the new codecId.
        return initDec(deci, sink, codecId, fcc, mt);
    } else {
        delete movie;
        return NULL;
    }
}

這個函式的功能還是比較好理解的,根據CodecID的不同,建立不同的解碼器(從TvideoCodecLibavcodec,TvideoCodecXviD4,TvideoCodecLibmpeg2這些裡面選擇)。

雖然不知道用途是什麼,但是我們可以順便看一下計算平均QP的函式,就是把quants1指向的QP表裡面的資料求了一個平均值:

//計算平均QP
float TvideoCodecDec::calcMeanQuant(void)
{
    if (!quants || !quantsDx || !quantsDy) {
        return 0;
    }
    unsigned int sum = 0, num = quantsDx * quantsDy;
    unsigned char *quants1 = (unsigned char*)quants;
    for (unsigned int y = 0; y < quantsDy; y++)
        for (unsigned int x = 0; x < quantsDx; x++) {
            sum += quants1[(y * quantsStride + x) * quantBytes];
        }
    return float(sum) / num;
}


相關推薦

ffdshow 原始碼分析 8 視訊解碼TvideoCodecDec

=====================================================ffdshow原始碼分析系列文章列表:=====================================================前面兩篇文章介紹了ffds

ffdshow 原始碼分析 9解碼有關的總結

=====================================================ffdshow原始碼分析系列文章列表:=====================================================前幾篇文章已經完成了ffd

XBMC原始碼分析 4視訊播放dvdplayer-解碼以ffmpeg為例

XBMC分析系列文章: 本文我們分析XBMC中視訊播放器(dvdplayer)中的解碼器部分。由於解碼器種類很多,不可能一一分析,因此以ffmpeg解碼器為例進行分析。 XBMC解碼器部分檔案目錄如下圖所示: 解碼器分為音訊解碼器和視訊解碼器。在這裡我們看一下視訊

XBMC原始碼分析 6視訊播放dvdplayer-檔案頭以ffmpeg為例

XBMC分析系列文章: XBMC原始碼簡析 5:視訊播放器(dvdplayer)-解複用器(以ffmpeg為例)本文我們分析XBMC中視訊播放器(dvdplayer)中的檔案頭部分。檔案頭部分裡包含的是封裝Dll用到的標頭檔案。由於檔案頭種類很多,不可能一一分析,

Tomcat原始碼分析Tomcat啟動載入過程原始碼解析

Tomcat啟動載入過程(一)的原始碼解析 今天,我將分享用原始碼的方式講解Tomcat啟動的載入過程,關於Tomcat的架構請參閱《Tomcat原始碼分析二:先看看Tomcat的整體架構》一文。 先看看應用情況 在《Servlet與Tomcat執行示例》一文中,我詳細的記錄了Tomcat是如何啟動一個Ser

Java原始碼分析——String、StringBuffer、StringBuilder——AbstractStringBuilder抽象

    在Java中,關於字串類分為兩種,一種是上篇部落格講的String類,即不可變字串類,另外一種則是可變字串類,即AbstractStringBuilder抽象類的子類,StringBuffer與StringBuilder類,其中的兩者的區別

WebRTC原始碼分析視訊模組結構

本文在上篇的基礎上介紹WebRTC視訊部分的模組結構,以進一步瞭解其實現框架,只有瞭解了整體框架結構,對區域性演算法修改才能夠胸有成竹。 一、對外介面        對外介面有ViEBase,ViECapture,ViECodec,ViEEncryption,V

Netty原始碼分析第6章(解碼)---->第3節: 行解碼

  Netty原始碼分析第六章: 解碼器   第三節: 行解碼器   這一小節瞭解下行解碼器LineBasedFrameDecoder, 行解碼器的功能是一個位元組流, 以\r\n或者直接以\n結尾進行解碼, 也就是以換行符為分隔進行解析 同樣, 這個解碼器也繼承了B

Netty原始碼分析第6章(解碼)---->第2節: 固定長度解碼

  Netty原始碼分析第六章: 解碼器   第二節: 固定長度解碼器   上一小節我們瞭解到, 解碼器需要繼承ByteToMessageDecoder, 並重寫decode方法, 將解析出來的物件放入集合中集合, ByteToMessageDecoder中可以將解析出

Netty原始碼分析第6章(解碼)---->第1節: ByteToMessageDecoder

  Netty原始碼分析第六章: 解碼器   概述:           在我們上一個章節遺留過一個問題, 就是如果Server在讀取客戶端的資料的時候, 如果一次讀取不完整, 就觸發channelRead事件, 那麼Netty是

Netty原始碼分析第6章(解碼)---->第4節: 分隔符解碼

  Netty原始碼分析第六章: 解碼器   第四節: 分隔符解碼器     基於分隔符解碼器DelimiterBasedFrameDecoder, 是按照指定分隔符進行解碼的解碼器, 通過分隔符, 可以將二進位制流拆分成完整的資料包   同樣

WebRTC原始碼分析視訊處理流程

 文字介紹視訊的處理流程。圖1中顯示了兩路視訊會話視訊訊號流過程。 圖1 視訊流程示意圖 以一路視訊會話為例,主要分為以下幾個執行緒: 1)視訊源產生執行緒:Camera生產視訊畫面,封裝成視訊幀,以一定幀率投遞到下一個模組。; 2)採集執行緒:由Capturer負責採集視訊幀,並對視訊幀進行一定處理,如

RTMPdumplibRTMP 原始碼分析 8 傳送訊息Message

=====================================================RTMPdump(libRTMP) 原始碼分析系列文章:=====================================================函式呼叫

ffdshow 原始碼分析 5 點陣圖覆蓋濾鏡總結

=====================================================ffdshow原始碼分析系列文章列表:=====================================================前面寫了三篇文章,介紹了 

python 學習彙總25迭代iter tcy

迭代器 2018/6/12  目錄: iter 1.iter 2.iter-型別判斷 3.iter-解包 itertools工具 1.itertools函式簡表 見本人相關博文 2.itertools函式詳細說明 見本人相關博文 3

【PHP7原始碼分析】如何理解PHP虛擬機器

順風車運營研發團隊 李樂 1.從物理機說起 虛擬機器也是計算機,設計思想和物理機有很多相似之處; 1.1馮諾依曼體系結構 馮·諾依曼是當之無愧的數字計算機之父,當前計算機都採用的是馮諾依曼體系結構;設計思想主要包含以下幾個方面: 指令和資料不加區別混合儲存在同一個儲

mmap核心原始碼分析,基於核心版本3.10

之前寫了(一)(二)其實就梳理到了get_unmapped_area的內容,而且有一點混亂,這裡進行第三篇的講解,講解在do_mmap_pgoff中除了get_unmapped_area的內容,來了解mmap的具體實現。通過(一)(二)(三)來將mmap核心原始碼進行一次梳理

springMVC原始碼分析--異常處理機制HandlerExceptionResolver執行原理

上一篇部落格springMVC原始碼分析--異常處理機制HandlerExceptionResolver簡單示例(一)中我們簡單地實現了一個異常處理例項,接下來我們要介紹一下HandlerExceptionResolver是如何捕獲到Controller中丟擲的異常並展示到前

springMVC原始碼分析--異常處理機制HandlerExceptionResolver簡單示例

springMVC對Controller執行過程中出現的異常提供了統一的處理機制,其實這種處理機制也簡單,只要丟擲的異常在DispatcherServlet中都會進行捕獲,這樣就可以統一的對異常進行處理。        springMVC提供了一個HandlerExcepti

live555原始碼分析之------ H264 RTP封包原理總結

在一個RTP 包中封裝多個NALU,對於較小的NALU 可以採用這種打包方案,從而提高傳輸效率。 即可能是由多個 NAL 單元組成一個 RTP 包。 分別有4種組合方式: STAP-A, STAP-B, MTAP16, MTAP24.  那麼這裡的型別值分別是 24, 25, 26 以及 27.