Hi. The following patch fixes the following issues: include/vfw.h - added macros for AVIFileClose and AVIStreamClose. dlls/avifil32/api.c - Fixed usage of handler instead of type in AVIMakeCompressedStream. - implemented AVIBuildFilterA - started to implement AVIBuildFilterW dlls/avifile32/avifile_De.rc,dlls/avifil32/avifile_En.rc - added strings for AVIBuildFilterW functions. dlls/avifile32/avifile.c - Fixed many bugs which prevents writing. - Fixed segfault while parsing invalid index. - Keep parsing of index even if streamheader says it's empty - Fixed bug in AVIFILE_SaveIndex when stream is empty. - Fixed bug in AVIFILE_WriteBlock, when chunk is not of even size. dlls/avifil32/acmfile.c - New AVI streamhandler for audio streams dlls/avifil32/icmfile.c - New AVI streamhandler for video streams
Index: dlls/avifil32/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/avifil32/Makefile.in,v retrieving revision 1.23 diff -u -r1.23 Makefile.in --- dlls/avifil32/Makefile.in 19 Oct 2002 00:12:06 -0000 1.23 +++ dlls/avifil32/Makefile.in 20 Oct 2002 19:36:41 -0000 @@ -11,11 +11,13 @@ SYMBOLFILE = $(MODULE).tmp.o C_SRCS = \ + acmstream.c \ api.c \ avifile.c \ extrachunk.c \ factory.c \ getframe.c \ + icmstream.c \ wavfile.c RC_SRCS = \ Index: dlls/avifil32/api.c =================================================================== RCS file: /home/wine/wine/dlls/avifil32/api.c,v retrieving revision 1.8 diff -u -r1.8 api.c --- dlls/avifil32/api.c 18 Oct 2002 23:48:59 -0000 1.8 +++ dlls/avifil32/api.c 20 Oct 2002 19:36:42 -0000 @@ -691,7 +691,7 @@ if (FAILED(hr)) return hr; - wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccHandler); + wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccType); if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &size) != ERROR_SUCCESS) return AVIERR_UNSUPPORTED; if (AVIFILE_CLSIDFromString(szValue, &clsidHandler) != S_OK) @@ -850,7 +850,10 @@ */ HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving) { - FIXME("(%p,%ld,%d): stub\n", szFilter, cbFilter, fSaving); + LPWSTR wszFilter; + HRESULT hr; + + TRACE("(%p,%ld,%d)\n", szFilter, cbFilter, fSaving); /* check parameters */ if (szFilter == NULL) @@ -861,7 +864,19 @@ szFilter[0] = 0; szFilter[1] = 0; - return AVIERR_UNSUPPORTED; + wszFilter = (LPWSTR)GlobalAllocPtr(GHND, cbFilter); + if (wszFilter == NULL) + return AVIERR_MEMORY; + + hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter, + szFilter, cbFilter, NULL, NULL); + } + + GlobalFreePtr(wszFilter); + + return hr; } /*********************************************************************** @@ -869,7 +884,10 @@ */ HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving) { - FIXME("(%p,%ld,%d): stub\n", szFilter, cbFilter, fSaving); + WCHAR szAllFiles[40]; + LONG size; + + FIXME("(%p,%ld,%d): partial stub\n", szFilter, cbFilter, fSaving); /* check parameters */ if (szFilter == NULL) @@ -877,10 +895,35 @@ if (cbFilter < 2) return AVIERR_BADSIZE; - szFilter[0] = 0; - szFilter[1] = 0; + /* FIXME: + * + * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect + * extensions and CLSID's + * 2. iterate over collected CLSID's and copy it's description and it's + * extensions to szFilter if it fits + * + * First filter is named "All multimedia files" and it's ilter is a + * collection of all possible extensions except "*.*". + */ + + /* add "All files" "*.*" filter if enough space left */ + size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES, + szAllFiles, sizeof(szAllFiles)); + if (cbFilter > size) { + int i; + + /* replace '@' with \000 to seperate description of filter */ + for (i = 0; i < size && szAllFiles[i] != 0; i++) { + if (szAllFiles[i] == '@') { + szAllFiles[i] = 0; + break; + } + } + + memcpy(szFilter, szAllFiles, size); + } - return AVIERR_UNSUPPORTED; + return AVIERR_OK; } /*********************************************************************** Index: dlls/avifil32/avifile.c =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifile.c,v retrieving revision 1.28 diff -u -r1.28 avifile.c --- dlls/avifil32/avifile.c 19 Oct 2002 00:11:32 -0000 1.28 +++ dlls/avifil32/avifile.c 20 Oct 2002 19:36:44 -0000 @@ -162,7 +162,7 @@ DWORD cbBuffer; /* size of lpBuffer */ DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */ - DWORD dwLastFrame; /* last correct index in idxFrames */ + LONG lLastFrame; /* last correct index in idxFrames */ AVIINDEXENTRY *idxFrames; DWORD nIdxFrames; /* upper index limit of idxFrames */ AVIINDEXENTRY *idxFmtChanges; @@ -390,6 +390,8 @@ if (avis == NULL || asi == NULL) return AVIERR_BADPARAM; + *avis = NULL; + /* Does the user have write permission? */ if ((This->uMode & MMIO_RWMODE) == 0) return AVIERR_READONLY; @@ -422,6 +424,10 @@ /* update our AVIFILEINFO structure */ AVIFILE_UpdateInfo(This); + /* return it */ + *avis = (PAVISTREAM)This->ppStreams[n]; + IAVIStream_AddRef(*avis); + return AVIERR_OK; } @@ -757,7 +763,7 @@ if (flags & FIND_TYPE) { if (flags & FIND_KEY) { - while (0 <= pos && pos <= This->dwLastFrame) { + while (0 <= pos && pos <= This->lLastFrame) { if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME) goto RETURN_FOUND; @@ -767,7 +773,7 @@ pos--; }; } else if (flags & FIND_ANY) { - while (0 <= pos && pos <= This->dwLastFrame) { + while (0 <= pos && pos <= This->lLastFrame) { if (This->idxFrames[pos].dwChunkLength > 0) goto RETURN_FOUND; @@ -882,11 +888,11 @@ return AVIERR_READONLY; /* can only set format before frame is written! */ - if (This->dwLastFrame > pos) + if (This->lLastFrame > pos) return AVIERR_UNSUPPORTED; /* initial format or a formatchange? */ - if (This->lpFormat != NULL) { + if (This->lpFormat == NULL) { /* initial format */ if (This->paf->dwMoviChunkPos != 0) return AVIERR_ERROR; /* user has used API in wrong sequnece! */ @@ -1063,7 +1069,7 @@ if (samples > 1) samples = 1; - assert(start <= This->dwLastFrame); + assert(start <= This->lLastFrame); size = This->idxFrames[start].dwChunkLength; if (buffer != NULL && buffersize >= size) { hr = AVIFILE_ReadBlock(This, start, buffer, size); @@ -1124,11 +1130,12 @@ /* append to end of stream? */ if (start == -1) { - if (This->dwLastFrame == -1) + if (This->lLastFrame == -1) start = This->sInfo.dwStart; else start = This->sInfo.dwLength; - } + } else if (This->lLastFrame == -1) + This->sInfo.dwStart = start; if (This->sInfo.dwSampleSize != 0) { /* fixed sample size -- audio like */ @@ -1140,7 +1147,7 @@ return AVIERR_UNSUPPORTED; /* Convert position to frame/block */ - start = This->dwLastFrame + 1; + start = This->lLastFrame + 1; if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) { FIXME(": not interleaved, could collect audio data!\n"); @@ -1151,11 +1158,11 @@ return AVIERR_UNSUPPORTED; /* must we fill up with empty frames? */ - if (This->dwLastFrame != -1) { + if (This->lLastFrame != -1) { FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); - while (start > This->dwLastFrame + 1) { - hr = AVIFILE_WriteBlock(This, This->dwLastFrame + 1, ckid2, 0, NULL, 0); + while (start > This->lLastFrame + 1) { + hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0); if (FAILED(hr)) return hr; } @@ -1298,6 +1305,9 @@ static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags) { + /* pre-conditions */ + assert(This != NULL); + switch (TWOCCFromFOURCC(ckid)) { case cktypeDIBbits: if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) @@ -1323,7 +1333,7 @@ if (This->idxFmtChanges == NULL) return AVIERR_MEMORY; - This->idxFmtChanges[n].ckid = This->dwLastFrame; + This->idxFmtChanges[n].ckid = This->lLastFrame; This->idxFmtChanges[n].dwFlags = 0; This->idxFmtChanges[n].dwChunkOffset = offset; This->idxFmtChanges[n].dwChunkLength = size; @@ -1341,25 +1351,29 @@ }; /* first frame is alwasy a keyframe */ - if (This->dwLastFrame == -1) + if (This->lLastFrame == -1) flags |= AVIIF_KEYFRAME; if (This->sInfo.dwSuggestedBufferSize < size) This->sInfo.dwSuggestedBufferSize = size; /* get memory for index */ - if (This->idxFrames == NULL || This->dwLastFrame + 1 >= This->nIdxFrames) { + if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) { This->nIdxFrames += 512; This->idxFrames = GlobalReAllocPtr(This->idxFrames, This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND); if (This->idxFrames == NULL) return AVIERR_MEMORY; } - This->dwLastFrame++; - This->idxFrames[This->dwLastFrame].ckid = ckid; - This->idxFrames[This->dwLastFrame].dwFlags = flags; - This->idxFrames[This->dwLastFrame].dwChunkOffset = offset; - This->idxFrames[This->dwLastFrame].dwChunkLength = size; + This->lLastFrame++; + This->idxFrames[This->lLastFrame].ckid = ckid; + This->idxFrames[This->lLastFrame].dwFlags = flags; + This->idxFrames[This->lLastFrame].dwChunkOffset = offset; + This->idxFrames[This->lLastFrame].dwChunkLength = size; + + /* update AVISTREAMINFO structure if neccessary */ + if (This->sInfo.dwLength < This->lLastFrame) + This->sInfo.dwLength = This->lLastFrame; return AVIERR_OK; } @@ -1413,7 +1427,7 @@ pstream->paf = paf; pstream->nStream = nr; pstream->dwCurrentFrame = -1; - pstream->dwLastFrame = -1; + pstream->lLastFrame = -1; if (asi != NULL) { memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo)); @@ -1451,7 +1465,7 @@ assert(This != NULL); This->dwCurrentFrame = -1; - This->dwLastFrame = -1; + This->lLastFrame = -1; This->paf = NULL; if (This->idxFrames != NULL) { GlobalFreePtr(This->idxFrames); @@ -1746,7 +1760,7 @@ * by parsing 'movi' chunk */ if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) { for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) - This->ppStreams[nStream]->dwLastFrame = -1; + This->ppStreams[nStream]->lLastFrame = -1; if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) { ERR(": Oops, can't seek back to 'movi' chunk!\n"); @@ -1759,6 +1773,9 @@ if (mmioAscend(This->hmmio, &ck, 0) == S_OK) { nStream = StreamFromFOURCC(ck.ckid); + if (nStream > This->fInfo.dwStreams) + return AVIERR_BADFORMAT; + AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, ck.dwDataOffset - 2 * sizeof(DWORD), 0); } else { @@ -1802,7 +1819,7 @@ for (n = 0; n < This->fInfo.dwStreams; n++) { IAVIStreamImpl *pStream = This->ppStreams[n]; - pStream->dwLastFrame = -1; + pStream->lLastFrame = -1; if (pStream->idxFrames != NULL) { GlobalFreePtr(pStream->idxFrames); @@ -1822,7 +1839,7 @@ pStream->idxFrames = (AVIINDEXENTRY*)GlobalAllocPtr(GHND, pStream->nIdxFrames * sizeof(AVIINDEXENTRY)); - if (pStream->idxFrames == NULL) { + if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) { pStream->nIdxFrames = 0; return AVIERR_MEMORY; } @@ -1889,7 +1906,7 @@ assert(This->paf != NULL); assert(This->paf->hmmio != (HMMIO)NULL); assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); - assert(pos <= This->dwLastFrame); + assert(pos <= This->lLastFrame); /* should we read as much as block gives us? */ if (size == 0 || size > This->idxFrames[pos].dwChunkLength) @@ -1953,7 +1970,7 @@ (*offset) *= This->sInfo.dwSampleSize; /* convert bytes to block number */ - for (block = 0; block <= This->dwLastFrame; block++) { + for (block = 0; block <= This->lLastFrame; block++) { if (This->idxFrames[block].dwChunkLength <= *offset) (*offset) -= This->idxFrames[block].dwChunkLength; else @@ -2049,6 +2066,7 @@ strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames; strHdr.dwScale = pStream->sInfo.dwScale; strHdr.dwRate = pStream->sInfo.dwRate; + strHdr.dwStart = pStream->sInfo.dwStart; strHdr.dwLength = pStream->sInfo.dwLength; strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize; strHdr.dwQuality = pStream->sInfo.dwQuality; @@ -2240,7 +2258,7 @@ nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames); /* reached end of this stream? */ - if (pStream->dwLastFrame <= nFrame) + if (pStream->lLastFrame <= nFrame) continue; if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && @@ -2274,10 +2292,13 @@ } } else { /* not interleaved -- write index for each stream at once */ - for (nStream = 0; nStream < This->fInfo.dwStreams; n++) { + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { pStream = This->ppStreams[nStream]; - for (n = 0; n < pStream->dwLastFrame; n++) { + if (pStream->lLastFrame == -1) + pStream->lLastFrame = 0; + + for (n = 0; n < pStream->lLastFrame; n++) { if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && (pStream->sInfo.dwFormatChangeCount != 0)) { DWORD pos; @@ -2431,7 +2452,7 @@ return AVIERR_FILEWRITE; This->paf->fDirty = TRUE; - This->paf->dwNextFramePos = ck.dwDataOffset + ck.cksize; + This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); return AVIFILE_AddFrame(This, ckid, size, ck.dwDataOffset, flags); } Index: dlls/avifil32/avifile_De.rc =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifile_De.rc,v retrieving revision 1.1 diff -u -r1.1 avifile_De.rc --- dlls/avifil32/avifile_De.rc 18 Oct 2002 00:24:41 -0000 1.1 +++ dlls/avifil32/avifile_De.rc 20 Oct 2002 19:36:44 -0000 @@ -22,6 +22,7 @@ { IDS_WAVESTREAMFORMAT "Waveform: %s" IDS_WAVEFILETYPE "Waveform" + IDS_ALLFILES "Alle Dateien (*.*)@*.*" IDS_VIDEO "Video" IDS_AUDIO "Audio" IDS_AVISTREAMFORMAT "%s %s #%d" Index: dlls/avifil32/avifile_En.rc =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifile_En.rc,v retrieving revision 1.1 diff -u -r1.1 avifile_En.rc --- dlls/avifil32/avifile_En.rc 18 Oct 2002 00:24:41 -0000 1.1 +++ dlls/avifil32/avifile_En.rc 20 Oct 2002 19:36:44 -0000 @@ -22,6 +22,7 @@ { IDS_WAVESTREAMFORMAT "Waveform: %s" IDS_WAVEFILETYPE "Waveform" + IDS_ALLFILES "All files (*.*)@*.*" IDS_VIDEO "video" IDS_AUDIO "audio" IDS_AVISTREAMFORMAT "%s %s #%d" Index: dlls/avifil32/avifile_private.h =================================================================== RCS file: /home/wine/wine/dlls/avifil32/avifile_private.h,v retrieving revision 1.5 diff -u -r1.5 avifile_private.h --- dlls/avifil32/avifile_private.h 18 Oct 2002 00:24:41 -0000 1.5 +++ dlls/avifil32/avifile_private.h 20 Oct 2002 19:36:44 -0000 @@ -32,8 +32,14 @@ #define DIBWIDTHBYTES(bi) WIDTHBYTES((bi).biWidth * (bi).biBitCount) #endif +#ifndef DIBPTR +#define DIBPTR(lp) ((LPBYTE)(lp) + (lp)->biSize + \ + (lp)->biClrUsed * sizeof(RGBQUAD)) +#endif + #define IDS_WAVESTREAMFORMAT 0x0100 #define IDS_WAVEFILETYPE 0x0101 +#define IDS_ALLFILES 0x0185 #define IDS_VIDEO 0x0189 #define IDS_AUDIO 0x0190 #define IDS_AVISTREAMFORMAT 0x0191 Index: dlls/avifil32/factory.c =================================================================== RCS file: /home/wine/wine/dlls/avifil32/factory.c,v retrieving revision 1.3 diff -u -r1.3 factory.c --- dlls/avifil32/factory.c 18 Oct 2002 23:48:59 -0000 1.3 +++ dlls/avifil32/factory.c 20 Oct 2002 19:36:45 -0000 @@ -127,7 +127,7 @@ { ICOM_THIS(IClassFactoryImpl,iface); - FIXME("(%p,%p,%s,%p): partial stub!\n", iface, pOuter, debugstr_guid(riid), + TRACE("(%p,%p,%s,%p)\n", iface, pOuter, debugstr_guid(riid), ppobj); if (ppobj == NULL || pOuter != NULL) @@ -136,12 +136,12 @@ if (IsEqualGUID(&CLSID_AVIFile, &This->clsid)) return AVIFILE_CreateAVIFile(riid,ppobj); -/* if (IsEqualGUID(&CLSID_ICMStream, &This->clsid)) */ -/* return AVIFILE_CreateICMStream(riid,ppobj); */ + if (IsEqualGUID(&CLSID_ICMStream, &This->clsid)) + return AVIFILE_CreateICMStream(riid,ppobj); if (IsEqualGUID(&CLSID_WAVFile, &This->clsid)) return AVIFILE_CreateWAVFile(riid,ppobj); -/* if (IsEqualGUID(&CLSID_ACMStream, &This->clsid)) */ -/* return AVIFILE_CreateACMStream(riid,ppobj); */ + if (IsEqualGUID(&CLSID_ACMStream, &This->clsid)) + return AVIFILE_CreateACMStream(riid,ppobj); return E_NOINTERFACE; } Index: include/vfw.h =================================================================== RCS file: /home/wine/wine/include/vfw.h,v retrieving revision 1.29 diff -u -r1.29 vfw.h --- include/vfw.h 18 Oct 2002 00:25:19 -0000 1.29 +++ include/vfw.h 20 Oct 2002 19:36:49 -0000 @@ -1081,6 +1081,10 @@ LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample); LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime); +#define AVIFileClose(pavi) \ + AVIFileRelease(pavi) +#define AVIStreamClose(pavi) \ + AVIStreamRelease(pavi); #define AVIStreamEnd(pavi) \ (AVIStreamStart(pavi) + AVIStreamLength(pavi)) #define AVIStreamEndTime(pavi) \ --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/acmstream.c Sun Oct 20 20:18:58 2002 @@ -0,0 +1,733 @@ +/* + * 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 "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" +#include "msacm.h" + +#include "avifile_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +/***********************************************************************/ + +static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI ACMStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface); +static HRESULT WINAPI ACMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI ACMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI ACMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); +static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); +static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI ACMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); +static HRESULT WINAPI ACMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); +static HRESULT WINAPI ACMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI ACMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); +static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); +static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +struct ICOM_VTABLE(IAVIStream) iacmst = { + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + ACMStream_fnQueryInterface, + ACMStream_fnAddRef, + ACMStream_fnRelease, + ACMStream_fnCreate, + ACMStream_fnInfo, + ACMStream_fnFindSample, + ACMStream_fnReadFormat, + ACMStream_fnSetFormat, + ACMStream_fnRead, + ACMStream_fnWrite, + ACMStream_fnDelete, + ACMStream_fnReadData, + ACMStream_fnWriteData, + ACMStream_fnSetInfo +}; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + ICOM_VFIELD(IAVIStream); + DWORD ref; + + /* IAVIStream stuff */ + PAVISTREAM pStream; + AVISTREAMINFOW sInfo; + + HACMSTREAM has; + + LPWAVEFORMATEX lpInFormat; + LONG cbInFormat; + + LPWAVEFORMATEX lpOutFormat; + LONG cbOutFormat; + + ACMSTREAMHEADER acmStreamHdr; +} IAVIStreamImpl; + +/***********************************************************************/ + +#define CONVERT_STREAM_to_THIS(a) { \ + acmStreamSize(This->has,(a)*This->lpInFormat->nBlockAlign,\ + &(a), ACM_STREAMSIZEF_SOURCE); \ + (a) /= This->lpOutFormat->nBlockAlign; } +#define CONVERT_THIS_to_STREAM(a) { \ + acmStreamSize(This->has,(a)*This->lpOutFormat->nBlockAlign,\ + &(a), ACM_STREAMSIZEF_DESTINATION); \ + (a) /= This->lpInFormat->nBlockAlign; } + +static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This); + +HRESULT AVIFILE_CreateACMStream(REFIID riid, LPVOID *ppv) +{ + IAVIStreamImpl *pstream; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl)); + if (pstream == NULL) + return AVIERR_MEMORY; + + ICOM_VTBL(pstream) = &iacmst; + + hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pstream); + + return hr; +} + +static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = This; + IAVIStream_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI ACMStream_fnAddRef(IAVIStream *iface) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p) -> %ld\n", iface, This->ref + 1); + + /* also add reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_AddRef(This->pStream); + + return ++(This->ref); +} + +static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p) -> %ld\n", iface, This->ref - 1); + + if (This->ref == 0) { + /* destruct */ + if (This->has != (HACMSTREAM)NULL) { + if (This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) + acmStreamUnprepareHeader(This->has, &This->acmStreamHdr, 0); + acmStreamClose(This->has, 0); + This->has = (HACMSTREAM)NULL; + } + if (This->acmStreamHdr.pbSrc != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbSrc); + This->acmStreamHdr.pbSrc = NULL; + } + if (This->acmStreamHdr.pbDst != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbDst); + This->acmStreamHdr.pbDst = NULL; + } + if (This->lpInFormat != NULL) { + GlobalFreePtr(This->lpInFormat); + This->lpInFormat = NULL; + This->cbInFormat = 0; + } + if (This->lpOutFormat != NULL) { + GlobalFreePtr(This->lpOutFormat); + This->lpOutFormat = NULL; + This->cbOutFormat = 0; + } + if (This->pStream != NULL) { + IAVIStream_Release(This->pStream); + This->pStream = NULL; + } + LocalFree((HLOCAL)This); + + return 0; + } + + /* also release reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_Release(This->pStream); + + return --This->ref; +} + +/* lParam1: PAVISTREAM + * lParam2: LPAVICOMPRESSOPTIONS -- even if doc's say LPWAVEFORMAT + */ +static HRESULT WINAPI ACMStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* check for swapped parameters */ + if ((LPVOID)lParam1 != NULL && + ((LPAVICOMPRESSOPTIONS)lParam1)->fccType == streamtypeAUDIO) { + register LPARAM tmp = lParam1; + + lParam1 = lParam2; + lParam2 = tmp; + } + + if ((LPVOID)lParam1 == NULL) + return AVIERR_BADPARAM; + + IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); + if (This->sInfo.fccType != streamtypeAUDIO) + return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */ + + This->sInfo.fccHandler = 0; /* be paranoid */ + + /* FIXME: check ACM version? Which version does we need? */ + + if ((LPVOID)lParam2 != NULL) { + /* We only need the format from the compress-options */ + if (((LPAVICOMPRESSOPTIONS)lParam2)->fccType == streamtypeAUDIO) + lParam2 = (LPARAM)((LPAVICOMPRESSOPTIONS)lParam2)->lpFormat; + + if (((LPWAVEFORMATEX)lParam2)->wFormatTag != WAVE_FORMAT_PCM) + This->cbOutFormat = sizeof(WAVEFORMATEX) + ((LPWAVEFORMATEX)lParam2)->cbSize; + else + This->cbOutFormat = sizeof(PCMWAVEFORMAT); + + This->lpOutFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, This->cbOutFormat); + if (This->lpOutFormat == NULL) + return AVIERR_MEMORY; + + memcpy(This->lpOutFormat, (LPVOID)lParam2, This->cbOutFormat); + } else { + This->lpOutFormat = NULL; + This->cbOutFormat = 0; + } + + This->pStream = (PAVISTREAM)lParam1; + IAVIStream_AddRef(This->pStream); + + return AVIERR_OK; +} + +static HRESULT WINAPI ACMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + /* Need codec to correct some values in structure */ + if (This->has == (HACMSTREAM)NULL) { + HRESULT hr = AVIFILE_OpenCompressor(This); + + if (FAILED(hr)) + return hr; + } + + memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo))); + + if (size < sizeof(This->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI ACMStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + /* convert pos from our 'space' to This->pStream's one */ + CONVERT_THIS_to_STREAM(pos); + + /* ask stream */ + pos = IAVIStream_FindSample(This->pStream, pos, flags); + + if (pos != -1) { + /* convert pos back to our 'space' if it's no size or physical pos */ + if ((flags & FIND_RET) == 0) + CONVERT_STREAM_to_THIS(pos); + } + + return pos; +} + +static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + if (This->has == (HACMSTREAM)NULL) { + HRESULT hr = AVIFILE_OpenCompressor(This); + + if (FAILED(hr)) + return hr; + } + + /* only interested in needed buffersize? */ + if (format == NULL || *formatsize <= 0) { + *formatsize = This->cbOutFormat; + + return AVIERR_OK; + } + + /* copy initial format (only as much as will fit) */ + memcpy(format, This->lpOutFormat, min(*formatsize, This->cbOutFormat)); + if (*formatsize < This->cbOutFormat) { + *formatsize = This->cbOutFormat; + return AVIERR_BUFFERTOOSMALL; + } + + *formatsize = This->cbOutFormat; + return AVIERR_OK; +} + +static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + HRESULT hr; + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= 0) + return AVIERR_BADPARAM; + + /* Input format already known? + * Changing is unsupported, but be quiet if it's the same */ + if (This->lpInFormat != NULL) { + if (This->cbInFormat != formatsize || + memcmp(format, This->lpInFormat, formatsize) != 0) + return AVIERR_UNSUPPORTED; + + return AVIERR_OK; + } + + /* Does the nested stream support writing? */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + This->lpInFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize); + if (This->lpInFormat == NULL) + return AVIERR_MEMORY; + This->cbInFormat = formatsize; + memcpy(This->lpInFormat, format, formatsize); + + /* initialize formats and get compressor */ + hr = AVIFILE_OpenCompressor(This); + if (FAILED(hr)) + return hr; + + CONVERT_THIS_to_STREAM(pos); + + /* tell the nested stream the new format */ + return IAVIStream_SetFormat(This->pStream, pos, This->lpOutFormat, + This->cbOutFormat); +} + +static HRESULT WINAPI ACMStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + HRESULT hr; + LONG size; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + /* Do we have our compressor? */ + if (This->has == (HACMSTREAM)NULL) { + hr = AVIFILE_OpenCompressor(This); + + if (FAILED(hr)) + return hr; + } + + /* only need to pass through? */ + if (This->cbInFormat == This->cbOutFormat && + memcmp(This->lpInFormat, This->lpOutFormat, This->cbInFormat) == 0) { + return IAVIStream_Read(This->pStream, start, samples, buffer, buffersize, + bytesread, samplesread); + } + + /* read as much as fit? */ + if (samples == -1) + samples = buffersize / This->lpOutFormat->nBlockAlign; + /* limit to buffersize */ + if (samples * This->lpOutFormat->nBlockAlign > buffersize) + samples = buffersize / This->lpOutFormat->nBlockAlign; + + /* only return needed size? */ + if (buffer == NULL || buffersize <= 0 || samples == 0) { + if (bytesread == NULL && samplesread == NULL) + return AVIERR_BADPARAM; + + if (bytesread != NULL) + *bytesread = samples * This->lpOutFormat->nBlockAlign; + if (samplesread != NULL) + *samplesread = samples; + + return AVIERR_OK; + } + + /* map our positions to pStream positions */ + CONVERT_THIS_to_STREAM(start); + + /* our needed internal buffersize */ + size = samples * This->lpInFormat->nBlockAlign; + + /* Need to free destination buffer used for writing? */ + if (This->acmStreamHdr.pbDst != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbDst); + This->acmStreamHdr.pbDst = NULL; + This->acmStreamHdr.dwDstUser = 0; + } + + /* need bigger source buffer? */ + if (This->acmStreamHdr.pbSrc == NULL || + This->acmStreamHdr.dwSrcUser < size) { + This->acmStreamHdr.pbSrc = GlobalReAllocPtr(This->acmStreamHdr.pbSrc, + size, GMEM_MOVEABLE); + if (This->acmStreamHdr.pbSrc == NULL) + return AVIERR_MEMORY; + This->acmStreamHdr.dwSrcUser = size; + } + + This->acmStreamHdr.cbStruct = sizeof(This->acmStreamHdr); + This->acmStreamHdr.cbSrcLengthUsed = 0; + This->acmStreamHdr.cbDstLengthUsed = 0; + This->acmStreamHdr.cbSrcLength = size; + + /* read source data */ + hr = IAVIStream_Read(This->pStream, start, -1, This->acmStreamHdr.pbSrc, + This->acmStreamHdr.cbSrcLength, + &This->acmStreamHdr.cbSrcLength, NULL); + if (FAILED(hr) || This->acmStreamHdr.cbSrcLength == 0) + return hr; + + /* need to prepare stream? */ + This->acmStreamHdr.pbDst = buffer; + This->acmStreamHdr.cbDstLength = buffersize; + if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) { + if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) { + This->acmStreamHdr.pbDst = NULL; + This->acmStreamHdr.cbDstLength = 0; + return AVIERR_COMPRESSOR; + } + } + + /* now do the conversion */ + /* FIXME: use ACM_CONVERTF_* flags */ + if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK) + hr = AVIERR_COMPRESSOR; + + This->acmStreamHdr.pbDst = NULL; + This->acmStreamHdr.cbDstLength = 0; + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = This->acmStreamHdr.cbDstLengthUsed; + if (samplesread != NULL) + *samplesread = + This->acmStreamHdr.cbDstLengthUsed / This->lpOutFormat->nBlockAlign; + + return hr; +} + +static HRESULT WINAPI ACMStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + HRESULT hr; + LONG size; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + /* Have we write capability? */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + /* also need a compressor */ + if (This->has == (HACMSTREAM)NULL) + return AVIERR_NOCOMPRESSOR; + + /* map our sizes to pStream sizes */ + size = buffersize; + CONVERT_THIS_to_STREAM(size); + CONVERT_THIS_to_STREAM(start); + + /* no bytes to write? -- short circuit */ + if (size == 0) { + return IAVIStream_Write(This->pStream, -1, samples, buffer, size, + flags, sampwritten, byteswritten); + } + + /* Need to free source buffer used for reading? */ + if (This->acmStreamHdr.pbSrc != NULL) { + GlobalFreePtr(This->acmStreamHdr.pbSrc); + This->acmStreamHdr.pbSrc = NULL; + This->acmStreamHdr.dwSrcUser = 0; + } + + /* Need bigger destination buffer? */ + if (This->acmStreamHdr.pbDst == NULL || + This->acmStreamHdr.dwDstUser < size) { + This->acmStreamHdr.pbDst = GlobalReAllocPtr(This->acmStreamHdr.pbDst, + size, GMEM_MOVEABLE); + if (This->acmStreamHdr.pbDst == NULL) + return AVIERR_MEMORY; + This->acmStreamHdr.dwDstUser = size; + } + This->acmStreamHdr.cbStruct = sizeof(This->acmStreamHdr); + This->acmStreamHdr.cbSrcLengthUsed = 0; + This->acmStreamHdr.cbDstLengthUsed = 0; + This->acmStreamHdr.cbDstLength = This->acmStreamHdr.dwDstUser; + + /* need to prepare stream? */ + This->acmStreamHdr.pbSrc = buffer; + This->acmStreamHdr.cbSrcLength = buffersize; + if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) { + if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) { + This->acmStreamHdr.pbSrc = NULL; + This->acmStreamHdr.cbSrcLength = 0; + return AVIERR_COMPRESSOR; + } + } + + /* now do the conversion */ + /* FIXME: use ACM_CONVERTF_* flags */ + if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK) + hr = AVIERR_COMPRESSOR; + else + hr = AVIERR_OK; + + This->acmStreamHdr.pbSrc = NULL; + This->acmStreamHdr.cbSrcLength = 0; + + if (FAILED(hr)) + return hr; + + return IAVIStream_Write(This->pStream,-1,This->acmStreamHdr.cbDstLengthUsed / + This->lpOutFormat->nBlockAlign,This->acmStreamHdr.pbDst, + This->acmStreamHdr.cbDstLengthUsed,flags,sampwritten, + byteswritten); +} + +static HRESULT WINAPI ACMStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,%ld)\n", iface, start, samples); + + /* check parameters */ + if (start < 0 || samples < 0) + return AVIERR_BADPARAM; + + /* Delete before start of stream? */ + if (start + samples < This->sInfo.dwStart) + return AVIERR_OK; + + /* Delete after end of stream? */ + if (start > This->sInfo.dwLength) + return AVIERR_OK; + + /* For the rest we need write capability */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + /* A compressor is also neccessary */ + if (This->has == (HACMSTREAM)NULL) + return AVIERR_NOCOMPRESSOR; + + /* map our positions to pStream positions */ + CONVERT_THIS_to_STREAM(start); + CONVERT_THIS_to_STREAM(samples); + + return IAVIStream_Delete(This->pStream, start, samples); +} + +static HRESULT WINAPI ACMStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread); + + assert(This->pStream != NULL); + + return IAVIStream_ReadData(This->pStream, fcc, lp, lpread); +} + +static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size); + + assert(This->pStream != NULL); + + return IAVIStream_WriteData(This->pStream, fcc, lp, size); +} + +static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This) +{ + HRESULT hr; + + /* pre-conditions */ + assert(This != NULL); + assert(This->pStream != NULL); + + if (This->has != (HACMSTREAM)NULL) + return AVIERR_OK; + + if (This->lpInFormat == NULL) { + /* decode or encode the data from pStream */ + hr = AVIStreamFormatSize(This->pStream, This->sInfo.dwStart, &This->cbInFormat); + if (FAILED(hr)) + return hr; + This->lpInFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, This->cbInFormat); + if (This->lpInFormat == NULL) + return AVIERR_MEMORY; + + hr = IAVIStream_ReadFormat(This->pStream, This->sInfo.dwStart, + This->lpInFormat, &This->cbInFormat); + if (FAILED(hr)) + return hr; + + if (This->lpOutFormat == NULL) { + /* we must decode to default format */ + This->cbOutFormat = sizeof(PCMWAVEFORMAT); + This->lpOutFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, This->cbOutFormat); + if (This->lpOutFormat == NULL) + return AVIERR_MEMORY; + + This->lpOutFormat->wFormatTag = WAVE_FORMAT_PCM; + if (acmFormatSuggest(NULL, This->lpInFormat, This->lpOutFormat, + This->cbOutFormat, ACM_FORMATSUGGESTF_WFORMATTAG) != S_OK) + return AVIERR_NOCOMPRESSOR; + } + } else if (This->lpOutFormat == NULL) + return AVIERR_ERROR; /* To what should I encode? */ + + if (acmStreamOpen(&This->has, NULL, This->lpInFormat, This->lpOutFormat, + NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME) != S_OK) + return AVIERR_NOCOMPRESSOR; + + /* update AVISTREAMINFO structure */ + This->sInfo.dwSampleSize = This->lpOutFormat->nBlockAlign; + This->sInfo.dwScale = This->lpOutFormat->nBlockAlign; + This->sInfo.dwRate = This->lpOutFormat->nAvgBytesPerSec; + This->sInfo.dwQuality = ICQUALITY_DEFAULT; + SetRectEmpty(&This->sInfo.rcFrame); + + /* convert positions ansd sizes to output format */ + CONVERT_STREAM_to_THIS(This->sInfo.dwStart); + CONVERT_STREAM_to_THIS(This->sInfo.dwLength); + CONVERT_STREAM_to_THIS(This->sInfo.dwSuggestedBufferSize); + + return AVIERR_OK; +} --- /dev/null Sat Feb 23 16:05:24 2002 +++ dlls/avifil32/icmstream.c Sun Oct 20 20:29:26 2002 @@ -0,0 +1,971 @@ +/* + * 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 "winuser.h" +#include "winnls.h" +#include "winerror.h" +#include "windowsx.h" +#include "mmsystem.h" +#include "vfw.h" +#include "msacm.h" + +#include "avifile_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(avifile); + +#define MAX_FRAMESIZE (16 * 1024 * 1024) +#define MAX_FRAMESIZE_DIFF 512 + +/***********************************************************************/ + +static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); +static ULONG WINAPI ICMStream_fnAddRef(IAVIStream*iface); +static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface); +static HRESULT WINAPI ICMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); +static HRESULT WINAPI ICMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); +static LONG WINAPI ICMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); +static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); +static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); +static HRESULT WINAPI ICMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); +static HRESULT WINAPI ICMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); +static HRESULT WINAPI ICMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); +static HRESULT WINAPI ICMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); +static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); +static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); + +struct ICOM_VTABLE(IAVIStream) iicmst = { + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + ICMStream_fnQueryInterface, + ICMStream_fnAddRef, + ICMStream_fnRelease, + ICMStream_fnCreate, + ICMStream_fnInfo, + ICMStream_fnFindSample, + ICMStream_fnReadFormat, + ICMStream_fnSetFormat, + ICMStream_fnRead, + ICMStream_fnWrite, + ICMStream_fnDelete, + ICMStream_fnReadData, + ICMStream_fnWriteData, + ICMStream_fnSetInfo +}; + +typedef struct _IAVIStreamImpl { + /* IUnknown stuff */ + ICOM_VFIELD(IAVIStream); + DWORD ref; + + /* IAVIStream stuff */ + PAVISTREAM pStream; + AVISTREAMINFOW sInfo; + + PGETFRAME pg; + HIC hic; + DWORD dwICMFlags; + + LONG lCurrent; + LONG lLastKey; + LONG lKeyFrameEvery; + DWORD dwLastQuality; + DWORD dwBytesPerFrame; + DWORD dwUnusedBytes; + + LPBITMAPINFOHEADER lpbiCur; /* current frame */ + LPVOID lpCur; + LPBITMAPINFOHEADER lpbiPrev; /* previous frame */ + LPVOID lpPrev; + + LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */ + LONG cbOutput; + LPBITMAPINFOHEADER lpbiInput; /* input format for codec */ + LONG cbInput; +} IAVIStreamImpl; + +/***********************************************************************/ + +static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This, + LPBITMAPINFOHEADER lpbi, LPVOID lpBits); +static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This); + +inline void AVIFILE_Reset(IAVIStreamImpl *This) +{ + This->lCurrent = -1; + This->lLastKey = 0; + This->dwLastQuality = ICQUALITY_HIGH; + This->dwUnusedBytes = 0; +} + +HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv) +{ + IAVIStreamImpl *pstream; + HRESULT hr; + + assert(riid != NULL && ppv != NULL); + + *ppv = NULL; + + pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl)); + if (pstream == NULL) + return AVIERR_MEMORY; + + ICOM_VTBL(pstream) = &iicmst; + AVIFILE_Reset(pstream); + + hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv); + if (FAILED(hr)) + LocalFree((HLOCAL)pstream); + + return hr; +} + +static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface, + REFIID refiid, LPVOID *obj) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); + + if (IsEqualGUID(&IID_IUnknown, refiid) || + IsEqualGUID(&IID_IAVIStream, refiid)) { + *obj = This; + IAVIStream_AddRef(iface); + + return S_OK; + } + + return OLE_E_ENUM_NOMORE; +} + +static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p) -> %ld\n", iface, This->ref + 1); + + /* also add reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_AddRef(This->pStream); + + return ++(This->ref); +} + +static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p) -> %ld\n", iface, This->ref - 1); + + if (This->ref == 0) { + /* destruct */ + if (This->pg != NULL) { + AVIStreamGetFrameClose(This->pg); + This->pg = NULL; + } + if (This->pStream != NULL) { + IAVIStream_Release(This->pStream); + This->pStream = NULL; + } + if (This->hic != (HIC)NULL) { + if (This->lpbiPrev != NULL) { + ICDecompressEnd(This->hic); + GlobalFreePtr(This->lpbiPrev); + This->lpbiPrev = NULL; + This->lpPrev = NULL; + } + ICCompressEnd(This->hic); + This->hic = (HIC)NULL; + } + if (This->lpbiCur != NULL) { + GlobalFreePtr(This->lpbiCur); + This->lpbiCur = NULL; + This->lpCur = NULL; + } + if (This->lpbiOutput != NULL) { + GlobalFreePtr(This->lpbiOutput); + This->lpbiOutput = NULL; + This->cbOutput = 0; + } + if (This->lpbiInput != NULL) { + GlobalFreePtr(This->lpbiInput); + This->lpbiInput = NULL; + This->cbInput = 0; + } + + LocalFree((HLOCAL)This); + + return 0; + } + + /* also release reference to the nested stream */ + if (This->pStream != NULL) + IAVIStream_Release(This->pStream); + + return --This->ref; +} + +/* lParam1: PAVISTREAM + * lParam2: LPAVICOMPRESSOPTIONS + */ +static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1, + LPARAM lParam2) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + ICINFO icinfo; + ICCOMPRESSFRAMES icFrames; + LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2; + + TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); + + /* check parameter */ + if ((LPVOID)lParam1 == NULL) + return AVIERR_BADPARAM; + + /* get infos from stream */ + IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); + if (This->sInfo.fccType != streamtypeVIDEO) + return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */ + + /* add reference to the stream */ + This->pStream = (PAVISTREAM)lParam1; + IAVIStream_AddRef(This->pStream); + + AVIFILE_Reset(This); + + if (pco != NULL && pco->fccHandler != comptypeDIB) { + /* we should compress */ + This->sInfo.fccHandler = pco->fccHandler; + + This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS); + if (This->hic == (HIC)NULL) + return AVIERR_NOCOMPRESSOR; + + /* restore saved state of codec */ + if (pco->cbParms > 0 && pco->lpParms != NULL) { + ICSetState(This->hic, pco->lpParms, pco->cbParms); + } + + /* set quality -- resolve default quality */ + This->sInfo.dwQuality = pco->dwQuality; + if (pco->dwQuality == ICQUALITY_DEFAULT) + This->sInfo.dwQuality = ICGetDefaultQuality(This->hic); + + /* get capabilities of codec */ + ICGetInfo(This->hic, &icinfo, sizeof(icinfo)); + This->dwICMFlags = icinfo.dwFlags; + + /* use keyframes? */ + if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) && + (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) { + This->lKeyFrameEvery = pco->dwKeyFrameEvery; + } else + This->lKeyFrameEvery = 1; + + /* use datarate? */ + if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) { + /* Do we have a chance to reduce size to desired one? */ + if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0) + return AVIERR_NOCOMPRESSOR; + + assert(This->sInfo.dwRate != 0); + + This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond, + This->sInfo.dwScale, This->sInfo.dwRate); + } else { + pco->dwBytesPerSecond = 0; + This->dwBytesPerFrame = 0; + } + + if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) { + memset(&icFrames, 0, sizeof(icFrames)); + icFrames.lpbiOutput = This->lpbiOutput; + icFrames.lpbiInput = This->lpbiInput; + icFrames.lFrameCount = This->sInfo.dwLength; + icFrames.lQuality = This->sInfo.dwQuality; + icFrames.lDataRate = pco->dwBytesPerSecond; + icFrames.lKeyRate = This->lKeyFrameEvery; + icFrames.dwRate = This->sInfo.dwRate; + icFrames.dwScale = This->sInfo.dwScale; + ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO, + (LPARAM)&icFrames, (LPARAM)sizeof(icFrames)); + } + } else + This->sInfo.fccHandler = comptypeDIB; + + return AVIERR_OK; +} + +static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, + LONG size) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%p,%ld)\n", iface, psi, size); + + if (psi == NULL) + return AVIERR_BADPARAM; + if (size < 0) + return AVIERR_BADSIZE; + + memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo))); + + if (size < sizeof(This->sInfo)) + return AVIERR_BUFFERTOOSMALL; + return AVIERR_OK; +} + +static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos, + LONG flags) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); + + if (flags & FIND_FROM_START) { + pos = This->sInfo.dwStart; + flags &= ~(FIND_FROM_START|FIND_PREV); + flags |= FIND_NEXT; + } + + if (flags & FIND_RET) + WARN(": FIND_RET flags will be ignored!\n"); + + if (flags & FIND_KEY) { + if (This->hic == (HIC)NULL) + return pos; /* we decompress so every frame is a keyframe */ + + if (flags & FIND_PREV) { + /* need to read old or new frames? */ + if (This->lLastKey <= pos || pos < This->lCurrent) + IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL); + + return This->lLastKey; + } + } else if (flags & FIND_ANY) { + return pos; /* We really don't know, reread is to expensive, so guess. */ + } else if (flags & FIND_FORMAT) { + if (flags & FIND_PREV) + return 0; + } + + return -1; +} + +static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG *formatsize) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + LPBITMAPINFOHEADER lpbi; + HRESULT hr; + + TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); + + if (formatsize == NULL) + return AVIERR_BADPARAM; + + if (This->pg == NULL) { + hr = AVIFILE_OpenGetFrame(This); + + if (FAILED(hr)) + return hr; + } + + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos); + if (lpbi == NULL) + return AVIERR_MEMORY; + + if (This->hic == (HIC)NULL) { + LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD); + + if (size > 0) { + if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage) + This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage; + + This->cbOutput = size; + if (format != NULL) { + if (This->lpbiOutput != NULL) + memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput)); + else + memcpy(format, lpbi, min(*formatsize, size)); + } + } + } else if (format != NULL) + memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput)); + + if (*formatsize < This->cbOutput) + hr = AVIERR_BUFFERTOOSMALL; + else + hr = AVIERR_OK; + + *formatsize = This->cbOutput; + return hr; +} + +static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos, + LPVOID format, LONG formatsize) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); + + /* check parameters */ + if (format == NULL || formatsize <= 0) + return AVIERR_BADPARAM; + + /* We can only accept RGB data for writing */ + if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) { + WARN(": need RGB data as input\n"); + return AVIERR_UNSUPPORTED; + } + + /* Input format already known? + * Changing of palette is supported, but be quiet if it's the same */ + if (This->lpbiInput != NULL) { + if (This->cbInput != formatsize) + return AVIERR_UNSUPPORTED; + + if (memcmp(format, This->lpbiInput, formatsize) == 0) + return AVIERR_OK; + } + + /* Does the nested stream support writing? */ + if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) + return AVIERR_READONLY; + + /* check if frame is already written */ + if (This->sInfo.dwLength + This->sInfo.dwStart > pos) + return AVIERR_UNSUPPORTED; + + /* check if we should compress */ + if (This->sInfo.fccHandler == 0 || + This->sInfo.fccHandler == mmioFOURCC('N','O','N','E')) + This->sInfo.fccHandler = comptypeDIB; + + /* only pass through? */ + if (This->sInfo.fccHandler == comptypeDIB) + return IAVIStream_SetFormat(This->pStream, pos, format, formatsize); + + /* initial format setting? */ + if (This->lpbiInput == NULL) { + LONG size; + + assert(This->hic != (HIC)NULL); + + /* get memory for input format */ + This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, formatsize); + if (This->lpbiInput == NULL) + return AVIERR_MEMORY; + This->cbInput = formatsize; + memcpy(This->lpbiInput, format, formatsize); + + /* get output format */ + size = ICCompressGetFormatSize(This->hic, This->lpbiInput); + if (size < sizeof(BITMAPINFOHEADER)) + return AVIERR_COMPRESSOR; + This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiOutput == NULL) + return AVIERR_MEMORY; + This->cbOutput = size; + if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK) + return AVIERR_COMPRESSOR; + + /* update AVISTREAMINFO structure */ + This->sInfo.rcFrame.right = + This->sInfo.rcFrame.left + This->lpbiOutput->biWidth; + This->sInfo.rcFrame.bottom = + This->sInfo.rcFrame.top + This->lpbiOutput->biHeight; + + /* prepare codec for compression */ + if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK) + return AVIERR_COMPRESSOR; + + /* allocate memory for compressed frame */ + size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput); + This->lpbiCur = + (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size); + if (This->lpbiCur == NULL) + return AVIERR_MEMORY; + memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput); + This->lpCur = DIBPTR(This->lpbiCur); + + /* allocate memory for last frame if needed */ + if (This->lKeyFrameEvery != 1 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { + size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput); + This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK) + return AVIERR_COMPRESSOR; + + if (This->lpbiPrev->biSizeImage == 0) { + This->lpbiPrev->biSizeImage = + DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight; + } + + /* get memory for format and picture */ + size += This->lpbiPrev->biSizeImage; + This->lpbiPrev = + (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + This->lpPrev = DIBPTR(This->lpbiPrev); + + /* prepare codec also for decompression */ + if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) + return AVIERR_COMPRESSOR; + } + } else { + /* format change -- check that's only the palette */ + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format; + + if (lpbi->biSize != This->lpbiInput->biSize || + lpbi->biWidth != This->lpbiInput->biWidth || + lpbi->biHeight != This->lpbiInput->biHeight || + lpbi->biBitCount != This->lpbiInput->biBitCount || + lpbi->biPlanes != This->lpbiInput->biPlanes || + lpbi->biCompression != This->lpbiInput->biCompression || + lpbi->biClrUsed != This->lpbiInput->biClrUsed) + return AVIERR_UNSUPPORTED; + + /* get new output format */ + if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK) + return AVIERR_BADFORMAT; + + /* restart compression */ + ICCompressEnd(This->hic); + if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK) + return AVIERR_COMPRESSOR; + + /* check if we need to restart decompresion also */ + if (This->lKeyFrameEvery != 1 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { + ICDecompressEnd(This->hic); + if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK) + return AVIERR_COMPRESSOR; + if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) + return AVIERR_COMPRESSOR; + } + } + + /* tell nested stream the new format */ + return IAVIStream_SetFormat(This->pStream, pos, + This->lpbiOutput, This->cbOutput); +} + +static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, LPLONG bytesread, + LPLONG samplesread) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + LPBITMAPINFOHEADER lpbi; + + TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, + buffersize, bytesread, samplesread); + + /* clear return parameters if given */ + if (bytesread != NULL) + *bytesread = 0; + if (samplesread != NULL) + *samplesread = 0; + + if (samples == 0) + return AVIERR_OK; + + /* check parameters */ + if (samples != 1 && (bytesread == NULL && samplesread == NULL)) + return AVIERR_BADPARAM; + if (samples == -1) /* read as much as we could */ + samples = 1; + + if (This->pg == NULL) { + HRESULT hr = AVIFILE_OpenGetFrame(This); + + if (FAILED(hr)) + return hr; + } + + /* compress or decompress? */ + if (This->hic == (HIC)NULL) { + /* decompress */ + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start); + if (lpbi == NULL) + return AVIERR_MEMORY; + + if (buffer != NULL && buffersize > 0) { + /* check buffersize */ + if (buffersize < lpbi->biSizeImage) + return AVIERR_BUFFERTOOSMALL; + + memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage); + } + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = lpbi->biSizeImage; + } else { + /* compress */ + if (This->lCurrent > start) + AVIFILE_Reset(This); + + while (start > This->lCurrent) { + HRESULT hr; + + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent); + if (lpbi == NULL) { + AVIFILE_Reset(This); + return AVIERR_MEMORY; + } + + hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi)); + if (FAILED(hr)) { + AVIFILE_Reset(This); + return hr; + } + } + + if (buffer != NULL && buffersize > 0) { + /* check buffersize */ + if (This->lpbiCur->biSizeImage > buffersize) + return AVIERR_BUFFERTOOSMALL; + + memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage); + } + + /* fill out return parameters if given */ + if (bytesread != NULL) + *bytesread = This->lpbiCur->biSizeImage; + } + + /* fill out return parameters if given */ + if (samplesread != NULL) + *samplesread = 1; + + return AVIERR_OK; +} + +static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start, + LONG samples, LPVOID buffer, + LONG buffersize, DWORD flags, + LPLONG sampwritten, + LPLONG byteswritten) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + HRESULT hr; + + TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, + buffer, buffersize, flags, sampwritten, byteswritten); + + /* clear return parameters if given */ + if (sampwritten != NULL) + *sampwritten = 0; + if (byteswritten != NULL) + *byteswritten = 0; + + /* check parameters */ + if (buffer == NULL && (buffersize > 0 || samples > 0)) + return AVIERR_BADPARAM; + + if (This->sInfo.fccHandler == comptypeDIB) { + /* only pass through */ + flags |= AVIIF_KEYFRAME; + + return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize, + flags, sampwritten, byteswritten); + } else { + /* compress data before writing to pStream */ + if (samples != 1 && (sampwritten == NULL && byteswritten == NULL)) + return AVIERR_UNSUPPORTED; + + This->lCurrent = start; + hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer); + if (FAILED(hr)) + return hr; + + if (This->lLastKey == start) + flags |= AVIIF_KEYFRAME; + + return IAVIStream_Write(This->pStream, start, samples, This->lpCur, + This->lpbiCur->biSizeImage, flags, byteswritten, + sampwritten); + } +} + +static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start, + LONG samples) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,%ld,%ld)\n", iface, start, samples); + + return IAVIStream_Delete(This->pStream, start, samples); +} + +static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LPLONG lpread) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread); + + assert(This->pStream != NULL); + + return IAVIStream_ReadData(This->pStream, fcc, lp, lpread); +} + +static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc, + LPVOID lp, LONG size) +{ + ICOM_THIS(IAVIStreamImpl,iface); + + TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size); + + assert(This->pStream != NULL); + + return IAVIStream_WriteData(This->pStream, fcc, lp, size); +} + +static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface, + LPAVISTREAMINFOW info, LONG infolen) +{ + FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); + + return E_FAIL; +} + +/***********************************************************************/ + +static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This, + LPBITMAPINFOHEADER lpbi, LPVOID lpBits) +{ + DWORD dwMinQual, dwMaxQual, dwCurQual; + DWORD dwRequest; + DWORD icmFlags = 0; + DWORD idxFlags = 0; + BOOL bDecreasedQual = FALSE; + BOOL doSizeCheck; + BOOL noPrev; + + /* make lKeyFrameEvery and at start a keyframe */ + if ((This->lKeyFrameEvery != 0 && + (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) || + This->lCurrent == This->sInfo.dwStart) { + idxFlags = AVIIF_KEYFRAME; + icmFlags = ICCOMPRESS_KEYFRAME; + } + + if (This->lKeyFrameEvery != 0) { + if (This->lCurrent == This->sInfo.dwStart) { + if (idxFlags & AVIIF_KEYFRAME) { + /* for keyframes allow to consume all unused bytes */ + dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes; + This->dwUnusedBytes = 0; + } else { + /* for non-keyframes only allow something of the unused bytes to be consumed */ + DWORD tmp1 = 0; + DWORD tmp2; + + if (This->dwBytesPerFrame >= This->dwUnusedBytes) + tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery; + tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery; + + dwRequest = This->dwBytesPerFrame - tmp1 + tmp2; + This->dwUnusedBytes -= tmp2; + } + } else + dwRequest = MAX_FRAMESIZE; + } else { + /* only one keyframe at start desired */ + if (This->lCurrent == This->sInfo.dwStart) { + dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes; + This->dwUnusedBytes = 0; + } else + dwRequest = MAX_FRAMESIZE; + } + + /* must we check for framesize to gain requested + * datarate or could we trust codec? */ + doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)); + + dwMaxQual = dwCurQual = This->sInfo.dwQuality; + dwMinQual = ICQUALITY_LOW; + + noPrev = TRUE; + if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) + noPrev = FALSE; + + do { + DWORD idxCkid = 0; + HRESULT hr; + + hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits, + &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual, + noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev); + if (hr == ICERR_NEWPALETTE) { + FIXME(": codec has changed palette -- unhandled!\n"); + } else if (hr != ICERR_OK) + return AVIERR_COMPRESSOR; + + /* need to check for framesize */ + if (! doSizeCheck) + break; + + if (dwRequest >= This->lpbiCur->biSizeImage) { + /* frame is smaller -- try to maximize quality */ + if (dwMaxQual - dwCurQual > 10) { + DWORD tmp = dwRequest / 8; + + if (tmp < MAX_FRAMESIZE_DIFF) + tmp = MAX_FRAMESIZE_DIFF; + + if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) { + tmp = dwCurQual; + dwCurQual = (dwMinQual + dwMaxQual) / 2; + dwMinQual = tmp; + continue; + } + } else + break; + } else if (dwMaxQual - dwMinQual <= 1) { + break; + } else { + dwMaxQual = dwCurQual; + + if (bDecreasedQual || dwCurQual == This->dwLastQuality) + dwCurQual = (dwMinQual + dwMaxQual) / 2; + else + FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n", + dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality); + + bDecreasedQual = TRUE; + } + } while (TRUE); + + /* remember some values */ + This->dwLastQuality = dwCurQual; + This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage; + if (icmFlags & ICCOMPRESS_KEYFRAME) + This->lLastKey = This->lCurrent; + + /* Does we manage previous frame? */ + if (This->lpPrev != NULL && This->lKeyFrameEvery != 1) + ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur, + This->lpbiPrev, This->lpPrev); + + return AVIERR_OK; +} + +static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This) +{ + LPBITMAPINFOHEADER lpbi; + LONG size; + + /* pre-conditions */ + assert(This != NULL); + assert(This->pStream != NULL); + assert(This->pg == NULL); + + This->pg = AVIStreamGetFrameOpen(This->pStream, NULL); + if (This->pg == NULL) + return AVIERR_ERROR; + + /* When we only decompress this is enough */ + if (This->sInfo.fccHandler == comptypeDIB) + return AVIERR_OK; + + assert(This->hic != (HIC)NULL); + assert(This->lpbiOutput == NULL); + + /* get input format */ + lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart); + if (lpbi == NULL) + return AVIERR_MEMORY; + + /* get memory for output format */ + size = ICCompressGetFormatSize(This->hic, lpbi); + if (size < sizeof(BITMAPINFOHEADER)) + return AVIERR_COMPRESSOR; + This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiOutput == NULL) + return AVIERR_MEMORY; + This->cbOutput = size; + + if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK) + return AVIERR_BADFORMAT; + + /* update AVISTREAMINFO structure */ + This->sInfo.rcFrame.right = + This->sInfo.rcFrame.left + This->lpbiOutput->biWidth; + This->sInfo.rcFrame.bottom = + This->sInfo.rcFrame.top + This->lpbiOutput->biHeight; + This->sInfo.dwSuggestedBufferSize = + ICCompressGetSize(This->hic, lpbi, This->lpbiOutput); + + /* prepare codec for compression */ + if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK) + return AVIERR_COMPRESSOR; + + /* allocate memory for current frame */ + size += This->sInfo.dwSuggestedBufferSize; + This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size); + if (This->lpbiCur == NULL) + return AVIERR_MEMORY; + memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput); + This->lpCur = DIBPTR(This->lpbiCur); + + /* allocate memory for last frame if needed */ + if (This->lKeyFrameEvery != 1 && + (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { + size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput); + This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK) + return AVIERR_COMPRESSOR; + + if (This->lpbiPrev->biSizeImage == 0) { + This->lpbiPrev->biSizeImage = + DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight; + } + + /* get memory for format and picture */ + size += This->lpbiPrev->biSizeImage; + This->lpbiPrev = + (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE); + if (This->lpbiPrev == NULL) + return AVIERR_MEMORY; + This->lpPrev = DIBPTR(This->lpbiPrev); + + /* prepare codec also for decompression */ + if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) + return AVIERR_COMPRESSOR; + } + + return AVIERR_OK; +}
Michael Günnewig