ffdshow 源代码分析 8: 视频解码器类(TvideoCodecDec)

来源:互联网 发布:java发送自定义短信 编辑:程序博客网 时间:2024/06/05 08:44

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

ffdshow源代码分析系列文章列表:

ffdshow 源代码分析 1: 整体结构

ffdshow 源代码分析 2: 位图覆盖滤镜(对话框部分Dialog)

ffdshow 源代码分析 3: 位图覆盖滤镜(设置部分Settings)

ffdshow 源代码分析 4: 位图覆盖滤镜(滤镜部分Filter)

ffdshow 源代码分析 5: 位图覆盖滤镜(总结)

ffdshow 源代码分析 6: 对解码器的dll的封装(libavcodec)

ffdshow 源代码分析 7: libavcodec视频解码器类(TvideoCodecLibavcodec)

ffdshow 源代码分析 8: 视频解码器类(TvideoCodecDec)

ffdshow 源代码分析 9: 编解码器有关类的总结

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

前面两篇文章介绍了ffdshow中libavcodec的封装类Tlibavcodec,以及libavcodec的解码器类TvideoCodecLibavcodec:

ffdshow 源代码分析 6: 对解码器的dll的封装(libavcodec)

ffdshow 源代码分析 7: 解码器类(TvideoCodecLibavcodec)

其中libavcodec的解码器类TvideoCodecLibavcodec通过调用Tlibavcodec中的方法实现了libavcodec的dll中方法的调用;而它继承了TvideoCodecDec,本文正是要分析它继承的这个类。

 

TvideoCodecDec是所有视频解码器共有的父类。可以看一下它的继承关系:


可见,除了TvideoCodecLibavcodec继承了TvideoCodecDec之外,还有好几个类继承了TvideoCodecDec,比如说:TvideoCodecLibmpeg2,TvideoCodecXviD4等等…。突然来了兴趣,我们可以看一下其他的解码器类的定义是什么样的。

TvideoCodecLibmpeg2定义如下:

/*  *雷霄骅  *leixiaohua1020@126.com  *中国传媒大学/数字电视技术  */ #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定义如下:

/*  *雷霄骅  *leixiaohua1020@126.com  *中国传媒大学/数字电视技术  */ #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表里面的数据求了一个平均值:

//计算平均QPfloat 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;}





原创粉丝点击