| /* |
| * Copyright 2002 Michael Günnewig |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <assert.h> |
| |
| #include "winbase.h" |
| #include "winnls.h" |
| #include "windowsx.h" |
| #include "vfw.h" |
| |
| #include "avifile_private.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(avifile); |
| |
| #ifndef DIBPTR |
| #define DIBPTR(lp) ((LPBYTE)(lp) + (lp)->biSize + \ |
| (lp)->biClrUsed * sizeof(RGBQUAD)) |
| #endif |
| |
| /***********************************************************************/ |
| |
| 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) igetframeVtbl = { |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE |
| IGetFrame_fnQueryInterface, |
| IGetFrame_fnAddRef, |
| IGetFrame_fnRelease, |
| IGetFrame_fnGetFrame, |
| IGetFrame_fnBegin, |
| IGetFrame_fnEnd, |
| IGetFrame_fnSetFormat |
| }; |
| |
| typedef struct _IGetFrameImpl { |
| /* IUnknown stuff */ |
| ICOM_VFIELD(IGetFrame); |
| DWORD ref; |
| |
| /* IGetFrame stuff */ |
| BOOL bFixedStream; |
| PAVISTREAM pStream; |
| |
| LPVOID lpInBuffer; |
| DWORD cbInBuffer; |
| LPBITMAPINFOHEADER lpInFormat; |
| DWORD cbInFormat; |
| |
| LONG lCurrentFrame; |
| LPBITMAPINFOHEADER lpOutFormat; |
| LPVOID lpOutBuffer; |
| |
| HIC hic; |
| BOOL bResize; |
| DWORD x; |
| DWORD y; |
| DWORD dx; |
| DWORD dy; |
| |
| BOOL bFormatChanges; |
| DWORD dwFormatChangeCount; |
| DWORD dwEditCount; |
| } IGetFrameImpl; |
| |
| /***********************************************************************/ |
| |
| static void AVIFILE_CloseCompressor(IGetFrameImpl *This) |
| { |
| if (This->lpOutFormat != NULL && This->lpInFormat != This->lpOutFormat) { |
| GlobalFreePtr(This->lpOutFormat); |
| This->lpOutFormat = NULL; |
| } |
| if (This->lpInFormat != NULL) { |
| GlobalFreePtr(This->lpInFormat); |
| This->lpInFormat = NULL; |
| } |
| if (This->hic != (HIC)NULL) { |
| if (This->bResize) |
| ICDecompressExEnd(This->hic); |
| else |
| ICDecompressEnd(This->hic); |
| ICClose(This->hic); |
| This->hic = (HIC)NULL; |
| } |
| } |
| |
| PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream) |
| { |
| IGetFrameImpl *pg; |
| |
| /* check parameter */ |
| if (pStream == NULL) |
| return NULL; |
| |
| pg = (IGetFrameImpl*)LocalAlloc(LPTR, sizeof(IGetFrameImpl)); |
| if (pg != NULL) { |
| ICOM_VTBL(pg) = &igetframeVtbl; |
| pg->ref = 1; |
| pg->lCurrentFrame = -1; |
| pg->pStream = pStream; |
| IAVIStream_AddRef(pStream); |
| } |
| |
| return (PGETFRAME)pg; |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface, |
| REFIID refiid, LPVOID *obj) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); |
| |
| if (IsEqualGUID(&IID_IUnknown, refiid) || |
| IsEqualGUID(&IID_IGetFrame, refiid)) { |
| *obj = iface; |
| return S_OK; |
| } |
| |
| return OLE_E_ENUM_NOMORE; |
| } |
| |
| static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame *iface) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p)\n", iface); |
| |
| return ++(This->ref); |
| } |
| |
| static ULONG WINAPI IGetFrame_fnRelease(IGetFrame *iface) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p)\n", iface); |
| |
| if (!--(This->ref)) { |
| AVIFILE_CloseCompressor(This); |
| if (This->pStream != NULL) { |
| AVIStreamRelease(This->pStream); |
| This->pStream = NULL; |
| } |
| |
| LocalFree((HLOCAL)iface); |
| return 0; |
| } |
| |
| return This->ref; |
| } |
| |
| static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| LONG readBytes; |
| LONG readSamples; |
| |
| TRACE("(%p,%ld)\n", iface, lPos); |
| |
| /* check state */ |
| if (This->pStream == NULL) |
| return NULL; |
| if (This->lpInFormat == NULL) |
| return NULL; |
| |
| /* Could stream have changed? */ |
| if (! This->bFixedStream) { |
| AVISTREAMINFOW sInfo; |
| |
| IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo)); |
| |
| if (sInfo.dwEditCount != This->dwEditCount) { |
| This->dwEditCount = sInfo.dwEditCount; |
| This->lCurrentFrame = -1; |
| } |
| |
| if (sInfo.dwFormatChangeCount != This->dwFormatChangeCount) { |
| /* stream has changed */ |
| if (This->lpOutFormat != NULL) { |
| BITMAPINFOHEADER bi; |
| |
| memcpy(&bi, This->lpOutFormat, sizeof(bi)); |
| AVIFILE_CloseCompressor(This); |
| |
| if (FAILED(IGetFrame_SetFormat(iface, &bi, NULL, 0, 0, -1, -1))) { |
| if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1))) |
| return NULL; |
| } |
| } else if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1))) |
| return NULL; |
| } |
| } |
| |
| if (lPos != This->lCurrentFrame) { |
| LONG lNext = AVIStreamFindSample(This->pStream, lPos, FIND_KEY|FIND_PREV); |
| |
| if (lNext == -1) |
| lNext = 0; /* first frame is always a keyframe */ |
| if (lNext <= This->lCurrentFrame && This->lCurrentFrame < lPos) |
| lNext++; |
| |
| for (; lNext < lPos; lNext++) { |
| /* new format for this frame? */ |
| if (This->bFormatChanges) { |
| AVIStreamReadFormat(This->pStream, lNext, This->lpInFormat, &This->cbInFormat); |
| if (This->lpOutFormat != NULL) { |
| if (This->lpOutFormat->biBitCount <= 8) |
| ICDecompressGetPalette(This->hic, This->lpInFormat, |
| This->lpOutFormat); |
| } |
| } |
| |
| /* read input frame */ |
| while (FAILED(AVIStreamRead(This->pStream, lNext, 1, This->lpInBuffer, |
| This->cbInBuffer, &readBytes, &readSamples))) { |
| /* not enough memory for input buffer? */ |
| readBytes = 0; |
| if (FAILED(AVIStreamSampleSize(This->pStream, lNext, &readBytes))) |
| return NULL; |
| if (This->cbInBuffer >= readBytes) |
| break; |
| This->lpInFormat = GlobalReAllocPtr(This->lpInFormat, This->cbInFormat + readBytes, 0); |
| if (This->lpInFormat == NULL) |
| return NULL; |
| This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat; |
| } |
| |
| if (readSamples != 1) |
| return NULL; |
| if (readBytes != 0) { |
| This->lpInFormat->biSizeImage = readBytes; |
| |
| /* nothing to decompress? */ |
| if (This->hic == (HIC)NULL) { |
| This->lCurrentFrame = lPos; |
| return This->lpInFormat; |
| } |
| |
| if (This->bResize) { |
| ICDecompressEx(This->hic,0,This->lpInFormat,This->lpInBuffer,0,0, |
| This->lpInFormat->biWidth,This->lpInFormat->biHeight, |
| This->lpOutFormat,This->lpOutBuffer,This->x,This->y, |
| This->dx,This->dy); |
| } else { |
| ICDecompress(This->hic, 0, This->lpInFormat, This->lpInBuffer, |
| This->lpOutFormat, This->lpOutBuffer); |
| } |
| } |
| } /* for (lNext < lPos) */ |
| } /* if (This->lCurrentFrame != lPos) */ |
| |
| return (This->hic == (HIC)NULL ? This->lpInFormat : This->lpOutFormat); |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart, |
| LONG lEnd, LONG lRate) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p,%ld,%ld,%ld)\n", iface, lStart, lEnd, lRate); |
| |
| This->bFixedStream = TRUE; |
| |
| return (IGetFrame_GetFrame(iface, lStart) ? AVIERR_OK : AVIERR_ERROR); |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| TRACE("(%p)\n", iface); |
| |
| This->bFixedStream = FALSE; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface, |
| LPBITMAPINFOHEADER lpbiWanted, |
| LPVOID lpBits, INT x, INT y, |
| INT dx, INT dy) |
| { |
| ICOM_THIS(IGetFrameImpl,iface); |
| |
| AVISTREAMINFOW sInfo; |
| LPBITMAPINFOHEADER lpbi = lpbiWanted; |
| BOOL bBestDisplay = FALSE; |
| |
| TRACE("(%p,%p,%p,%d,%d,%d,%d)\n", iface, lpbiWanted, lpBits, |
| x, y, dx, dy); |
| |
| if (This->pStream == NULL) |
| return AVIERR_ERROR; |
| |
| if ((LONG)lpbiWanted == AVIGETFRAMEF_BESTDISPLAYFMT) { |
| lpbi = NULL; |
| bBestDisplay = TRUE; |
| } |
| |
| IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo)); |
| if (sInfo.fccType != streamtypeVIDEO) |
| return AVIERR_UNSUPPORTED; |
| |
| This->bFormatChanges = |
| (sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES ? TRUE : FALSE ); |
| This->dwFormatChangeCount = sInfo.dwFormatChangeCount; |
| This->dwEditCount = sInfo.dwEditCount; |
| This->lCurrentFrame = -1; |
| |
| /* get input format from stream */ |
| if (This->lpInFormat == NULL) { |
| This->cbInBuffer = sInfo.dwSuggestedBufferSize; |
| if (This->cbInBuffer == 0) |
| This->cbInBuffer = 1024; |
| |
| AVIStreamFormatSize(This->pStream, sInfo.dwStart, &This->cbInFormat); |
| |
| This->lpInFormat = |
| (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, This->cbInFormat + This->cbInBuffer); |
| if (This->lpInFormat == NULL) { |
| AVIFILE_CloseCompressor(This); |
| return AVIERR_MEMORY; |
| } |
| |
| AVIStreamReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat); |
| |
| This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat; |
| } |
| |
| /* check input format */ |
| if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8) |
| This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount; |
| if (This->lpInFormat->biSizeImage == 0 && |
| This->lpInFormat->biCompression == BI_RGB) { |
| This->lpInFormat->biSizeImage = |
| DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight; |
| } |
| |
| /* only to pass through? */ |
| if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) { |
| if (lpbi == NULL || |
| (lpbi->biCompression == BI_RGB && |
| lpbi->biWidth == This->lpInFormat->biWidth && |
| lpbi->biHeight == This->lpInFormat->biHeight && |
| lpbi->biBitCount == This->lpInFormat->biBitCount)) { |
| This->lpOutFormat = This->lpInFormat; |
| This->lpOutBuffer = DIBPTR(This->lpInFormat); |
| return AVIERR_OK; |
| } |
| } |
| |
| /* need memory for output format? */ |
| if (This->lpOutFormat == NULL) { |
| This->lpOutFormat = |
| (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, sizeof(BITMAPINFOHEADER) |
| + 256 * sizeof(RGBQUAD)); |
| if (This->lpOutFormat == NULL) { |
| AVIFILE_CloseCompressor(This); |
| return AVIERR_MEMORY; |
| } |
| } |
| |
| /* need handle to video compressor */ |
| if (This->hic == (HIC)NULL) { |
| FOURCC fccHandler; |
| |
| if (This->lpInFormat->biCompression == BI_RGB) |
| fccHandler = comptypeDIB; |
| else if (This->lpInFormat->biCompression == BI_RLE8) |
| fccHandler = mmioFOURCC('R','L','E',' '); |
| else |
| fccHandler = sInfo.fccHandler; |
| |
| if (lpbi != NULL) { |
| if (lpbi->biWidth == 0) |
| lpbi->biWidth = This->lpInFormat->biWidth; |
| if (lpbi->biHeight == 0) |
| lpbi->biHeight = This->lpInFormat->biHeight; |
| } |
| |
| This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS); |
| if (This->hic == (HIC)NULL) { |
| AVIFILE_CloseCompressor(This); |
| return AVIERR_NOCOMPRESSOR; |
| } |
| } |
| |
| /* output format given? */ |
| if (lpbi != NULL) { |
| /* check the given output format ... */ |
| if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) |
| lpbi->biClrUsed = 1u << lpbi->biBitCount; |
| |
| /* ... and remember it */ |
| memcpy(This->lpOutFormat, lpbi, |
| lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD)); |
| if (lpbi->biBitCount <= 8) |
| ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat); |
| |
| return AVIERR_OK; |
| } else { |
| if (bBestDisplay) { |
| ICGetDisplayFormat(This->hic, This->lpInFormat, |
| This->lpOutFormat, 0, dx, dy); |
| } else if (ICDecompressGetFormat(This->hic, This->lpInFormat, |
| This->lpOutFormat) < 0) { |
| AVIFILE_CloseCompressor(This); |
| return AVIERR_NOCOMPRESSOR; |
| } |
| |
| /* check output format */ |
| if (This->lpOutFormat->biClrUsed == 0 && |
| This->lpOutFormat->biBitCount <= 8) |
| This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount; |
| if (This->lpOutFormat->biSizeImage == 0 && |
| This->lpOutFormat->biCompression == BI_RGB) { |
| This->lpOutFormat->biSizeImage = |
| DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight; |
| } |
| |
| if (lpBits == NULL) { |
| register DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD); |
| |
| size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage; |
| This->lpOutFormat = |
| (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpOutFormat, size, GMEM_MOVEABLE); |
| if (This->lpOutFormat == NULL) { |
| AVIFILE_CloseCompressor(This); |
| return AVIERR_MEMORY; |
| } |
| This->lpOutBuffer = DIBPTR(This->lpOutFormat); |
| } else |
| This->lpOutBuffer = lpBits; |
| |
| /* for user size was irrelevant */ |
| if (dx == -1) |
| dx = This->lpOutFormat->biWidth; |
| if (dy == -1) |
| dy = This->lpOutFormat->biHeight; |
| |
| /* need to resize? */ |
| if (x != 0 || y != 0) { |
| if (dy == This->lpOutFormat->biHeight && |
| dx == This->lpOutFormat->biWidth) |
| This->bResize = FALSE; |
| else |
| This->bResize = TRUE; |
| } |
| |
| if (This->bResize) { |
| This->x = x; |
| This->y = y; |
| This->dx = dx; |
| This->dy = dy; |
| |
| if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0, |
| 0,This->lpInFormat->biWidth, |
| This->lpInFormat->biHeight,This->lpOutFormat, |
| This->lpOutBuffer, x, y, dx, dy) == ICERR_OK) |
| return AVIERR_OK; |
| } else if (ICDecompressBegin(This->hic, This->lpInFormat, |
| This->lpOutFormat) == ICERR_OK) |
| return AVIERR_OK; |
| |
| AVIFILE_CloseCompressor(This); |
| |
| return AVIERR_COMPRESSOR; |
| } |
| } |
| |
| /***********************************************************************/ |