home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Riza / source / w32videocodec.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  18.2 KB  |  615 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    A/V interface library
  3. //    Copyright (C) 1998-2004 Avery Lee
  4. //
  5. //    This program is free software; you can redistribute it and/or modify
  6. //    it under the terms of the GNU General Public License as published by
  7. //    the Free Software Foundation; either version 2 of the License, or
  8. //    (at your option) any later version.
  9. //
  10. //    This program is distributed in the hope that it will be useful,
  11. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //    GNU General Public License for more details.
  14. //
  15. //    You should have received a copy of the GNU General Public License
  16. //    along with this program; if not, write to the Free Software
  17. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. #include <windows.h>
  20. #include <vfw.h>
  21. #include <vd2/system/debug.h>
  22. #include <vd2/system/error.h>
  23. #include <vd2/system/protscope.h>
  24. #include <vd2/system/vdalloc.h>
  25. #include <vd2/system/VDString.h>
  26. #include <vd2/system/vdstl.h>
  27. #include <vd2/system/w32assist.h>
  28. #include <vd2/Kasumi/pixmap.h>
  29. #include <vd2/Riza/bitmap.h>
  30. #include <vd2/Riza/videocodec.h>
  31.  
  32. IVDVideoCodecBugTrap *g_pVDVideoCodecBugTrap;
  33.  
  34. class VDVideoDecompressorVCM : public IVDVideoDecompressor {
  35. public:
  36.     VDVideoDecompressorVCM();
  37.     ~VDVideoDecompressorVCM();
  38.  
  39.     void Init(const void *srcFormat, uint32 srcFormatSize, HIC hic);
  40.  
  41.     bool QueryTargetFormat(int format);
  42.     bool QueryTargetFormat(const void *format);
  43.     bool SetTargetFormat(int format);
  44.     bool SetTargetFormat(const void *format);
  45.     int GetTargetFormat() { return mFormat; }
  46.     int GetTargetFormatVariant() { return mFormatVariant; }
  47.     const uint32 *GetTargetFormatPalette() { return mFormatPalette; }
  48.     void Start();
  49.     void Stop();
  50.     void DecompressFrame(void *dst, const void *src, uint32 srcSize, bool keyframe, bool preroll);
  51.     const void *GetRawCodecHandlePtr();
  52.     const wchar_t *GetName();
  53.  
  54. protected:
  55.     HIC            mhic;
  56.     int            mFormat;
  57.     int            mFormatVariant;
  58.     bool        mbActive;
  59.     bool        mbUseEx;
  60.     VDStringW    mName;
  61.     VDStringW    mDriverName;
  62.     vdstructex<VDAVIBitmapInfoHeader>    mSrcFormat;
  63.     vdstructex<VDAVIBitmapInfoHeader>    mDstFormat;
  64.  
  65.     uint32        mFormatPalette[256];
  66. };
  67.  
  68. IVDVideoDecompressor *VDCreateVideoDecompressorVCM(const void *srcFormat, uint32 srcFormatSize, const void *pHIC) {
  69.     vdautoptr<VDVideoDecompressorVCM> p(new VDVideoDecompressorVCM);
  70.  
  71.     p->Init(srcFormat, srcFormatSize, *(const HIC *)pHIC);
  72.     return p.release();
  73. }
  74.  
  75. VDVideoDecompressorVCM::VDVideoDecompressorVCM()
  76.     : mhic(NULL)
  77.     , mbActive(false)
  78.     , mFormat(0)
  79.     , mFormatVariant(0)
  80. {
  81. }
  82.  
  83. VDVideoDecompressorVCM::~VDVideoDecompressorVCM() {
  84.     Stop();
  85.  
  86.     if (mhic) {
  87.         VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  88.         ICClose(mhic);
  89.     }
  90. }
  91.  
  92. void VDVideoDecompressorVCM::Init(const void *srcFormat, uint32 srcFormatSize, HIC hic) {
  93.     VDASSERT(!mhic);
  94.  
  95.     mhic = hic;
  96.  
  97.     const VDAVIBitmapInfoHeader *bih = (const VDAVIBitmapInfoHeader *)srcFormat;
  98.  
  99.     mSrcFormat.assign(bih, srcFormatSize);
  100.  
  101.     ICINFO info = {sizeof(ICINFO)};
  102.     DWORD rv;
  103.  
  104.     {
  105.         VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  106.         rv = ICGetInfo(mhic, &info, sizeof info);
  107.     }
  108.  
  109.     if (rv >= sizeof info) {
  110.         mName = info.szDescription;
  111.         const wchar_t *pName = info.szDescription;
  112.         mDriverName = VDswprintf(L"Video codec \"%ls\"", 1, &pName);
  113.     }
  114. }
  115.  
  116. bool VDVideoDecompressorVCM::QueryTargetFormat(int format) {
  117.     vdstructex<VDAVIBitmapInfoHeader> bmformat;
  118.     const int variants = VDGetPixmapToBitmapVariants(format);
  119.  
  120.     for(int variant=1; variant<=variants; ++variant) {
  121.         if (VDMakeBitmapFormatFromPixmapFormat(bmformat, mSrcFormat, format, variant) && QueryTargetFormat(bmformat.data()))
  122.             return true;
  123.     }
  124.  
  125.     return false;
  126. }
  127.  
  128. bool VDVideoDecompressorVCM::QueryTargetFormat(const void *format) {
  129.     const BITMAPINFO *pSrcFormat = (const BITMAPINFO *)mSrcFormat.data();
  130.     DWORD retval;
  131.  
  132.     {
  133.         VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  134.         retval = ICDecompressQuery(mhic, pSrcFormat, (BITMAPINFO *)format);
  135.     }
  136.  
  137.     return retval == ICERR_OK;
  138. }
  139.  
  140. bool VDVideoDecompressorVCM::SetTargetFormat(int format) {
  141.     using namespace nsVDPixmap;
  142.  
  143.     if (!format) {
  144.         if (SetTargetFormat(kPixFormat_RGB888)
  145.             || SetTargetFormat(kPixFormat_XRGB8888)
  146.             || SetTargetFormat(kPixFormat_XRGB1555)
  147.             || SetTargetFormat(kPixFormat_YUV422_YUYV)
  148.             || SetTargetFormat(kPixFormat_YUV422_UYVY)
  149.             || SetTargetFormat(kPixFormat_YUV420_Planar)
  150.             )
  151.         {
  152.             return true;
  153.         }
  154.  
  155.         return SetTargetFormat(kPixFormat_Pal8);
  156.     }
  157.  
  158.     vdstructex<VDAVIBitmapInfoHeader> bmformat;
  159.     const int variants = VDGetPixmapToBitmapVariants(format);
  160.  
  161.     for(int variant=1; variant<=variants; ++variant) {
  162.         if (VDMakeBitmapFormatFromPixmapFormat(bmformat, mSrcFormat, format, variant) && SetTargetFormat(bmformat.data())) {
  163.             mFormat = format;
  164.             mFormatVariant = variant;
  165.             return true;
  166.         }
  167.     }
  168.  
  169.     return false;
  170. }
  171.  
  172. bool VDVideoDecompressorVCM::SetTargetFormat(const void *format) {
  173.     BITMAPINFO *pSrcFormat = (BITMAPINFO *)mSrcFormat.data();
  174.     BITMAPINFO *pDstFormat = (BITMAPINFO *)format;
  175.     DWORD retval;
  176.  
  177.     {
  178.         VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  179.         retval = ICDecompressQuery(mhic, pSrcFormat, pDstFormat);
  180.     }
  181.  
  182.     if (retval == ICERR_OK) {
  183.         if (mbActive)
  184.             Stop();
  185.  
  186.         if (pDstFormat->bmiHeader.biCompression == BI_RGB && pDstFormat->bmiHeader.biBitCount <= 8) {
  187.             uint32 colors = 1 << pDstFormat->bmiHeader.biBitCount;
  188.  
  189.             if (pDstFormat->bmiHeader.biClrUsed && pDstFormat->bmiHeader.biClrUsed < colors)
  190.                 colors = pDstFormat->bmiHeader.biClrUsed;
  191.  
  192.             if (colors > 256)
  193.                 colors = 256;
  194.  
  195.             uint32 palOffset = pDstFormat->bmiHeader.biSize;
  196.             memcpy(mFormatPalette, (const char *)format + palOffset, sizeof(uint32)*colors);
  197.         }
  198.  
  199.         mDstFormat.assign((const VDAVIBitmapInfoHeader *)format, VDGetSizeOfBitmapHeaderW32((const BITMAPINFOHEADER *)format));
  200.         mFormat = 0;
  201.         mFormatVariant = 0;
  202.         return true;
  203.     }
  204.  
  205.     return false;
  206. }
  207.  
  208. void VDVideoDecompressorVCM::Start() {
  209.     if (mDstFormat.empty())
  210.         throw MyError("Cannot find compatible target format for video decompression.");
  211.  
  212.     if (!mbActive) {
  213.         BITMAPINFO *pSrcFormat = (BITMAPINFO *)mSrcFormat.data();
  214.         BITMAPINFO *pDstFormat = (BITMAPINFO *)mDstFormat.data();
  215.         DWORD retval;
  216.  
  217.         mbUseEx = false;
  218.         {
  219.             VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  220.             retval = ICDecompressBegin(mhic, pSrcFormat, pDstFormat);
  221.  
  222.             if (retval != ICERR_OK) {
  223.                 BITMAPINFOHEADER *bihSrc = (BITMAPINFOHEADER *)pSrcFormat;
  224.                 BITMAPINFOHEADER *bihDst = (BITMAPINFOHEADER *)pDstFormat;
  225.                 if (ICERR_OK == ICDecompressExBegin(mhic, 0, bihSrc, NULL, 0, 0, bihSrc->biWidth, abs(bihSrc->biHeight), bihDst, NULL, 0, 0, bihDst->biWidth, abs(bihDst->biHeight))) {
  226.                     mbUseEx = true;
  227.                     retval = ICERR_OK;
  228.                 }
  229.             }
  230.         }
  231.  
  232.         if (retval != ICERR_OK)
  233.             throw MyICError("VideoSourceAVI", retval);
  234.  
  235.         mbActive = true;
  236.     }
  237. }
  238.  
  239. void VDVideoDecompressorVCM::Stop() {
  240.     if (mbActive) {
  241.         mbActive = false;
  242.  
  243.         VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  244.         if (mbUseEx)
  245.             ICDecompressExEnd(mhic);
  246.         else
  247.             ICDecompressEnd(mhic);
  248.     }
  249. }
  250.  
  251. void VDVideoDecompressorVCM::DecompressFrame(void *dst, const void *src, uint32 srcSize, bool keyframe, bool preroll) {
  252.     if (!mbActive)
  253.         Start();
  254.  
  255.     VDAVIBitmapInfoHeader *pSrcFormat = mSrcFormat.data();
  256.     VDAVIBitmapInfoHeader *pDstFormat = mDstFormat.data();
  257.  
  258.     DWORD dwFlags = 0;
  259.  
  260.     if (!keyframe)
  261.         dwFlags |= ICDECOMPRESS_NOTKEYFRAME;
  262.  
  263.     if (preroll)
  264.         dwFlags |= ICDECOMPRESS_PREROLL;
  265.  
  266.     DWORD dwOldSize = pSrcFormat->biSizeImage;
  267.     pSrcFormat->biSizeImage = srcSize;
  268.     DWORD retval;
  269.     
  270.     {
  271.         VDExternalCodeBracket bracket(mDriverName.c_str(), __FILE__, __LINE__);
  272.         if (mbUseEx) {
  273.             BITMAPINFOHEADER *bihSrc = (BITMAPINFOHEADER *)pSrcFormat;
  274.             BITMAPINFOHEADER *bihDst = (BITMAPINFOHEADER *)pDstFormat;
  275.             retval = ICDecompressEx(mhic, dwFlags, bihSrc, (LPVOID)src, 0, 0, bihSrc->biWidth, abs(bihSrc->biHeight), bihDst, dst, 0, 0, bihDst->biWidth, abs(bihDst->biHeight));
  276.         } else
  277.             retval = ICDecompress(mhic, dwFlags, (BITMAPINFOHEADER *)pSrcFormat, (LPVOID)src, (BITMAPINFOHEADER *)pDstFormat, dst);
  278.     }
  279.  
  280.     pSrcFormat->biSizeImage = dwOldSize;
  281.  
  282.     // We will get ICERR_DONTDRAW if we set preroll.
  283.     if (retval < 0)
  284.         throw MyICError(retval, "%%s (Error code: %d)", (int)retval);
  285. }
  286.  
  287. const void *VDVideoDecompressorVCM::GetRawCodecHandlePtr() {
  288.     return &mhic;
  289. }
  290.  
  291. const wchar_t *VDVideoDecompressorVCM::GetName() {
  292.     return mName.c_str();
  293. }
  294.  
  295. ///////////////////////////////////////////////////////////////////////////
  296.  
  297. namespace {
  298.     static bool CheckMPEG4Codec(HIC hic, bool isV3) {
  299.         char frame[0x380];
  300.         BITMAPINFOHEADER bih;
  301.  
  302.         // Form a completely black frame if it's V3.
  303.  
  304.         bih.biSize            = 40;
  305.         bih.biWidth            = 320;
  306.         bih.biHeight        = 240;
  307.         bih.biPlanes        = 1;
  308.         bih.biBitCount        = 24;
  309.         bih.biCompression    = '24PM';
  310.         bih.biSizeImage        = 0;
  311.         bih.biXPelsPerMeter    = 0;
  312.         bih.biYPelsPerMeter    = 0;
  313.         bih.biClrUsed        = 0;
  314.         bih.biClrImportant    = 0;
  315.  
  316.         if (isV3) {
  317.             int i;
  318.  
  319.             frame[0] = (char)0x3f;
  320.             frame[1] = (char)0x71;
  321.             frame[2] = (char)0x1b;
  322.             frame[3] = (char)0x7c;
  323.  
  324.             for(i=4; i<0x179; i+=5) {
  325.                 frame[i+0] = (char)0x2f;
  326.                 frame[i+1] = (char)0x0b;
  327.                 frame[i+2] = (char)0xc2;
  328.                 frame[i+3] = (char)0xf0;
  329.                 frame[i+4] = (char)0xbc;
  330.             }
  331.  
  332.             frame[0x179] = (char)0xf0;
  333.             frame[0x17a] = (char)0xb8;
  334.             frame[0x17b] = (char)0x01;
  335.  
  336.             bih.biCompression    = '34PM';
  337.             bih.biSizeImage        = 0x17c;
  338.         }
  339.  
  340.         // Attempt to decompress.
  341.  
  342.         HANDLE h;
  343.  
  344.         {
  345.             VDSilentExternalCodeBracket bracket;
  346.             h = ICImageDecompress(hic, 0, (BITMAPINFO *)&bih, frame, NULL);
  347.         }
  348.  
  349.         if (h) {
  350.             GlobalFree(h);
  351.             return true;
  352.         } else {
  353.             return false;
  354.         }
  355.     }
  356.  
  357.     DWORD VDSafeICDecompressQueryW32(HIC hic, LPBITMAPINFOHEADER lpbiIn, uint32 cbIn, LPBITMAPINFOHEADER lpbiOut, uint32 cbOut, const wchar_t *codecDesc) {
  358.         vdstructex<BITMAPINFOHEADER> bihIn, bihOut;
  359.  
  360.         if (lpbiIn)
  361.             bihIn.assign(lpbiIn, cbIn);
  362.  
  363.         if (lpbiOut)
  364.             bihOut.assign(lpbiIn, cbOut);
  365.  
  366.         // AngelPotion overwrites its input format with biCompression='MP43' and doesn't
  367.         // restore it, which leads to video codec lookup errors.  So what we do here is
  368.         // make a copy of the format in nice, safe memory and feed that in instead.
  369.  
  370.         // We used to write protect the format here, but apparently some versions of Windows
  371.         // have certain functions accepting (BITMAPINFOHEADER *) that actually call
  372.         // IsBadWritePtr() to verify the incoming pointer, even though those functions
  373.         // don't actually need to write to the format.  It would be nice if someone learned
  374.         // what 'const' was for.  AngelPotion doesn't crash because it has a try/catch
  375.         // handler wrapped around its code.
  376.  
  377.         DWORD result;
  378.         {
  379.             VDExternalCodeBracket bracket(codecDesc, __FILE__, __LINE__);
  380.             result = ICDecompressQuery(hic, lpbiIn ? bihIn.data() : NULL, lpbiOut ? bihOut.data() : NULL);
  381.         }
  382.  
  383.         // check for unwanted modification
  384.         if ((lpbiIn && memcmp(bihIn.data(), lpbiIn, cbIn)) || (lpbiOut && memcmp(bihOut.data(), lpbiOut, cbOut))) {
  385.             ICINFO info = {sizeof(ICINFO)};
  386.             {
  387.                 VDExternalCodeBracket bracket(codecDesc, __FILE__, __LINE__);
  388.                 ICGetInfo(hic, &info, sizeof info);
  389.             }
  390.  
  391.             if (g_pVDVideoCodecBugTrap)
  392.                 g_pVDVideoCodecBugTrap->OnCodecRenamingDetected(info.szDescription);
  393.         }
  394.  
  395.         // if the result passed, check whether we have a bad MS MPEG-4 V2/V3 codec
  396.  
  397.         if (result == ICERR_OK) {
  398.             // check for bad MPEG-4 V2/V3 codec
  399.  
  400.             if (lpbiIn->biCompression == '24PM') {
  401.                 if (!CheckMPEG4Codec(hic, false))
  402.                     return ICERR_UNSUPPORTED;
  403.             } else if (lpbiIn->biCompression == '34PM') {
  404.                 if (!CheckMPEG4Codec(hic, true))
  405.                     return ICERR_UNSUPPORTED;
  406.             }
  407.         }
  408.  
  409.         return result;
  410.     }
  411.  
  412.     HIC VDSafeICOpenW32(DWORD fccType, DWORD fccHandler, UINT wMode) {
  413.         HIC hic;
  414.         
  415.         vdprotected1("attempting to open video codec with FOURCC '%.4s'", const char *, (const char *)&fccHandler) {
  416.             wchar_t buf[64];
  417.             swprintf(buf, sizeof buf / sizeof buf[0], L"A video codec with FOURCC '%.4S'", (const char *)&fccHandler);
  418.             VDExternalCodeBracket bracket(buf, __FILE__, __LINE__);
  419.             hic = ICOpen(fccType, fccHandler, wMode);
  420.         }
  421.  
  422.         return hic;
  423.     }
  424.  
  425.     HIC VDSafeICLocateDecompressW32(DWORD fccType, DWORD fccHandler, LPBITMAPINFOHEADER lpbiIn, uint32 cbIn, LPBITMAPINFOHEADER lpbiOut, uint32 cbOut) {
  426.         ICINFO info={0};
  427.  
  428.         for(DWORD id=0; ICInfo(fccType, id, &info); ++id) {
  429.             info.dwSize = sizeof(ICINFO);    // I don't think this is necessary, but just in case....
  430.  
  431.             HIC hic = VDSafeICOpenW32(fccType, info.fccHandler, ICMODE_DECOMPRESS);
  432.  
  433.             if (!hic)
  434.                 continue;
  435.  
  436.             wchar_t buf[64];
  437.             swprintf(buf, 64, L"A video codec with FOURCC '%.4S'", (const char *)&fccHandler);
  438.  
  439.             vdprotected1("querying video codec with FOURCC \"%.4s\"", const char *, (const char *)&info.fccHandler) {
  440.                 DWORD result = VDSafeICDecompressQueryW32(hic, lpbiIn, cbIn, lpbiOut, cbOut, buf);
  441.  
  442.                 if (result == ICERR_OK) {
  443.                     // Check for a codec that doesn't actually support what it says it does.
  444.                     // We ask the codec whether it can do a specific conversion that it can't
  445.                     // possibly support.  If it does it, then we call BS and ignore the codec.
  446.                     // The Grand Tech Camera Codec and Panasonic DV codecs are known to do this.
  447.                     //
  448.                     // (general idea from Raymond Chen's blog)
  449.  
  450.                     BITMAPINFOHEADER testSrc = {        // note: can't be static const since IsBadWritePtr() will get called on it
  451.                         sizeof(BITMAPINFOHEADER),
  452.                         320,
  453.                         240,
  454.                         1,
  455.                         24,
  456.                         0x2E532E42,
  457.                         320*240*3,
  458.                         0,
  459.                         0,
  460.                         0,
  461.                         0
  462.                     };
  463.  
  464.                     DWORD res;
  465.                     {
  466.                         VDExternalCodeBracket bracket(buf, __FILE__, __LINE__);
  467.                         res = ICDecompressQuery(hic, &testSrc, NULL);
  468.                     }
  469.  
  470.                     if (ICERR_OK == res) {        // Don't need to wrap this, as it's OK if testSrc gets modified.
  471.                         ICINFO info = {sizeof(ICINFO)};
  472.  
  473.                         {
  474.                             VDExternalCodeBracket bracket(buf, __FILE__, __LINE__);
  475.                             ICGetInfo(hic, &info, sizeof info);
  476.                         }
  477.  
  478.                         if (g_pVDVideoCodecBugTrap)
  479.                             g_pVDVideoCodecBugTrap->OnAcceptedBS(info.szDescription);
  480.  
  481.                         // Okay, let's give the codec a chance to redeem itself. Reformat the input format into
  482.                         // a plain 24-bit RGB image, and ask it what the compressed format is. If it produces
  483.                         // a FOURCC that matches, allow it to handle the format. This should allow at least
  484.                         // the codec's primary format to work. Otherwise, drop it on the ground.
  485.                         
  486.                         if (lpbiIn) {
  487.                             BITMAPINFOHEADER unpackedSrc={
  488.                                 sizeof(BITMAPINFOHEADER),
  489.                                 lpbiIn ? lpbiIn->biWidth : 320,
  490.                                 lpbiIn ? lpbiIn->biHeight : 240,
  491.                                 1,
  492.                                 24,
  493.                                 BI_RGB,
  494.                                 0,
  495.                                 0,
  496.                                 0,
  497.                                 0,
  498.                                 0
  499.                             };
  500.  
  501.                             unpackedSrc.biSizeImage = ((unpackedSrc.biWidth*3+3)&~3)*abs(unpackedSrc.biHeight);
  502.  
  503.                             LONG size = ICCompressGetFormatSize(hic, &unpackedSrc);
  504.  
  505.                             if (size >= sizeof(BITMAPINFOHEADER)) {
  506.                                 vdstructex<BITMAPINFOHEADER> tmp;
  507.  
  508.                                 tmp.resize(size);
  509.                                 if (ICERR_OK == ICCompressGetFormat(hic, &unpackedSrc, tmp.data()) && tmp->biCompression == lpbiIn->biCompression)
  510.                                     return hic;
  511.                             }
  512.                         }
  513.                     } else {
  514.                         return hic;
  515.                     }
  516.                 }
  517.  
  518.                 ICClose(hic);
  519.             }
  520.         }
  521.  
  522.         return NULL;
  523.     }
  524. }
  525.  
  526. IVDVideoDecompressor *VDFindVideoDecompressor(uint32 preferredHandler, const void *srcFormat, uint32 srcFormatSize) {
  527.     vdstructex<BITMAPINFOHEADER> bmih((const BITMAPINFOHEADER *)srcFormat, srcFormatSize);
  528.     HIC hicDecomp = NULL;
  529.  
  530.     vdprotected2("attempting codec negotiation: fccHandler=0x%08x, biCompression=0x%08x", unsigned, preferredHandler, unsigned, bmih->biCompression) {
  531.         VDExternalCodeBracket bracket(L"A video codec", __FILE__, __LINE__);
  532.  
  533.         // Try the handler specified in the file first.  In some cases, it'll
  534.         // be wrong or missing. (VideoMatrix, among other programs, sets fccHandler=0.
  535.  
  536.         if (preferredHandler)
  537.             hicDecomp = VDSafeICOpenW32(ICTYPE_VIDEO, preferredHandler, ICMODE_DECOMPRESS);
  538.  
  539.         wchar_t buf[64];
  540.         swprintf(buf, 64, L"A video codec with FOURCC '%.4S'", (const char *)&preferredHandler);
  541.  
  542.         if (!hicDecomp || ICERR_OK!=VDSafeICDecompressQueryW32(hicDecomp, &*bmih, bmih.size(), NULL, 0, buf)) {
  543.             if (hicDecomp)
  544.                 ICClose(hicDecomp);
  545.  
  546.             // Pick a handler based on the biCompression field instead. We should imitate the
  547.             // mappings that ICLocate() does -- namely, BI_RGB and BI_RLE8 map to MRLE, and
  548.             // CRAM maps to MSVC (apparently an outdated name for Microsoft Video 1).
  549.  
  550.             DWORD fcc = bmih->biCompression;
  551.  
  552.             if (fcc == BI_RGB || fcc == BI_RLE8)
  553.                 fcc = 'ELRM';
  554.             else if (fcc == 'MARC')
  555.                 fcc = 'CVSM';
  556.  
  557.             if (fcc >= 0x10000)        // if we couldn't map a numerical value like BI_BITFIELDS, don't open a random codec
  558.                 hicDecomp = VDSafeICOpenW32(ICTYPE_VIDEO, fcc, ICMODE_DECOMPRESS);
  559.  
  560.             if (!hicDecomp || ICERR_OK!=VDSafeICDecompressQueryW32(hicDecomp, &*bmih, bmih.size(), NULL, 0, buf)) {
  561.                 if (hicDecomp) {
  562.                     ICClose(hicDecomp);
  563.                     hicDecomp = NULL;
  564.                 }
  565.  
  566.                 // Failed. Check if it is an MPEG-4 V3 clone; if so, cycle through the known clones
  567.                 // in order.
  568.  
  569.                 static const uint32 kMPEG4V3Clones[]={
  570.                     '34PM',
  571.                     '3VID',
  572.                     '4VID',
  573.                     '5VID',
  574.                     '14PA'
  575.                 };
  576.  
  577.                 enum { kMPEG4V3CloneCount = sizeof kMPEG4V3Clones / sizeof kMPEG4V3Clones[0] };
  578.  
  579.                 for(int i=0; i<kMPEG4V3CloneCount; ++i) {
  580.                     if (bmih->biCompression == kMPEG4V3Clones[i]) {
  581.                         // clone
  582.                         for(int j=0; j<kMPEG4V3CloneCount; ++j) {
  583.                             if (i == j)
  584.                                 continue;
  585.  
  586.                             bmih->biCompression = kMPEG4V3Clones[j];
  587.                             hicDecomp = VDSafeICLocateDecompressW32(ICTYPE_VIDEO, NULL, &*bmih, bmih.size(), NULL, 0);
  588.                             if (hicDecomp)
  589.                                 break;
  590.                             bmih->biCompression = fcc;
  591.                         }
  592.  
  593.                         break;
  594.                     }
  595.                 }
  596.  
  597.                 // Okay, search all installed codecs.
  598.                 if (!hicDecomp)
  599.                     hicDecomp = VDSafeICLocateDecompressW32(ICTYPE_VIDEO, NULL, &*bmih, bmih.size(), NULL, 0);
  600.             }
  601.         }
  602.     }
  603.  
  604.     if (!hicDecomp)
  605.         return NULL;
  606.  
  607.     // All good!
  608.  
  609.     return VDCreateVideoDecompressorVCM(&*bmih, bmih.size(), &hicDecomp);
  610. }
  611.  
  612. void VDSetVideoCodecBugTrap(IVDVideoCodecBugTrap *p) {
  613.     g_pVDVideoCodecBugTrap = p;
  614. }
  615.