/* * Implements AVI Decompressor(CLSID_AVIDec). * * hidenori@a2.ctktv.ne.jp */ #include "config.h" #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "vfw.h" #include "strmif.h" #include "control.h" #include "amvideo.h" #include "vfwmsgs.h" #include "uuids.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(quartz); #include "quartz_private.h" #include "xform.h" static const WCHAR AVIDec_FilterName[] = {'A','V','I',' ','D','e','c','o','m','p','r','e','s','s','o','r',0}; typedef struct CAVIDecImpl { HIC hicCached; HIC hicTrans; AM_MEDIA_TYPE m_mtOut; BITMAPINFO* m_pbiIn; BITMAPINFO* m_pbiOut; BYTE* m_pOutBuf; } CAVIDecImpl; /*************************************************************************** * * CAVIDecImpl methods * */ static void AVIDec_ReleaseDIBBuffers(CAVIDecImpl* This) { TRACE("(%p)\n",This); if ( This->m_pbiIn != NULL ) { QUARTZ_FreeMem(This->m_pbiIn); This->m_pbiIn = NULL; } if ( This->m_pbiOut != NULL ) { QUARTZ_FreeMem(This->m_pbiOut); This->m_pbiOut = NULL; } if ( This->m_pOutBuf != NULL ) { QUARTZ_FreeMem(This->m_pOutBuf); This->m_pOutBuf = NULL; } } static BITMAPINFO* AVIDec_DuplicateBitmapInfo(const BITMAPINFO* pbi) { DWORD dwSize; BITMAPINFO* pbiRet; dwSize = pbi->bmiHeader.biSize; if ( dwSize < sizeof(BITMAPINFOHEADER) ) return NULL; if ( pbi->bmiHeader.biBitCount <= 8 ) { if ( pbi->bmiHeader.biClrUsed == 0 ) dwSize += sizeof(RGBQUAD)*(1<bmiHeader.biBitCount); else dwSize += sizeof(RGBQUAD)*pbi->bmiHeader.biClrUsed; } if ( pbi->bmiHeader.biCompression == 3 && dwSize == sizeof(BITMAPINFOHEADER) ) dwSize += sizeof(DWORD)*3; pbiRet = (BITMAPINFO*)QUARTZ_AllocMem(dwSize); if ( pbiRet != NULL ) memcpy( pbiRet, pbi, dwSize ); return pbiRet; } static HRESULT AVIDec_Init( CTransformBaseImpl* pImpl ) { CAVIDecImpl* This = pImpl->m_pUserData; TRACE("(%p)\n",This); if ( This != NULL ) return NOERROR; This = (CAVIDecImpl*)QUARTZ_AllocMem( sizeof(CAVIDecImpl) ); if ( This == NULL ) return E_OUTOFMEMORY; ZeroMemory( This, sizeof(CAVIDecImpl) ); pImpl->m_pUserData = This; /* construct */ This->hicCached = (HIC)NULL; This->hicTrans = (HIC)NULL; ZeroMemory( &This->m_mtOut, sizeof(AM_MEDIA_TYPE) ); This->m_pbiIn = NULL; This->m_pbiOut = NULL; This->m_pOutBuf = NULL; return NOERROR; } static HRESULT AVIDec_Cleanup( CTransformBaseImpl* pImpl ) { CAVIDecImpl* This = pImpl->m_pUserData; TRACE("(%p)\n",This); if ( This == NULL ) return NOERROR; /* destruct */ QUARTZ_MediaType_Free( &This->m_mtOut ); AVIDec_ReleaseDIBBuffers(This); if ( This->hicCached != (HIC)NULL ) ICClose(This->hicCached); if ( This->hicTrans != (HIC)NULL ) ICClose(This->hicTrans); QUARTZ_FreeMem( This ); pImpl->m_pUserData = NULL; return NOERROR; } static HRESULT AVIDec_CheckMediaType( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut ) { CAVIDecImpl* This = pImpl->m_pUserData; BITMAPINFO* pbiIn = NULL; BITMAPINFO* pbiOut = NULL; HIC hic; TRACE("(%p)\n",This); if ( This == NULL ) return E_UNEXPECTED; if ( !IsEqualGUID( &pmtIn->majortype, &MEDIATYPE_Video ) ) return E_FAIL; if ( !IsEqualGUID( &pmtIn->formattype, &FORMAT_VideoInfo ) ) return E_FAIL; pbiIn = (BITMAPINFO*)(&((VIDEOINFOHEADER*)pmtIn->pbFormat)->bmiHeader); if ( pmtOut != NULL ) { if ( !IsEqualGUID( &pmtOut->majortype, &MEDIATYPE_Video ) ) return E_FAIL; if ( !IsEqualGUID( &pmtOut->formattype, &FORMAT_VideoInfo ) ) return E_FAIL; pbiOut = (BITMAPINFO*)(&((VIDEOINFOHEADER*)pmtOut->pbFormat)->bmiHeader); } if ( This->hicCached != (HIC)NULL && ICDecompressQuery( This->hicCached, pbiIn, pbiOut ) == ICERR_OK ) { TRACE("supported format\n"); return NOERROR; } TRACE("try to find a decoder...\n"); hic = ICLocate( mmioFOURCC('V','I','D','C'), 0, &pbiIn->bmiHeader, &pbiOut->bmiHeader, ICMODE_DECOMPRESS ); if ( hic == (HIC)NULL ) { WARN("no decoder for %c%c%c%c\n", (int)(( pbiIn->bmiHeader.biCompression >> 0 ) & 0xff), (int)(( pbiIn->bmiHeader.biCompression >> 8 ) & 0xff), (int)(( pbiIn->bmiHeader.biCompression >> 16 ) & 0xff), (int)(( pbiIn->bmiHeader.biCompression >> 24 ) & 0xff) ); return E_FAIL; } TRACE("found\n"); if ( This->hicCached != (HIC)NULL ) ICClose(This->hicCached); This->hicCached = hic; return NOERROR; } static HRESULT AVIDec_GetOutputTypes( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE** ppmtAcceptTypes, ULONG* pcAcceptTypes ) { CAVIDecImpl* This = pImpl->m_pUserData; HRESULT hr; LONG cbFmt; BITMAPINFO* pbiIn = NULL; BITMAPINFO* pbiOut = NULL; TRACE("(%p)\n",This); hr = AVIDec_CheckMediaType( pImpl, pmtIn, NULL ); if ( FAILED(hr) ) return hr; TRACE("(%p) - get size of format\n",This); pbiIn = (BITMAPINFO*)(&((VIDEOINFOHEADER*)pmtIn->pbFormat)->bmiHeader); cbFmt = (LONG)ICDecompressGetFormatSize( This->hicCached, pbiIn ); if ( cbFmt < sizeof(BITMAPINFOHEADER) ) return E_FAIL; QUARTZ_MediaType_Free( &This->m_mtOut ); ZeroMemory( &This->m_mtOut, sizeof(AM_MEDIA_TYPE) ); memcpy( &This->m_mtOut.majortype, &MEDIATYPE_Video, sizeof(GUID) ); memcpy( &This->m_mtOut.formattype, &FORMAT_VideoInfo, sizeof(GUID) ); This->m_mtOut.cbFormat = sizeof(VIDEOINFOHEADER) + cbFmt + sizeof(RGBQUAD)*256; This->m_mtOut.pbFormat = (BYTE*)CoTaskMemAlloc(This->m_mtOut.cbFormat); if ( This->m_mtOut.pbFormat == NULL ) return E_OUTOFMEMORY; ZeroMemory( This->m_mtOut.pbFormat, This->m_mtOut.cbFormat ); pbiOut = (BITMAPINFO*)(&((VIDEOINFOHEADER*)This->m_mtOut.pbFormat)->bmiHeader); TRACE("(%p) - get format\n",This); if ( ICDecompressGetFormat( This->hicCached, pbiIn, pbiOut ) != ICERR_OK ) return E_FAIL; hr = QUARTZ_MediaSubType_FromBitmap( &This->m_mtOut.subtype, &pbiOut->bmiHeader ); if ( FAILED(hr) ) return hr; if ( hr != S_OK ) QUARTZ_MediaSubType_FromFourCC( &This->m_mtOut.subtype, pbiOut->bmiHeader.biCompression ); This->m_mtOut.bFixedSizeSamples = (pbiOut->bmiHeader.biCompression == 0) ? 1 : 0; This->m_mtOut.lSampleSize = (pbiOut->bmiHeader.biCompression == 0) ? DIBSIZE(pbiOut->bmiHeader) : pbiOut->bmiHeader.biSizeImage; /* get palette */ if ( pbiOut->bmiHeader.biBitCount <= 8 ) { TRACE("(%p) - get palette\n",This); if ( ICDecompressGetPalette( This->hicCached, pbiIn, pbiOut ) != ICERR_OK ) { TRACE("(%p) - use the input palette\n",This); if ( pbiIn->bmiHeader.biBitCount != pbiOut->bmiHeader.biBitCount ) { FIXME( "no palette...fixme?\n" ); return E_FAIL; } if ( pbiOut->bmiHeader.biClrUsed == 0 ) pbiOut->bmiHeader.biClrUsed = 1<bmiHeader.biBitCount; if ( pbiOut->bmiHeader.biClrUsed > (1<bmiHeader.biBitCount) ) { ERR( "biClrUsed=%ld\n", pbiOut->bmiHeader.biClrUsed ); return E_FAIL; } memcpy( pbiOut->bmiColors, pbiIn->bmiColors, sizeof(RGBQUAD) * pbiOut->bmiHeader.biClrUsed ); } } TRACE("(%p) - return format\n",This); *ppmtAcceptTypes = &This->m_mtOut; *pcAcceptTypes = 1; return NOERROR; } static HRESULT AVIDec_GetAllocProp( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut, ALLOCATOR_PROPERTIES* pProp, BOOL* pbTransInPlace, BOOL* pbTryToReuseSample ) { CAVIDecImpl* This = pImpl->m_pUserData; BITMAPINFO* pbiOut = NULL; HRESULT hr; TRACE("(%p)\n",This); if ( This == NULL ) return E_UNEXPECTED; hr = AVIDec_CheckMediaType( pImpl, pmtIn, pmtOut ); if ( FAILED(hr) ) return hr; pbiOut = (BITMAPINFO*)(&((VIDEOINFOHEADER*)pmtOut->pbFormat)->bmiHeader); pProp->cBuffers = 1; if ( pbiOut->bmiHeader.biCompression == 0 ) pProp->cbBuffer = DIBSIZE(pbiOut->bmiHeader); else pProp->cbBuffer = pbiOut->bmiHeader.biSizeImage; *pbTransInPlace = FALSE; *pbTryToReuseSample = TRUE; return NOERROR; } static HRESULT AVIDec_BeginTransform( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut, BOOL bReuseSample ) { CAVIDecImpl* This = pImpl->m_pUserData; BITMAPINFO* pbiIn = NULL; BITMAPINFO* pbiOut = NULL; HRESULT hr; TRACE("(%p,%p,%p,%d)\n",This,pmtIn,pmtOut,bReuseSample); if ( This == NULL || This->hicTrans != (HIC)NULL ) return E_UNEXPECTED; hr = AVIDec_CheckMediaType( pImpl, pmtIn, pmtOut ); if ( FAILED(hr) ) return hr; AVIDec_ReleaseDIBBuffers(This); pbiIn = (BITMAPINFO*)(&((VIDEOINFOHEADER*)pmtIn->pbFormat)->bmiHeader); pbiOut = (BITMAPINFO*)(&((VIDEOINFOHEADER*)pmtOut->pbFormat)->bmiHeader); This->m_pbiIn = AVIDec_DuplicateBitmapInfo(pbiIn); This->m_pbiOut = AVIDec_DuplicateBitmapInfo(pbiOut); if ( This->m_pbiIn == NULL || This->m_pbiOut == NULL ) return E_OUTOFMEMORY; if ( This->m_pbiOut->bmiHeader.biCompression == 0 || This->m_pbiOut->bmiHeader.biCompression == 3 ) This->m_pbiOut->bmiHeader.biSizeImage = DIBSIZE(This->m_pbiOut->bmiHeader); if ( !bReuseSample ) { This->m_pOutBuf = QUARTZ_AllocMem(This->m_pbiOut->bmiHeader.biSizeImage); if ( This->m_pOutBuf == NULL ) return E_OUTOFMEMORY; ZeroMemory( This->m_pOutBuf, This->m_pbiOut->bmiHeader.biSizeImage ); } if ( ICERR_OK != ICDecompressBegin( This->hicCached, This->m_pbiIn, This->m_pbiOut ) ) return E_FAIL; This->hicTrans = This->hicCached; This->hicCached = (HIC)NULL; return NOERROR; } static HRESULT AVIDec_Transform( CTransformBaseImpl* pImpl, IMediaSample* pSampIn, IMediaSample* pSampOut ) { CAVIDecImpl* This = pImpl->m_pUserData; DWORD dwFlags; BYTE* pDataIn = NULL; BYTE* pDataOut = NULL; HRESULT hr; TRACE("(%p)\n",This); if ( This == NULL || pSampOut == NULL || This->hicTrans == (HIC)NULL || This->m_pbiIn == NULL || This->m_pbiOut == NULL ) return E_UNEXPECTED; hr = IMediaSample_GetPointer( pSampIn, &pDataIn ); if ( FAILED(hr) ) return hr; hr = IMediaSample_GetPointer( pSampOut, &pDataOut ); if ( FAILED(hr) ) return hr; dwFlags = 0; /*** FIXME!!! * * if ( IMediaSample_IsSyncPoint(pSampIn) != S_OK ) * dwFlags |= ICDECOMPRESS_NOTKEYFRAME; ****/ if ( IMediaSample_IsPreroll(pSampIn) == S_OK ) dwFlags |= ICDECOMPRESS_PREROLL; if ( ICERR_OK != ICDecompress( This->hicTrans, dwFlags, &This->m_pbiIn->bmiHeader, pDataIn, &This->m_pbiOut->bmiHeader, ( This->m_pOutBuf != NULL ) ? This->m_pOutBuf : pDataOut ) ) return E_FAIL; if ( This->m_pOutBuf != NULL ) memcpy( pDataOut, This->m_pOutBuf, This->m_pbiOut->bmiHeader.biSizeImage ); return NOERROR; } static HRESULT AVIDec_EndTransform( CTransformBaseImpl* pImpl ) { CAVIDecImpl* This = pImpl->m_pUserData; TRACE("(%p)\n",This); if ( This == NULL ) return E_UNEXPECTED; if ( This->hicTrans == (HIC)NULL ) return NOERROR; ICDecompressEnd(This->hicTrans); if ( This->hicCached != (HIC)NULL ) ICClose(This->hicCached); This->hicCached = This->hicTrans; This->hicTrans = (HIC)NULL; AVIDec_ReleaseDIBBuffers(This); return NOERROR; } static const TransformBaseHandlers transhandlers = { AVIDec_Init, AVIDec_Cleanup, AVIDec_CheckMediaType, AVIDec_GetOutputTypes, AVIDec_GetAllocProp, AVIDec_BeginTransform, AVIDec_Transform, AVIDec_EndTransform, }; HRESULT QUARTZ_CreateAVIDec(IUnknown* punkOuter,void** ppobj) { return QUARTZ_CreateTransformBase( punkOuter,ppobj, &CLSID_AVIDec, AVIDec_FilterName, NULL, NULL, &transhandlers ); }