// Copyright (c) 1997 - 1998 Microsoft Corporation. All Rights Reserved. // Util.cpp : Utility functions // #include "stdafx.h" #include "project.h" #include bool IsSameObject(IUnknown *pUnk1, IUnknown *pUnk2) { if (pUnk1 == pUnk2) { return TRUE; } // // NOTE: We can't use CComQIPtr here becuase it won't do the QueryInterface! // IUnknown *pRealUnk1; IUnknown *pRealUnk2; pUnk1->QueryInterface(IID_IUnknown, (void **)&pRealUnk1); pUnk2->QueryInterface(IID_IUnknown, (void **)&pRealUnk2); pRealUnk1->Release(); pRealUnk2->Release(); return (pRealUnk1 == pRealUnk2); } STDAPI_(void) TStringFromGUID(const GUID* pguid, LPTSTR pszBuf) { wsprintf(pszBuf, TEXT("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"), pguid->Data1, pguid->Data2, pguid->Data3, pguid->Data4[0], pguid->Data4[1], pguid->Data4[2], pguid->Data4[3], pguid->Data4[4], pguid->Data4[5], pguid->Data4[6], pguid->Data4[7]); } #ifndef UNICODE STDAPI_(void) WStringFromGUID(const GUID* pguid, LPWSTR pszBuf) { char szAnsi[40]; TStringFromGUID(pguid, szAnsi); MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, pszBuf, sizeof(szAnsi)); } #endif // // Media Type helpers // void InitMediaType(AM_MEDIA_TYPE * pmt) { ZeroMemory(pmt, sizeof(*pmt)); pmt->lSampleSize = 1; pmt->bFixedSizeSamples = TRUE; } bool IsEqualMediaType(AM_MEDIA_TYPE const & mt1, AM_MEDIA_TYPE const & mt2) { return ((IsEqualGUID(mt1.majortype,mt2.majortype) == TRUE) && (IsEqualGUID(mt1.subtype,mt2.subtype) == TRUE) && (IsEqualGUID(mt1.formattype,mt2.formattype) == TRUE) && (mt1.cbFormat == mt2.cbFormat) && ( (mt1.cbFormat == 0) || ( memcmp(mt1.pbFormat, mt2.pbFormat, mt1.cbFormat) == 0))); } void CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource) { *pmtTarget = *pmtSource; if (pmtSource->cbFormat != 0) { _ASSERTE(pmtSource->pbFormat != NULL); pmtTarget->pbFormat = (PBYTE)CoTaskMemAlloc(pmtSource->cbFormat); if (pmtTarget->pbFormat == NULL) { pmtTarget->cbFormat = 0; } else { CopyMemory((PVOID)pmtTarget->pbFormat, (PVOID)pmtSource->pbFormat, pmtTarget->cbFormat); } } if (pmtTarget->pUnk != NULL) { pmtTarget->pUnk->AddRef(); } } AM_MEDIA_TYPE * CreateMediaType(AM_MEDIA_TYPE *pSrc) { AM_MEDIA_TYPE *pMediaType = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); if (pMediaType ) { if (pSrc) { CopyMediaType(pMediaType,pSrc); } else { InitMediaType(pMediaType); } } return pMediaType; } void DeleteMediaType(AM_MEDIA_TYPE *pmt) { if (pmt) { FreeMediaType(*pmt); CoTaskMemFree((PVOID)pmt); } } void FreeMediaType(AM_MEDIA_TYPE& mt) { if (mt.cbFormat != 0) { CoTaskMemFree((PVOID)mt.pbFormat); // Strictly unnecessary but tidier mt.cbFormat = 0; mt.pbFormat = NULL; } if (mt.pUnk != NULL) { mt.pUnk->Release(); mt.pUnk = NULL; } } // this also comes in useful when using the IEnumMediaTypes interface so // that you can copy a media type, you can do nearly the same by creating // a CMediaType object but as soon as it goes out of scope the destructor // will delete the memory it allocated (this takes a copy of the memory) AM_MEDIA_TYPE * WINAPI AllocVideoMediaType(const AM_MEDIA_TYPE * pmtSource) { AM_MEDIA_TYPE *pMediaType = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); if (pMediaType) { VIDEOINFO *pVideoInfo = (VIDEOINFO *)CoTaskMemAlloc(sizeof(VIDEOINFO)); if (pVideoInfo) { if (pmtSource) { *pMediaType = *pmtSource; CopyMemory(pVideoInfo, pmtSource->pbFormat, sizeof(*pVideoInfo)); } else { ZeroMemory(pMediaType, sizeof(*pMediaType)); ZeroMemory(pVideoInfo, sizeof(*pVideoInfo)); pMediaType->majortype = MEDIATYPE_Video; pMediaType->cbFormat = sizeof(*pVideoInfo); pMediaType->formattype = FORMAT_VideoInfo; } pMediaType->pbFormat = (BYTE *)pVideoInfo; } else { CoTaskMemFree((PVOID)pMediaType); pMediaType = NULL; } } return pMediaType; } // // WARNING: The order of the entries in these tables is important! Make sure the // pixelformats and mediatypes line up! // const GUID * g_aFormats[] = { &MEDIASUBTYPE_RGB8, &MEDIASUBTYPE_RGB565, &MEDIASUBTYPE_RGB555, &MEDIASUBTYPE_RGB24, &MEDIASUBTYPE_RGB24, &MEDIASUBTYPE_RGB32, &MEDIASUBTYPE_RGB32 }; const DDPIXELFORMAT g_aPixelFormats[] = { {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_PALETTEINDEXED8, 0, 8, 0, 0, 0, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x0000F800, 0x000007E0, 0x0000001F, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}, {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0} }; bool VideoSubtypeFromPixelFormat(const DDPIXELFORMAT *pPixelFormat, GUID *pSubType) { for( int i = 0; i < sizeof(g_aPixelFormats)/sizeof(g_aPixelFormats[0]); i++ ) { if (ComparePixelFormats(&g_aPixelFormats[i], pPixelFormat)) { *pSubType = *g_aFormats[i]; return true; } } // OK - try just using the fourcc if (pPixelFormat->dwFlags & DDPF_FOURCC) { *pSubType = FOURCCMap(pPixelFormat->dwFourCC); return true; } return false; } bool IsSupportedType(const DDPIXELFORMAT *pPixelFormat) { for( int i = 0; i < sizeof(g_aPixelFormats)/sizeof(g_aPixelFormats[0]); i++ ) { if(ComparePixelFormats(&g_aPixelFormats[i], pPixelFormat)) { return true; } } return false; } const DDPIXELFORMAT * GetDefaultPixelFormatPtr(IDirectDraw *pDirectDraw) { if (pDirectDraw) { DDSURFACEDESC ddsd; ddsd.dwSize = sizeof(ddsd); if (SUCCEEDED(pDirectDraw->GetDisplayMode(&ddsd))) { for( int i = 0; i < sizeof(g_aPixelFormats)/sizeof(g_aPixelFormats[0]); i++ ) { if(memcmp(&g_aPixelFormats[i], &ddsd.ddpfPixelFormat, sizeof(g_aPixelFormats[i])) == 0) { return &g_aPixelFormats[i]; } } } } return &g_aPixelFormats[0]; } // // Helper function converts a DirectDraw surface to a media type. // The surface description must have: // Height // Width // lPitch -- Only used if DDSD_PITCH is set // PixelFormat // Initialise our output type based on the DirectDraw surface. As DirectDraw // only deals with top down display devices so we must convert the height of // the surface returned in the DDSURFACEDESC into a negative height. This is // because DIBs use a positive height to indicate a bottom up image. We also // initialise the other VIDEOINFO fields although they're hardly ever needed // // pmtTemplate is used to resolve any ambiguous mappings when we don't // want to change the connection type HRESULT ConvertSurfaceDescToMediaType(const DDSURFACEDESC *pSurfaceDesc, IDirectDrawPalette *pPalette, const RECT *pRect, BOOL bInvertSize, AM_MEDIA_TYPE **ppMediaType, AM_MEDIA_TYPE *pmtTemplate) { *ppMediaType = NULL; AM_MEDIA_TYPE *pMediaType = AllocVideoMediaType(NULL); if (pMediaType == NULL) { return E_OUTOFMEMORY; } if (!VideoSubtypeFromPixelFormat(&pSurfaceDesc->ddpfPixelFormat, &pMediaType->subtype)) { DeleteMediaType(pMediaType); return VFW_E_TYPE_NOT_ACCEPTED; } VIDEOINFO *pVideoInfo = (VIDEOINFO *)pMediaType->pbFormat; BITMAPINFOHEADER *pbmiHeader = &pVideoInfo->bmiHeader; // Convert a DDSURFACEDESC into a BITMAPINFOHEADER (see notes later). The // bit depth of the surface can be retrieved from the DDPIXELFORMAT field // in the DDpSurfaceDesc-> The documentation is a little misleading because // it says the field is permutations of DDBD_*'s however in this case the // field is initialised by DirectDraw to be the actual surface bit depth pbmiHeader->biSize = sizeof(BITMAPINFOHEADER); if (pSurfaceDesc->dwFlags & DDSD_PITCH) { pbmiHeader->biWidth = pSurfaceDesc->lPitch; // Convert the pitch from a byte count to a pixel count. // For some weird reason if the format is not a standard bit depth the // width field in the BITMAPINFOHEADER should be set to the number of // bytes instead of the width in pixels. This supports odd YUV formats // like IF09 which uses 9bpp. int bpp = pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount; if (bpp == 8 || bpp == 16 || bpp == 24 || bpp == 32) { pbmiHeader->biWidth /= (bpp / 8); // Divide by number of BYTES per pixel. } } else { pbmiHeader->biWidth = pSurfaceDesc->dwWidth; // BUGUBUG -- Do something odd here with strange YUV pixel formats? Or does it matter? } pbmiHeader->biHeight = pSurfaceDesc->dwHeight; if (bInvertSize) { pbmiHeader->biHeight = -pbmiHeader->biHeight; } pbmiHeader->biPlanes = 1; pbmiHeader->biBitCount = (USHORT) pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount; pbmiHeader->biCompression = pSurfaceDesc->ddpfPixelFormat.dwFourCC; //pbmiHeader->biXPelsPerMeter = 0; //pbmiHeader->biYPelsPerMeter = 0; //pbmiHeader->biClrUsed = 0; //pbmiHeader->biClrImportant = 0; // For true colour RGB formats tell the source there are bit fields // unless it's regular RGB555 // // Try to preserve BI_RGB for RGB32 from template in case // connection wasn't queried for a BI_BITFIELDS -> BI_RGB switch _ASSERTE(!pmtTemplate || pmtTemplate->formattype == FORMAT_VideoInfo); DWORD dwSrcComp = pmtTemplate ? ((VIDEOINFO *)pmtTemplate->pbFormat)->bmiHeader.biCompression : (DWORD)-1; if (pbmiHeader->biCompression == BI_RGB) { if (pbmiHeader->biBitCount == 16 && pMediaType->subtype != MEDIASUBTYPE_RGB555 || pbmiHeader->biBitCount == 32 && dwSrcComp == BI_BITFIELDS) { pbmiHeader->biCompression = BI_BITFIELDS; } } if (PALETTISED(pVideoInfo)) { pbmiHeader->biClrUsed = 1 << pbmiHeader->biBitCount; if (pPalette) { pPalette->GetEntries(0, 0, pbmiHeader->biClrUsed, (LPPALETTEENTRY)&pVideoInfo->bmiColors); for (unsigned int i = 0; i < pbmiHeader->biClrUsed; i++) { BYTE tempRed = pVideoInfo->bmiColors[i].rgbRed; pVideoInfo->bmiColors[i].rgbRed = pVideoInfo->bmiColors[i].rgbBlue; pVideoInfo->bmiColors[i].rgbBlue = tempRed; } } } // The RGB bit fields are in the same place as for YUV formats if (pbmiHeader->biCompression != BI_RGB) { pVideoInfo->dwBitMasks[0] = pSurfaceDesc->ddpfPixelFormat.dwRBitMask; pVideoInfo->dwBitMasks[1] = pSurfaceDesc->ddpfPixelFormat.dwGBitMask; pVideoInfo->dwBitMasks[2] = pSurfaceDesc->ddpfPixelFormat.dwBBitMask; } pbmiHeader->biSizeImage = DIBSIZE(*pbmiHeader); // Complete the rest of the VIDEOINFO fields //pVideoInfo->dwBitRate = 0; //pVideoInfo->dwBitErrorRate = 0; //pVideoInfo->AvgTimePerFrame = 0; // And finish it off with the other media type fields // pMediaType->formattype = FORMAT_VideoInfo; pMediaType->lSampleSize = pbmiHeader->biSizeImage; pMediaType->bFixedSizeSamples = TRUE; //pMediaType->bTemporalCompression = FALSE; // Initialise the source and destination rectangles if (pRect) { pVideoInfo->rcSource.right = pRect->right - pRect->left; pVideoInfo->rcSource.bottom = pRect->bottom - pRect->top; pVideoInfo->rcTarget = *pRect; } else { //pVideoInfo->rcTarget.left = pVideoInfo->rcTarget.top = 0; pVideoInfo->rcTarget.right = pSurfaceDesc->dwWidth; pVideoInfo->rcTarget.bottom = pSurfaceDesc->dwHeight; //pVideoInfo->rcSource.left = pVideoInfo->rcSource.top = 0; pVideoInfo->rcSource.right = pSurfaceDesc->dwWidth; pVideoInfo->rcSource.bottom = pSurfaceDesc->dwHeight; } *ppMediaType = pMediaType; return S_OK; } bool PixelFormatFromVideoSubtype(REFGUID refSubType, DDPIXELFORMAT *pPixelFormat) { for( int i = 0; i < sizeof(g_aFormats)/sizeof(g_aFormats[0]); i++ ) { if (*g_aFormats[i] == refSubType) { *pPixelFormat = g_aPixelFormats[i]; return TRUE; } } return FALSE; } HRESULT ConvertMediaTypeToSurfaceDesc(const AM_MEDIA_TYPE *pmt, IDirectDraw *pDD, IDirectDrawPalette **ppPalette, LPDDSURFACEDESC pSurfaceDesc) { *ppPalette = NULL; if (pmt->majortype != MEDIATYPE_Video || pmt->formattype != FORMAT_VideoInfo) { return VFW_E_TYPE_NOT_ACCEPTED; } VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat; BITMAPINFOHEADER *pbmiHeader = &pVideoInfo->bmiHeader; pSurfaceDesc->dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; // Should really look at rcTarget here if it's not empty but there are // very few valid cases where it makes sense so rather than risk // regressions we're not going to change it. pSurfaceDesc->dwHeight = (pbmiHeader->biHeight > 0) ? pbmiHeader->biHeight : -pbmiHeader->biHeight; pSurfaceDesc->dwWidth = pbmiHeader->biWidth; if (PixelFormatFromVideoSubtype(pmt->subtype, &pSurfaceDesc->ddpfPixelFormat)) { if (pDD && pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 8) { // // The RGBQUAD and PALETTEENTRY sturctures have Red and Blue swapped so // we can't do a simple memory copy. // PALETTEENTRY aPaletteEntry[256]; int iEntries = min(256, pVideoInfo->bmiHeader.biClrUsed); if (0 == iEntries && pmt->cbFormat >= (DWORD)FIELD_OFFSET(VIDEOINFO, bmiColors[256])) { iEntries = 256; } ZeroMemory(aPaletteEntry, sizeof(aPaletteEntry)); for (int i = 0; i < iEntries; i++) { aPaletteEntry[i].peRed = pVideoInfo->bmiColors[i].rgbRed; aPaletteEntry[i].peGreen = pVideoInfo->bmiColors[i].rgbGreen; aPaletteEntry[i].peBlue = pVideoInfo->bmiColors[i].rgbBlue; } return pDD->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, aPaletteEntry, ppPalette, NULL); } return S_OK; } else { return VFW_E_TYPE_NOT_ACCEPTED; } } // Helper to compare pixel formats bool ComparePixelFormats(const DDPIXELFORMAT *pFormat1, const DDPIXELFORMAT *pFormat2) { // Compare the flags if (pFormat1->dwSize != pFormat2->dwSize) { return false; } if ((pFormat1->dwFlags ^ pFormat2->dwFlags) & (DDPF_RGB | DDPF_PALETTEINDEXED8 | DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED2 | DDPF_PALETTEINDEXED1 | DDPF_PALETTEINDEXEDTO8 | DDPF_YUV) ) { return false; } return (0 == memcmp(&pFormat1->dwFourCC, &pFormat2->dwFourCC, FIELD_OFFSET(DDPIXELFORMAT, dwRGBAlphaBitMask) - FIELD_OFFSET(DDPIXELFORMAT, dwFourCC)) ); }