#include #define DMO_NOATL // xfwrap.h needs this to work w/o ATL #include #include "decode.h" #include #include "resource.h" #include "strmif.h" // DVINFO #include "initguid.h" DEFINE_GUID(CLSID_DVDecDMO, 0xb321fd8b,0xcf6c,0x4bbe,0xaf,0x37,0xd1,0xa5,0x10,0xe4,0xee,0xee); DEFINE_GUID(MEDIASUBTYPE_dvc, 0x20637664,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71); const DWORD bits565[] = {0x00F800,0x0007E0,0x00001F}; class CDVDec : public CComBase, public C1for1QCDMO, public ISpecifyPropertyPages, public IIPDVDec { public: DECLARE_IUNKNOWN; STDMETHODIMP NDQueryInterface(REFIID riid, void **ppv); static CComBase *CreateInstance(IUnknown *pUnk, HRESULT *phr); CDVDec(IUnknown *pUnk, HRESULT *phr); ~CDVDec(); // entry points called by the base class HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt); HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt); HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt); HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt); HRESULT QCProcess(BYTE* pIn,ULONG ulBytesIn,BYTE* pOut,ULONG* pulProduced); HRESULT GetSampleSizes(ULONG* ulMaxInputSize, ULONG* ulMaxOutputSize); HRESULT Init(); // internal methods HRESULT MapInputTypeToOutputType(const DMO_MEDIA_TYPE *pmtIn, DMO_MEDIA_TYPE *pmtOut); void InitDestinationVideoInfo(VIDEOINFO *pVI, DWORD Comp, int n); long ActualHeight(long biHeight); void SetDimensions(); // ISpecifyPropertyPages interface STDMETHODIMP GetPages(CAUUID *pPages); // Entry points for communicating with the property page STDMETHODIMP get_IPDisplay(int *iDisplay); STDMETHODIMP put_IPDisplay(int iDisplay); private: char *m_pMem; int m_iDisplay; long m_lPicWidth; long m_lPicHeight; long m_lStride; DWORD m_CodecCap; DWORD m_CodecReq; }; CComBase* CDVDec::CreateInstance(IUnknown *pUnk, HRESULT *phr) { return new CDVDec(pUnk, phr); } HRESULT CDVDec::get_IPDisplay(int *iDisplay) { CDMOAutoLock l(&m_cs); *iDisplay = m_iDisplay; return NOERROR; } HRESULT CDVDec::put_IPDisplay(int iDisplay) { CDMOAutoLock l(&m_cs); m_iDisplay = iDisplay; SetDimensions(); return NOERROR; } // Set m_lPicHeight and m_lPicWidth based on the m_iDisplay setting. // The way this sets m_lPicHeight assumes NTSC, but the value of m_lPicHeight // is used only for mediatype *enumeration* - the mediatype *checking* code // will accept matching PAL values as well. void CDVDec::SetDimensions() { switch (m_iDisplay) { case IDC_DEC360x240: m_lPicWidth = 360; m_lPicHeight = 240; break; case IDC_DEC720x480: m_lPicWidth = 720; m_lPicHeight = 480; break; case IDC_DEC180x120: m_lPicWidth = 180; m_lPicHeight = 120; break; case IDC_DEC88x60: m_lPicWidth = 88; m_lPicHeight = 60; break; // no default } } CDVDec::CDVDec(IUnknown *pUnk, HRESULT *phr) : CComBase(pUnk, phr), m_pMem(NULL), m_iDisplay(IDC_DEC360x240) { SetDimensions(); m_CodecCap = AM_DVDEC_DC | AM_DVDEC_Quarter | AM_DVDEC_Half | AM_DVDEC_Full | AM_DVDEC_NTSC | AM_DVDEC_PAL | AM_DVDEC_RGB24 | AM_DVDEC_UYVY | AM_DVDEC_YUY2 | AM_DVDEC_RGB565 | AM_DVDEC_RGB555 | AM_DVDEC_RGB8 | AM_DVDEC_DVSD | AM_DVDEC_MMX; } CDVDec::~CDVDec() { delete[] m_pMem; } HRESULT CDVDec::NDQueryInterface(REFIID riid, void **ppv) { if (riid == IID_IMediaObject) return GetInterface((IMediaObject*)this, ppv); if (riid == IID_ISpecifyPropertyPages) return GetInterface((ISpecifyPropertyPages*)this, ppv); if (riid == IID_IIPDVDec) return GetInterface((IIPDVDec*)this, ppv); if (riid == IID_IDMOQualityControl) return GetInterface((IDMOQualityControl*)this, ppv); return CComBase::NDQueryInterface(riid, ppv); } // Returns the clsid's of the property pages we support STDMETHODIMP CDVDec::GetPages(CAUUID *pPages) { pPages->cElems = 1; pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID)); if (pPages->pElems == NULL) { return E_OUTOFMEMORY; } *(pPages->pElems) = CLSID_DVDecPropertiesPage; return NOERROR; } /***********************************************************************\ * IsMMXCPU * * Function to check if the current processor is an MMX processor. * \***********************************************************************/ BOOL IsMMXCPU() { #ifdef _X86_ ////////////////////////////////////////////////////// // work around for Cyrix M2 hang (when MMX flag is on) // emit cpuid and detect Cyrix M2, if its present, then return FALSE // WARNING: This will not work in 64 bit architectures __try { DWORD s1, s2, s3; // temporary holders for the vendor name __asm { // null out eax mov eax, 0x00; // load opcode CPUID == (0x0FA2) _emit 0x0f; _emit 0xa2; mov s1, ebx; // copy "Cyri" (backwards) mov s2, edx; // copy "xIns" (backwards) mov s3, ecx; // copy "tead" (backwards) } //DbgLog((LOG_TRACE, 1, TEXT("CPUID Instruction Supported"))); // check Vendor Id if( (s1 == (('i' << 24) | ('r' << 16) | ('y' << 8) | ('C'))) && (s2 == (('s' << 24) | ('n' << 16) | ('I' << 8) | ('x'))) && (s3 == (('d' << 24) | ('a' << 16) | ('e' << 8) | ('t'))) ) { //DbgLog((LOG_TRACE, 1, TEXT("Cyrix detected"))); return FALSE; } else { // otherwise it's some other vendor and continue with MMX detection //DbgLog((LOG_TRACE, 1, TEXT("Cyrix not found, reverting to MMX detection"))); } } __except(EXCEPTION_EXECUTE_HANDLER) { // log it and continue on to MMX detection sequence //DbgLog((LOG_TRACE, 1, TEXT("CPUID instruction not supported, reverting to MMX detection"))); } // END Cyrix M2 detection ////////////////////////////////////////////////////// // // If this is an Intel platform we need to make sure that we // are running on a machine that supports MMX instructions // __try { __asm _emit 0fh; __asm _emit 77h; return TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { return FALSE; } #else return FALSE; #endif } /////////////////////////////////////////////////////////////// // // // M E D I A T Y P E N E G O T I A T I O N C O D E // // // /////////////////////////////////////////////////////////////// // // A note about the mediatype code below. // // The code in GetOutputType(), CheckOutputType(), and Init() largely came from // the original DV decoder's CheckInputType(), CheckTransform(), SetMediaType(), // and GetMediaType(). I had to shuffle pieces of code around a lot to fit the // different framework. I tried to understand the code while porting it, but // some of it is still beyond me, so it is quite possible I introduced some bugs // in the process. // ///////////////////////////////////////////////////////////////////// // Helpers. These were mostly inlined in the original DV decoder, // // but I made them functions to eliminate some code duplication. // // Again, I probably introduced bugs in the process. // ///////////////////////////////////////////////////////////////////// long CDVDec::ActualHeight(long biHeight) { // // This function uses biHieght only to determine whether this format is PAL or // NTSC. Once that is determined, the code completely ignores the actual value // of biHeight and relies exclusively on m_iDisplay. I do not understand why // this makes sense, but I swear that this is effectively what the code in the // original DV decoder does. // if ((biHeight == 480) || (biHeight == 240) || (biHeight == 120) || (biHeight == 60)) { // NTSC if (m_iDisplay == IDC_DEC720x480) return 480; if (m_iDisplay == IDC_DEC360x240) return 240; if (m_iDisplay == IDC_DEC180x120) return 120; if (m_iDisplay == IDC_DEC88x60) return 60; return 0; } if ((biHeight == 576) || (biHeight == 288) || (biHeight == 144) || (biHeight == 72)) { // PAL if (m_iDisplay == IDC_DEC720x480) return 576; if (m_iDisplay == IDC_DEC360x240) return 288; if (m_iDisplay == IDC_DEC180x120) return 144; if (m_iDisplay == IDC_DEC88x60) return 72; return 0; } return 0; } DWORD InputReq(REFGUID subtype) { if ((subtype == MEDIASUBTYPE_dvsd) || (subtype == MEDIASUBTYPE_dvc)) return AM_DVDEC_DVSD; if (subtype == MEDIASUBTYPE_dvhd) return AM_DVDEC_DVHD; if (subtype == MEDIASUBTYPE_dvsl) return AM_DVDEC_DVSL; return 0; } DWORD OutputReq(REFGUID subtype) { if (subtype == MEDIASUBTYPE_UYVY) return AM_DVDEC_UYVY; if (subtype == MEDIASUBTYPE_YUY2) return AM_DVDEC_YUY2; if (subtype == MEDIASUBTYPE_RGB565) return AM_DVDEC_RGB565; else if (subtype == MEDIASUBTYPE_RGB555) return AM_DVDEC_RGB555; if (subtype == MEDIASUBTYPE_RGB24) return AM_DVDEC_RGB24; if (subtype == MEDIASUBTYPE_Y41P) return AM_DVDEC_Y41P; if (subtype == MEDIASUBTYPE_RGB8) return AM_DVDEC_RGB8; return 0; } DWORD ScaleReq(long biHeight, long biWidth) { if ((biHeight == 480) || (biHeight == 576)) { if (biWidth != 720) return 0; return AM_DVDEC_Full; } if ((biHeight == 240) || (biHeight == 288)) { if (biWidth != 360) return 0; return AM_DVDEC_Half; } if ((biHeight == 120) || (biHeight == 144)) { if (biWidth != 180) return 0; return AM_DVDEC_Quarter; } if ((biHeight == 60) || (biHeight == 72)) { if (biWidth != 88) return 0; return AM_DVDEC_DC; } return 0; } void GetHeightAndWidth(VIDEOINFO *videoInfo, long *pbiHeight, long *pbiWidth) { // if rcTarget is not empty, use its dimensions instead of biWidth and biHeight, // to see if it's an acceptable size. Then use biWidth as the stride. RECT* prcDst = &(videoInfo->rcTarget); if (!IsRectEmpty(prcDst)) { *pbiHeight = abs(prcDst->bottom - prcDst->top); *pbiWidth = abs(prcDst->right - prcDst->left); } else { *pbiHeight = abs(videoInfo->bmiHeader.biHeight); *pbiWidth = videoInfo->bmiHeader.biWidth; } } /////////////////////////// // End mediatype helpers // /////////////////////////// /////////////////////////////////// // Public mediatype entry points // /////////////////////////////////// HRESULT CDVDec::GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) { return DMO_E_NO_MORE_ITEMS; } HRESULT CDVDec::GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) { DMO_MEDIA_TYPE* pmtIn = InputType(); if (!pmtIn) return DMO_E_NO_MORE_ITEMS; pmt->majortype = MEDIATYPE_Video; pmt->formattype = FORMAT_VideoInfo; DWORD cbFormat; DWORD dwCompression; int nBitCount; //looking for format flag DWORD dw = 1L << 5; //first output format flag is the 7th bit in m_CodecCap do { dw <<= 1; while (!(m_CodecCap & dw))//will not do unlimited loop since AM_DVDEC_DVSD has to be 1 dw <<= 1; if (dw > AM_DVDEC_Y41P) return DMO_E_NO_MORE_ITEMS; if (ulTypeIndex == 0) break; ulTypeIndex--; } while (1); dw = m_CodecCap & dw; switch (dw ) { case AM_DVDEC_YUY2: cbFormat = SIZE_VIDEOHEADER; dwCompression = MAKEFOURCC('Y','U','Y','2'); nBitCount = 16; pmt->subtype = MEDIASUBTYPE_YUY2; break; case AM_DVDEC_UYVY: cbFormat = SIZE_VIDEOHEADER; dwCompression = MAKEFOURCC('U','Y','V','Y'); nBitCount = 16; pmt->subtype = MEDIASUBTYPE_UYVY; break; case AM_DVDEC_RGB24: cbFormat = SIZE_VIDEOHEADER; dwCompression = BI_RGB; nBitCount = 24; pmt->subtype = MEDIASUBTYPE_RGB24; break; case AM_DVDEC_RGB565: cbFormat = SIZE_VIDEOHEADER + SIZE_MASKS; dwCompression = BI_BITFIELDS; nBitCount = 16; pmt->subtype = MEDIASUBTYPE_RGB565; break; case AM_DVDEC_RGB555: cbFormat = SIZE_VIDEOHEADER; dwCompression = BI_RGB; nBitCount = 16; pmt->subtype = MEDIASUBTYPE_RGB555; break; case AM_DVDEC_RGB8: cbFormat = SIZE_VIDEOHEADER+SIZE_PALETTE; dwCompression = BI_RGB; nBitCount = 8; pmt->subtype = MEDIASUBTYPE_RGB8; break; case AM_DVDEC_Y41P: cbFormat = SIZE_VIDEOHEADER; dwCompression = MAKEFOURCC('Y','4','1','P'); nBitCount = 12; pmt->subtype = MEDIASUBTYPE_Y41P; break; default: return DMO_E_NO_MORE_ITEMS; } MoInitMediaType(pmt, cbFormat); // copy input format block // Dirty trick ! The original DV decoder does this, and it seems to work, // but I don't have enough knowledge about what goes on in the format block // to feel confident about this. memcpy(pmt->pbFormat, pmtIn->pbFormat, min(pmt->cbFormat, pmtIn->cbFormat)); VIDEOINFO* pVideoInfo = (VIDEOINFO *)pmt->pbFormat; InitDestinationVideoInfo(pVideoInfo, dwCompression, nBitCount); if (dw == AM_DVDEC_RGB565) { DWORD *pdw; pdw = (DWORD *)(HEADER(pVideoInfo) + 1); pdw[iRED] = bits565[iRED]; pdw[iGREEN] = bits565[iGREEN]; pdw[iBLUE] = bits565[iBLUE]; } pmt->bTemporalCompression = FALSE; pmt->lSampleSize = HEADER(pVideoInfo)->biSizeImage; return S_OK; } HRESULT CDVDec::CheckInputType(const DMO_MEDIA_TYPE *pmt) { if (pmt->majortype != MEDIATYPE_Video) return DMO_E_TYPE_NOT_ACCEPTED; // check format block if (!(((pmt->formattype == FORMAT_VideoInfo) && (pmt->cbFormat >= SIZE_VIDEOHEADER) && (pmt->pbFormat != NULL)) || ((pmt->formattype == FORMAT_DvInfo) && (pmt->cbFormat >= SIZE_VIDEOHEADER) && (pmt->pbFormat != NULL)))) return DMO_E_TYPE_NOT_ACCEPTED; DWORD dwReq = InputReq(pmt->subtype); if (!(dwReq & m_CodecCap)) return DMO_E_TYPE_NOT_ACCEPTED; if (!ActualHeight((HEADER((VIDEOINFO*)pmt->pbFormat))->biHeight)) return DMO_E_TYPE_NOT_ACCEPTED; return NOERROR; } HRESULT CDVDec::CheckOutputType(const DMO_MEDIA_TYPE *pmt) { // check major type if (pmt->majortype != MEDIATYPE_Video) return DMO_E_TYPE_NOT_ACCEPTED; // check format block if ((pmt->formattype != FORMAT_VideoInfo) || (pmt->cbFormat < SIZE_VIDEOHEADER) || (pmt->pbFormat == NULL)) return DMO_E_TYPE_NOT_ACCEPTED; // check subtype DWORD dwTmp = OutputReq(pmt->subtype); if(!(m_CodecCap & dwTmp)) return DMO_E_TYPE_NOT_ACCEPTED; VIDEOINFO* videoInfo = (VIDEOINFO *)pmt->pbFormat; RECT* prcSrc = &(videoInfo->rcSource); RECT* prcDst = &(videoInfo->rcTarget); //if rcSource is not empty, it must be the same as rcTarget, otherwise, FAIL if (!IsRectEmpty(prcSrc)) { if ((prcSrc->left != prcDst->left) || (prcSrc->top != prcDst->top) || (prcSrc->right != prcDst->right) || (prcSrc->bottom != prcDst->bottom)) return DMO_E_TYPE_NOT_ACCEPTED; } // Also, make sure biWidth and biHeight are bigger than the rcTarget size. if (!IsRectEmpty(prcDst)) { if((abs(videoInfo->bmiHeader.biHeight) < abs(prcDst->bottom - prcDst->top)) || (abs(videoInfo->bmiHeader.biWidth) < abs(prcDst->right - prcDst->left))) return DMO_E_TYPE_NOT_ACCEPTED; } long biHeight, biWidth; GetHeightAndWidth(videoInfo, &biHeight, &biWidth); //check down stream filter's require height and width dwTmp = ScaleReq(biHeight, biWidth); if (!(m_CodecCap & dwTmp)) return DMO_E_TYPE_NOT_ACCEPTED; return NOERROR; } /////////////////////////////////////// // End public mediatype entry points // /////////////////////////////////////// // // Fills in common video and bitmap info header fields // Stolen from the original DV decoder, which I hear in turn stole it from // some other filter. Needless to say, I do not thoroughly understand it. // void CDVDec::InitDestinationVideoInfo( VIDEOINFO *pVideoInfo, DWORD dwComppression, int nBitCount ) { LPBITMAPINFOHEADER lpbi = HEADER(pVideoInfo); lpbi->biSize = sizeof(BITMAPINFOHEADER); lpbi->biWidth = m_lPicWidth; lpbi->biHeight = m_lPicHeight; lpbi->biPlanes = 1; lpbi->biBitCount = (WORD)nBitCount; lpbi->biXPelsPerMeter = 0; lpbi->biYPelsPerMeter = 0; lpbi->biCompression = dwComppression; lpbi->biSizeImage = DIBSIZE(*lpbi); //pVideoInfo->bmiHeader.biClrUsed = STDPALCOLOURS; //pVideoInfo->bmiHeader.biClrImportant = STDPALCOLOURS; if(nBitCount >8 ){ lpbi->biClrUsed = 0; lpbi->biClrImportant = 0; }else if( nBitCount==8) { lpbi->biClrUsed = SIZE_PALETTE / sizeof(RGBQUAD); lpbi->biClrImportant = 0; RGBQUAD * prgb = (RGBQUAD *) (lpbi+1); // fixed PALETTE table (0 <= i < 256) for(int i=0; i<256;i++) { prgb[i].rgbRed = (i/64) << 6; prgb[i].rgbGreen = ((i/4)%16) << 4; prgb[i].rgbBlue = (i%4) << 6 ; prgb[i].rgbReserved =0; } } pVideoInfo->rcSource.top = 0; pVideoInfo->rcSource.left = 0; pVideoInfo->rcSource.right = m_lPicWidth; pVideoInfo->rcSource.bottom = m_lPicHeight; if( m_lPicHeight== 576 || m_lPicHeight== 288 || m_lPicHeight== 144 || m_lPicHeight== 72 ) pVideoInfo->AvgTimePerFrame = 10000000 / 25; //InVidInfo->AvgTimePerFrame; else pVideoInfo->AvgTimePerFrame = 10000000 / 30; //InVidInfo->AvgTimePerFrame; pVideoInfo->rcTarget = pVideoInfo->rcSource; // // The "bit" rate is image size in bytes times 8 (to convert to bits) // divided by the AvgTimePerFrame. This result is in bits per 100 nSec, // so we multiply by 10000000 to convert to bits per second, this multiply // is combined with "times" 8 above so the calculations becomes: // // BitRate = (biSizeImage * 80000000) / AvgTimePerFrame // LARGE_INTEGER li; li.QuadPart = pVideoInfo->AvgTimePerFrame; pVideoInfo->dwBitRate = MulDiv(lpbi->biSizeImage, 80000000, li.LowPart); pVideoInfo->dwBitErrorRate = 0L; } /////////////////////////////////////////////////////////////////////// // // // E N D M E D I A T Y P E N E G O T I A T I O N C O D E // // // /////////////////////////////////////////////////////////////////////// HRESULT CDVDec::GetSampleSizes(ULONG* pulMaxInputSize, ULONG* pulMaxOutputSize) { DMO_MEDIA_TYPE *pmtIn = InputType(); DMO_MEDIA_TYPE *pmtOut = OutputType(); if (!pmtIn || !pmtOut) return DMO_E_TYPE_NOT_SET; long lHeight = ActualHeight((HEADER((VIDEOINFO*)pmtIn->pbFormat))->biHeight); if (lHeight % 60 == 0) *pulMaxInputSize = 150*80*10; // NTSC else if (lHeight % 72 == 0) *pulMaxInputSize = 150*80*12; // PAL else return E_FAIL; *pulMaxOutputSize = pmtOut->lSampleSize; return NOERROR; } // // Like with mediatype nogotiation, the logic here came straight from the // original DV decoder. I made minor code changes, but they should not // affect the outcome. // HRESULT CDVDec::Init() { HRESULT hr; DMO_MEDIA_TYPE *pmtIn = InputType(); DMO_MEDIA_TYPE *pmtOut = OutputType(); m_lPicHeight = ActualHeight((HEADER((VIDEOINFO*)pmtIn->pbFormat))->biHeight); m_CodecReq = 0; m_CodecReq |= InputReq(pmtIn->subtype); m_CodecReq |= OutputReq(pmtOut->subtype); // size long biHeight, biWidth; VIDEOINFO* pvi = (VIDEOINFO*)pmtOut->pbFormat; GetHeightAndWidth(pvi, &biHeight, &biWidth); m_CodecReq |= ScaleReq(biHeight, biWidth); if (m_lPicHeight % 60 == 0) m_CodecReq |= AM_DVDEC_NTSC; else m_CodecReq |= AM_DVDEC_PAL; if (IsMMXCPU() && (m_CodecCap & AM_DVDEC_MMX)) m_CodecReq |= AM_DVDEC_MMX; //m_lStride = ((pvi->bmiHeader.biWidth * pvi->bmiHeader.biBitCount) + 7) / 8; m_lStride = pvi->bmiHeader.biWidth ; m_lStride = (m_lStride + 3) & ~3; #define ABSOL(x) (x < 0 ? -x : x) if ((pvi->bmiHeader.biHeight < 0) || (pvi->bmiHeader.biCompression > BI_BITFIELDS)) m_lStride = ABSOL(m_lStride); //directDraw else m_lStride = -ABSOL(m_lStride); //DIB //memory for MEI's decoder if (m_pMem == NULL) m_pMem = new char[440000]; if(m_pMem == NULL) return E_OUTOFMEMORY; return C1for1QCDMO::Init(); } HRESULT CDVDec::QCProcess(BYTE* pIn, ULONG ulBytesIn, BYTE* pOut, ULONG* pulProduced) { *m_pMem = 0; HRESULT hr = DvDecodeAFrame(pIn ,pOut, m_CodecReq, m_lStride, m_pMem); if (hr != S_OK) return E_FAIL; *pulProduced = OutputType()->lSampleSize; return NOERROR; } // // COM DLL stuff // struct CComClassTemplate g_ComClassTemplates[] = { { &CLSID_DVDecDMO, CDVDec::CreateInstance } }; int g_cComClassTemplates = 1; STDAPI DllRegisterServer(void) { HRESULT hr; // Register as a COM class hr = CreateCLSIDRegKey(CLSID_DVDecDMO, "DV decoder media object"); if (FAILED(hr)) return hr; // Now register as a DMO DMO_PARTIAL_MEDIATYPE mtIn[4]; mtIn[0].type = MEDIATYPE_Video; mtIn[0].subtype = MEDIASUBTYPE_dvsd; mtIn[1].type = MEDIATYPE_Video; mtIn[1].subtype = MEDIASUBTYPE_dvhd; mtIn[2].type = MEDIATYPE_Video; mtIn[2].subtype = MEDIASUBTYPE_dvsl; mtIn[3].type = MEDIATYPE_Video; mtIn[3].subtype = MEDIASUBTYPE_dvc; DMO_PARTIAL_MEDIATYPE mtOut[6]; mtOut[0].type = MEDIATYPE_Video; mtOut[0].subtype = MEDIASUBTYPE_UYVY; mtOut[1].type = MEDIATYPE_Video; mtOut[1].subtype = MEDIASUBTYPE_YUY2; mtOut[2].type = MEDIATYPE_Video; mtOut[2].subtype = MEDIASUBTYPE_RGB565; mtOut[3].type = MEDIATYPE_Video; mtOut[3].subtype = MEDIASUBTYPE_RGB555; mtOut[4].type = MEDIATYPE_Video; mtOut[4].subtype = MEDIASUBTYPE_RGB24; mtOut[5].type = MEDIATYPE_Video; mtOut[5].subtype = MEDIASUBTYPE_Y41P; return DMORegister(L"DV decoder", CLSID_DVDecDMO, DMOCATEGORY_VIDEO_DECODER, 0, 4, mtIn, 6, mtOut); } STDAPI DllUnregisterServer(void) { // Delete our clsid key RemoveCLSIDRegKey(CLSID_DVDecDMO); return DMOUnregister(CLSID_DVDecDMO, GUID_NULL); }