444 lines
11 KiB
C
444 lines
11 KiB
C
/*
|
|
* 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<<pbi->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<<pbiOut->bmiHeader.biBitCount;
|
|
if ( pbiOut->bmiHeader.biClrUsed > (1<<pbiOut->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 );
|
|
}
|
|
|