dlls/avifil32: more bug fixes, added compressed stream handlers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux