| /* |
| * Copyright 2001 Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp> |
| * |
| * FIXME - implements color space(depth) converter. |
| */ |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| |
| #include "winbase.h" |
| #include "winnls.h" |
| #include "mmsystem.h" |
| #include "winerror.h" |
| #include "vfw.h" |
| #include "debugtools.h" |
| #include "avifile_private.h" |
| |
| DEFAULT_DEBUG_CHANNEL(avifile); |
| |
| static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame* iface,REFIID refiid,LPVOID *obj); |
| static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame* iface); |
| static ULONG WINAPI IGetFrame_fnRelease(IGetFrame* iface); |
| static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame* iface,LONG lPos); |
| static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame* iface,LONG lStart,LONG lEnd,LONG lRate); |
| static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame* iface); |
| static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame* iface,LPBITMAPINFOHEADER lpbi,LPVOID lpBits,INT x,INT y,INT dx,INT dy); |
| |
| struct ICOM_VTABLE(IGetFrame) igetfrm = { |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE |
| IGetFrame_fnQueryInterface, |
| IGetFrame_fnAddRef, |
| IGetFrame_fnRelease, |
| IGetFrame_fnGetFrame, |
| IGetFrame_fnBegin, |
| IGetFrame_fnEnd, |
| IGetFrame_fnSetFormat, |
| }; |
| |
| typedef struct IGetFrameImpl |
| { |
| ICOM_VFIELD(IGetFrame); |
| /* IUnknown stuff */ |
| DWORD ref; |
| /* IGetFrame stuff */ |
| IAVIStream* pas; |
| HIC hIC; |
| LONG lCachedFrame; |
| BITMAPINFO* pbiICIn; |
| BITMAPINFO* pbiICOut; |
| LPVOID pvICOutBits; |
| LPVOID pvICInFmtBuf; |
| DWORD dwICInDataBufSize; |
| LPVOID pvICInDataBuf; |
| LPVOID pvICOutBuf; |
| } IGetFrameImpl; |
| |
| static HRESULT IGetFrame_Construct( IGetFrameImpl* This, |
| IAVIStream* pstr, |
| LPBITMAPINFOHEADER lpbi ); |
| static void IGetFrame_Destruct( IGetFrameImpl* This ); |
| |
| |
| |
| |
| static LPVOID AVIFILE_IGetFrame_DecodeFrame(IGetFrameImpl* This,LONG lPos) |
| { |
| HRESULT hr; |
| DWORD dwRes; |
| LONG lFrameLength; |
| LONG lSampleCount; |
| ICDECOMPRESS icd; |
| |
| if ( This->hIC == (HIC)NULL ) |
| return NULL; |
| |
| hr = IAVIStream_Read(This->pas,lPos,1,NULL,0, |
| &lFrameLength,&lSampleCount); |
| if ( hr != S_OK || lSampleCount <= 0 ) |
| { |
| FIXME( "IAVIStream_Read failed! res = %08lx\n", hr ); |
| return NULL; |
| } |
| TRACE( "frame length = %ld\n", lFrameLength ); |
| |
| if ( This->dwICInDataBufSize < lFrameLength ) |
| { |
| LPVOID lpv; |
| |
| if ( This->pvICInDataBuf == NULL ) |
| { |
| lpv = HeapAlloc( |
| AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, |
| lFrameLength ); |
| } |
| else |
| { |
| lpv = HeapReAlloc( |
| AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, |
| This->pvICInDataBuf,lFrameLength ); |
| } |
| if ( lpv == NULL ) |
| { |
| ERR( "out of memory!\n" ); |
| return NULL; |
| } |
| This->pvICInDataBuf = lpv; |
| This->dwICInDataBufSize = lFrameLength; |
| } |
| |
| hr = IAVIStream_Read(This->pas,lPos,1, |
| This->pvICInDataBuf,This->dwICInDataBufSize, |
| &lFrameLength,&lSampleCount); |
| if ( hr != S_OK || lSampleCount <= 0 ) |
| { |
| FIXME( "IAVIStream_Read to buffer failed! res = %08lx\n", hr ); |
| return NULL; |
| } |
| |
| This->pbiICIn->bmiHeader.biSizeImage = lFrameLength; |
| |
| TRACE( "call ICM_DECOMPRESS\n" ); |
| icd.dwFlags = (*(BYTE*)This->pvICInDataBuf) == 'c' ? |
| ICDECOMPRESS_NOTKEYFRAME : 0; |
| icd.lpbiInput = &This->pbiICIn->bmiHeader; |
| icd.lpInput = (BYTE*)This->pvICInDataBuf + 8; |
| icd.lpbiOutput = &This->pbiICOut->bmiHeader; |
| icd.lpOutput = This->pvICOutBits; |
| icd.ckid = *((DWORD*)This->pvICInDataBuf); |
| dwRes = ICSendMessage(This->hIC,ICM_DECOMPRESS, |
| (DWORD)(&icd),sizeof(ICDECOMPRESS) ); |
| TRACE( "returned from ICM_DECOMPRESS\n" ); |
| if ( dwRes != ICERR_OK ) |
| { |
| ERR( "ICDecompress failed!\n" ); |
| return NULL; |
| } |
| |
| This->lCachedFrame = lPos; |
| |
| return This->pvICOutBits; |
| } |
| |
| /****************************************************************************/ |
| |
| HRESULT AVIFILE_CreateIGetFrame(void** ppobj, |
| IAVIStream* pstr,LPBITMAPINFOHEADER lpbi) |
| { |
| IGetFrameImpl *This; |
| HRESULT hr; |
| |
| *ppobj = NULL; |
| This = (IGetFrameImpl*)HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, |
| sizeof(IGetFrameImpl)); |
| This->ref = 1; |
| ICOM_VTBL(This) = &igetfrm; |
| hr = IGetFrame_Construct( This, pstr, lpbi ); |
| if ( hr != S_OK ) |
| { |
| IGetFrame_Destruct( This ); |
| return hr; |
| } |
| |
| *ppobj = (LPVOID)This; |
| |
| return S_OK; |
| } |
| |
| /**************************************************************************** |
| * IUnknown interface |
| */ |
| |
| static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame* iface,REFIID refiid,LPVOID *obj) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(refiid),obj); |
| if ( IsEqualGUID(&IID_IUnknown,refiid) || |
| IsEqualGUID(&IID_IGetFrame,refiid) ) |
| { |
| IGetFrame_AddRef(iface); |
| *obj = iface; |
| return S_OK; |
| } |
| |
| return OLE_E_ENUM_NOMORE; |
| } |
| |
| static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame* iface) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p)->AddRef()\n",iface); |
| return ++(This->ref); |
| } |
| |
| static ULONG WINAPI IGetFrame_fnRelease(IGetFrame* iface) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p)->Release()\n",iface); |
| if ((--(This->ref)) > 0 ) |
| return This->ref; |
| IGetFrame_Destruct(This); |
| if ( This->pas != NULL ) |
| IAVIStream_Release( This->pas ); |
| |
| HeapFree(AVIFILE_data.hHeap,0,iface); |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * IGetFrrame interface |
| */ |
| |
| static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame* iface,LONG lPos) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| LPVOID lpv; |
| LONG lKeyFrame; |
| |
| TRACE( "(%p)->(%ld)\n", This, lPos ); |
| |
| if ( lPos < 0 ) |
| return NULL; |
| |
| if ( This->lCachedFrame == lPos ) |
| return This->pvICOutBits; |
| if ( (This->lCachedFrame+1) != lPos ) |
| { |
| lKeyFrame = IAVIStream_FindSample( This->pas, lPos, |
| FIND_KEY | FIND_PREV ); |
| if ( lKeyFrame < 0 || lKeyFrame > lPos ) |
| return NULL; |
| while ( ++lKeyFrame < lPos ) |
| { |
| lpv = AVIFILE_IGetFrame_DecodeFrame(This, lKeyFrame); |
| if ( lpv == NULL ) |
| return NULL; |
| } |
| } |
| |
| lpv = AVIFILE_IGetFrame_DecodeFrame(This, lPos); |
| TRACE( "lpv = %p\n",lpv ); |
| if ( lpv == NULL ) |
| return NULL; |
| |
| return lpv; |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame* iface,LONG lStart,LONG lEnd,LONG lRate) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE( "(%p)->(%ld,%ld,%ld)\n", This, lStart, lEnd, lRate ); |
| |
| if ( This->hIC == (HIC)NULL ) |
| return E_UNEXPECTED; |
| |
| if ( ICDecompressBegin( This->hIC, |
| This->pbiICIn, |
| This->pbiICOut ) != ICERR_OK ) |
| return E_FAIL; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame* iface) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE( "(%p)->()\n", This ); |
| |
| if ( This->hIC == (HIC)NULL ) |
| return E_UNEXPECTED; |
| |
| if ( ICDecompressEnd( This->hIC ) != ICERR_OK ) |
| return E_FAIL; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame* iface,LPBITMAPINFOHEADER lpbi,LPVOID lpBits,INT x,INT y,INT dx,INT dy) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| HRESULT hr; |
| LONG fmtlen; |
| BITMAPINFOHEADER biTemp; |
| DWORD dwSizeImage; |
| |
| FIXME( "(%p)->(%p,%p,%d,%d,%d,%d)\n",This,lpbi,lpBits,x,y,dx,dy ); |
| |
| IGetFrame_Destruct(This); |
| |
| hr = IAVIStream_ReadFormat(This->pas,0,NULL,&fmtlen); |
| if ( hr != S_OK ) |
| return hr; |
| This->pvICInFmtBuf = HeapAlloc( |
| AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,fmtlen); |
| if ( This->pvICInFmtBuf == NULL ) |
| return AVIERR_MEMORY; |
| hr = IAVIStream_ReadFormat(This->pas,0,This->pvICInFmtBuf,&fmtlen); |
| if ( hr != S_OK ) |
| return hr; |
| This->pbiICIn = (LPBITMAPINFO)This->pvICInFmtBuf; |
| |
| This->hIC = (HIC)ICOpen( ICTYPE_VIDEO, |
| This->pbiICIn->bmiHeader.biCompression, |
| ICMODE_DECOMPRESS ); |
| if ( This->hIC == (HIC)NULL ) |
| { |
| ERR( "no AVI decompressor for %c%c%c%c.\n", |
| (int)(This->pbiICIn->bmiHeader.biCompression>> 0)&0xff, |
| (int)(This->pbiICIn->bmiHeader.biCompression>> 8)&0xff, |
| (int)(This->pbiICIn->bmiHeader.biCompression>>16)&0xff, |
| (int)(This->pbiICIn->bmiHeader.biCompression>>24)&0xff ); |
| return E_FAIL; |
| } |
| |
| if ( lpbi == NULL || lpbi == ((LPBITMAPINFOHEADER)1) ) |
| { |
| memset( &biTemp, 0, sizeof(biTemp) ); |
| biTemp.biSize = sizeof(BITMAPINFOHEADER); |
| biTemp.biWidth = This->pbiICIn->bmiHeader.biWidth; |
| biTemp.biHeight = This->pbiICIn->bmiHeader.biHeight; |
| biTemp.biPlanes = 1; |
| biTemp.biBitCount = 24; |
| biTemp.biCompression = 0; |
| lpbi = &biTemp; |
| } |
| |
| if ( lpbi->biPlanes != 1 || lpbi->biCompression != 0 ) |
| return E_FAIL; |
| |
| dwSizeImage = |
| ((This->pbiICIn->bmiHeader.biWidth*lpbi->biBitCount+7)/8)* |
| This->pbiICIn->bmiHeader.biHeight; |
| This->pvICOutBuf = HeapAlloc( |
| AVIFILE_data.hHeap,HEAP_ZERO_MEMORY, |
| (sizeof(BITMAPINFO)+sizeof(RGBQUAD)*256)*2+ |
| dwSizeImage ); |
| if ( This->pvICOutBuf == NULL ) |
| return AVIERR_MEMORY; |
| |
| This->pbiICOut = (BITMAPINFO*)This->pvICOutBuf; |
| This->pvICOutBits = (LPVOID)( (BYTE*)This->pvICOutBuf + |
| sizeof(BITMAPINFO) + sizeof(RGBQUAD)*256 ); |
| |
| This->pbiICOut->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
| This->pbiICOut->bmiHeader.biWidth = This->pbiICIn->bmiHeader.biWidth; |
| This->pbiICOut->bmiHeader.biHeight = This->pbiICIn->bmiHeader.biHeight; |
| This->pbiICOut->bmiHeader.biPlanes = 1; |
| This->pbiICOut->bmiHeader.biBitCount = lpbi->biBitCount; |
| This->pbiICOut->bmiHeader.biSizeImage = dwSizeImage; |
| memcpy( This->pvICOutBits, This->pbiICOut, sizeof(BITMAPINFOHEADER) ); |
| |
| return S_OK; |
| } |
| |
| static HRESULT IGetFrame_Construct( IGetFrameImpl* This, |
| IAVIStream* pstr, |
| LPBITMAPINFOHEADER lpbi ) |
| { |
| HRESULT hr; |
| |
| TRACE( "(%p)->(%p,%p)\n",This,pstr,lpbi ); |
| |
| IAVIStream_AddRef( pstr ); |
| This->pas = pstr; |
| This->hIC = (HIC)NULL; |
| This->lCachedFrame = -1L; |
| This->pbiICIn = NULL; |
| This->pbiICOut = NULL; |
| This->pvICInFmtBuf = NULL; |
| This->pvICInDataBuf = NULL; |
| This->dwICInDataBufSize = 0; |
| This->pvICOutBuf = NULL; |
| |
| hr = IGetFrame_SetFormat((IGetFrame*)This,lpbi,NULL,0,0,0,0); |
| if ( hr != S_OK ) |
| return hr; |
| |
| return S_OK; |
| } |
| |
| static void IGetFrame_Destruct( IGetFrameImpl* This ) |
| { |
| if ( This->hIC != (HIC)NULL ) |
| { |
| ICClose( This->hIC ); |
| This->hIC = (HIC)NULL; |
| } |
| if ( This->pvICInFmtBuf != NULL ) |
| { |
| HeapFree( AVIFILE_data.hHeap, 0, This->pvICInFmtBuf ); |
| This->pvICInFmtBuf = NULL; |
| } |
| if ( This->pvICInDataBuf != NULL ) |
| { |
| HeapFree( AVIFILE_data.hHeap, 0, This->pvICInDataBuf ); |
| This->pvICInDataBuf = NULL; |
| } |
| if ( This->pvICOutBuf != NULL ) |
| { |
| HeapFree( AVIFILE_data.hHeap, 0, This->pvICOutBuf ); |
| This->pvICOutBuf = NULL; |
| } |
| |
| This->lCachedFrame = -1L; |
| This->pbiICIn = NULL; |
| This->pbiICOut = NULL; |
| This->dwICInDataBufSize = 0; |
| } |