~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/avifil32/icmstream.c

Version: ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Copyright 2002 Michael Günnewig
  3  *
  4  * This library is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2.1 of the License, or (at your option) any later version.
  8  *
  9  * This library is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with this library; if not, write to the Free Software
 16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 17  */
 18 
 19 #include <assert.h>
 20 #include <stdarg.h>
 21 
 22 #include "windef.h"
 23 #include "winbase.h"
 24 #include "wingdi.h"
 25 #include "winuser.h"
 26 #include "winerror.h"
 27 #include "mmsystem.h"
 28 #include "vfw.h"
 29 
 30 #include "avifile_private.h"
 31 
 32 #include "wine/debug.h"
 33 
 34 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
 35 
 36 #define MAX_FRAMESIZE       (16 * 1024 * 1024)
 37 #define MAX_FRAMESIZE_DIFF  512
 38 
 39 /***********************************************************************/
 40 
 41 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
 42 static ULONG   WINAPI ICMStream_fnAddRef(IAVIStream*iface);
 43 static ULONG   WINAPI ICMStream_fnRelease(IAVIStream* iface);
 44 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
 45 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
 46 static LONG    WINAPI ICMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
 47 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
 48 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
 49 static HRESULT WINAPI ICMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
 50 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
 51 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
 52 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
 53 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
 54 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
 55 
 56 static const struct IAVIStreamVtbl iicmst = {
 57   ICMStream_fnQueryInterface,
 58   ICMStream_fnAddRef,
 59   ICMStream_fnRelease,
 60   ICMStream_fnCreate,
 61   ICMStream_fnInfo,
 62   ICMStream_fnFindSample,
 63   ICMStream_fnReadFormat,
 64   ICMStream_fnSetFormat,
 65   ICMStream_fnRead,
 66   ICMStream_fnWrite,
 67   ICMStream_fnDelete,
 68   ICMStream_fnReadData,
 69   ICMStream_fnWriteData,
 70   ICMStream_fnSetInfo
 71 };
 72 
 73 typedef struct _IAVIStreamImpl {
 74   /* IUnknown stuff */
 75   const IAVIStreamVtbl *lpVtbl;
 76   LONG               ref;
 77 
 78   /* IAVIStream stuff */
 79   PAVISTREAM         pStream;
 80   AVISTREAMINFOW     sInfo;
 81 
 82   PGETFRAME          pg;
 83   HIC                hic;
 84   DWORD              dwICMFlags;
 85 
 86   LONG               lCurrent;
 87   LONG               lLastKey;
 88   LONG               lKeyFrameEvery;
 89   DWORD              dwLastQuality;
 90   DWORD              dwBytesPerFrame;
 91   DWORD              dwUnusedBytes;
 92 
 93   LPBITMAPINFOHEADER lpbiCur;  /* current frame */
 94   LPVOID             lpCur;
 95   LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
 96   LPVOID             lpPrev;
 97 
 98   LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
 99   LONG               cbOutput;
100   LPBITMAPINFOHEADER lpbiInput;  /* input format for codec */
101   LONG               cbInput;
102 } IAVIStreamImpl;
103 
104 /***********************************************************************/
105 
106 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
107                                    LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
108 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
109 
110 static inline void AVIFILE_Reset(IAVIStreamImpl *This)
111 {
112   This->lCurrent      = -1;
113   This->lLastKey      = 0;
114   This->dwLastQuality = ICQUALITY_HIGH;
115   This->dwUnusedBytes = 0;
116 }
117 
118 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
119 {
120   IAVIStreamImpl *pstream;
121   HRESULT         hr;
122 
123   assert(riid != NULL && ppv != NULL);
124 
125   *ppv = NULL;
126 
127   pstream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
128   if (pstream == NULL)
129     return AVIERR_MEMORY;
130 
131   pstream->lpVtbl  = &iicmst;
132   AVIFILE_Reset(pstream);
133 
134   hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv);
135   if (FAILED(hr))
136     HeapFree(GetProcessHeap(), 0, pstream);
137 
138   return hr;
139 }
140 
141 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface,
142                                                   REFIID refiid, LPVOID *obj)
143 {
144   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
145 
146   TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
147 
148   if (IsEqualGUID(&IID_IUnknown, refiid) ||
149       IsEqualGUID(&IID_IAVIStream, refiid)) {
150     *obj = This;
151     IAVIStream_AddRef(iface);
152 
153     return S_OK;
154   }
155 
156   return OLE_E_ENUM_NOMORE;
157 }
158 
159 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface)
160 {
161   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
162   ULONG ref = InterlockedIncrement(&This->ref);
163 
164   TRACE("(%p) -> %d\n", iface, ref);
165 
166   /* also add reference to the nested stream */
167   if (This->pStream != NULL)
168     IAVIStream_AddRef(This->pStream);
169 
170   return ref;
171 }
172 
173 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
174 {
175   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
176   ULONG ref = InterlockedDecrement(&This->ref);
177 
178   TRACE("(%p) -> %d\n", iface, ref);
179 
180   if (ref == 0) {
181     /* destruct */
182     if (This->pg != NULL) {
183       AVIStreamGetFrameClose(This->pg);
184       This->pg = NULL;
185     }
186     if (This->pStream != NULL) {
187       IAVIStream_Release(This->pStream);
188       This->pStream = NULL;
189     }
190     if (This->hic != NULL) {
191       if (This->lpbiPrev != NULL) {
192         ICDecompressEnd(This->hic);
193         HeapFree(GetProcessHeap(), 0, This->lpbiPrev);
194         This->lpbiPrev = NULL;
195         This->lpPrev   = NULL;
196       }
197       ICCompressEnd(This->hic);
198       This->hic = NULL;
199     }
200     if (This->lpbiCur != NULL) {
201       HeapFree(GetProcessHeap(), 0, This->lpbiCur);
202       This->lpbiCur = NULL;
203       This->lpCur   = NULL;
204     }
205     if (This->lpbiOutput != NULL) {
206       HeapFree(GetProcessHeap(), 0, This->lpbiOutput);
207       This->lpbiOutput = NULL;
208       This->cbOutput   = 0;
209     }
210     if (This->lpbiInput != NULL) {
211       HeapFree(GetProcessHeap(), 0, This->lpbiInput);
212       This->lpbiInput = NULL;
213       This->cbInput   = 0;
214     }
215 
216     HeapFree(GetProcessHeap(), 0, This);
217 
218     return 0;
219   }
220 
221   /* also release reference to the nested stream */
222   if (This->pStream != NULL)
223     IAVIStream_Release(This->pStream);
224 
225   return ref;
226 }
227 
228 /* lParam1: PAVISTREAM
229  * lParam2: LPAVICOMPRESSOPTIONS
230  */
231 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
232                                           LPARAM lParam2)
233 {
234   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
235 
236   ICINFO               icinfo;
237   ICCOMPRESSFRAMES     icFrames;
238   LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2;
239 
240   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
241 
242   /* check parameter */
243   if ((LPVOID)lParam1 == NULL)
244     return AVIERR_BADPARAM;
245 
246   /* get infos from stream */
247   IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
248   if (This->sInfo.fccType != streamtypeVIDEO)
249     return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */
250 
251   /* add reference to the stream */
252   This->pStream = (PAVISTREAM)lParam1;
253   IAVIStream_AddRef(This->pStream);
254 
255   AVIFILE_Reset(This);
256 
257   if (pco != NULL && pco->fccHandler != comptypeDIB) {
258     /* we should compress */
259     This->sInfo.fccHandler = pco->fccHandler;
260 
261     This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS);
262     if (This->hic == NULL)
263       return AVIERR_NOCOMPRESSOR;
264 
265     /* restore saved state of codec */
266     if (pco->cbParms > 0 && pco->lpParms != NULL) {
267       ICSetState(This->hic, pco->lpParms, pco->cbParms);
268     }
269 
270     /* set quality -- resolve default quality */
271     This->sInfo.dwQuality = pco->dwQuality;
272     if (pco->dwQuality == ICQUALITY_DEFAULT)
273       This->sInfo.dwQuality = ICGetDefaultQuality(This->hic);
274 
275     /* get capabilities of codec */
276     ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
277     This->dwICMFlags = icinfo.dwFlags;
278 
279     /* use keyframes? */
280     if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
281         (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
282       This->lKeyFrameEvery = pco->dwKeyFrameEvery;
283     } else
284       This->lKeyFrameEvery = 1;
285 
286     /* use datarate? */
287     if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) {
288       /* Do we have a chance to reduce size to desired one? */
289       if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)
290         return AVIERR_NOCOMPRESSOR;
291 
292       assert(This->sInfo.dwRate != 0);
293 
294       This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond,
295                                      This->sInfo.dwScale, This->sInfo.dwRate);
296     } else {
297       pco->dwBytesPerSecond = 0;
298       This->dwBytesPerFrame = 0;
299     }
300 
301     if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) {
302       memset(&icFrames, 0, sizeof(icFrames));
303       icFrames.lpbiOutput  = This->lpbiOutput;
304       icFrames.lpbiInput   = This->lpbiInput;
305       icFrames.lFrameCount = This->sInfo.dwLength;
306       icFrames.lQuality    = This->sInfo.dwQuality;
307       icFrames.lDataRate   = pco->dwBytesPerSecond;
308       icFrames.lKeyRate    = This->lKeyFrameEvery;
309       icFrames.dwRate      = This->sInfo.dwRate;
310       icFrames.dwScale     = This->sInfo.dwScale;
311       ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO,
312                     (LPARAM)&icFrames, (LPARAM)sizeof(icFrames));
313     }
314   } else
315     This->sInfo.fccHandler = comptypeDIB;
316 
317   return AVIERR_OK;
318 }
319 
320 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
321                                         LONG size)
322 {
323   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
324 
325   TRACE("(%p,%p,%d)\n", iface, psi, size);
326 
327   if (psi == NULL)
328     return AVIERR_BADPARAM;
329   if (size < 0)
330     return AVIERR_BADSIZE;
331 
332   memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
333 
334   if ((DWORD)size < sizeof(This->sInfo))
335     return AVIERR_BUFFERTOOSMALL;
336   return AVIERR_OK;
337 }
338 
339 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
340                                            LONG flags)
341 {
342   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
343 
344   TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
345 
346   if (flags & FIND_FROM_START) {
347     pos = This->sInfo.dwStart;
348     flags &= ~(FIND_FROM_START|FIND_PREV);
349     flags |= FIND_NEXT;
350   }
351 
352   if (flags & FIND_RET)
353     WARN(": FIND_RET flags will be ignored!\n");
354 
355   if (flags & FIND_KEY) {
356     if (This->hic == NULL)
357       return pos; /* we decompress so every frame is a keyframe */
358 
359     if (flags & FIND_PREV) {
360       /* need to read old or new frames? */
361       if (This->lLastKey <= pos || pos < This->lCurrent)
362         IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL);
363 
364       return This->lLastKey;
365     }
366   } else if (flags & FIND_ANY) {
367     return pos; /* We really don't know, reread is to expensive, so guess. */
368   } else if (flags & FIND_FORMAT) {
369     if (flags & FIND_PREV)
370       return 0;
371   }
372 
373   return -1;
374 }
375 
376 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
377                                               LPVOID format, LONG *formatsize)
378 {
379   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
380 
381   LPBITMAPINFOHEADER lpbi;
382   HRESULT            hr;
383 
384   TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
385 
386   if (formatsize == NULL)
387     return AVIERR_BADPARAM;
388 
389   if (This->pg == NULL) {
390     hr = AVIFILE_OpenGetFrame(This);
391 
392     if (FAILED(hr))
393       return hr;
394   }
395 
396   lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
397   if (lpbi == NULL)
398     return AVIERR_MEMORY;
399 
400   if (This->hic == NULL) {
401     LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
402 
403     if (size > 0) {
404       if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage)
405         This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage;
406 
407       This->cbOutput = size;
408       if (format != NULL) {
409         if (This->lpbiOutput != NULL)
410           memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
411         else
412           memcpy(format, lpbi, min(*formatsize, size));
413       }
414     }
415   } else if (format != NULL)
416     memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
417 
418   if (*formatsize < This->cbOutput)
419     hr = AVIERR_BUFFERTOOSMALL;
420   else
421     hr = AVIERR_OK;
422 
423   *formatsize = This->cbOutput;
424   return hr;
425 }
426 
427 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos,
428                                              LPVOID format, LONG formatsize)
429 {
430   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
431 
432   TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
433 
434   /* check parameters */
435   if (format == NULL || formatsize <= 0)
436     return AVIERR_BADPARAM;
437 
438   /* We can only accept RGB data for writing */
439   if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) {
440     WARN(": need RGB data as input\n");
441     return AVIERR_UNSUPPORTED;
442   }
443 
444   /* Input format already known?
445    * Changing of palette is supported, but be quiet if it's the same */
446   if (This->lpbiInput != NULL) {
447     if (This->cbInput != formatsize)
448       return AVIERR_UNSUPPORTED;
449 
450     if (memcmp(format, This->lpbiInput, formatsize) == 0)
451       return AVIERR_OK;
452   }
453 
454   /* Does the nested stream support writing? */
455   if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
456     return AVIERR_READONLY;
457 
458   /* check if frame is already written */
459   if (This->sInfo.dwLength + This->sInfo.dwStart > pos)
460     return AVIERR_UNSUPPORTED;
461 
462   /* check if we should compress */
463   if (This->sInfo.fccHandler == 0 ||
464       This->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))
465     This->sInfo.fccHandler = comptypeDIB;
466 
467   /* only pass through? */
468   if (This->sInfo.fccHandler == comptypeDIB)
469     return IAVIStream_SetFormat(This->pStream, pos, format, formatsize);
470 
471   /* initial format setting? */
472   if (This->lpbiInput == NULL) {
473     ULONG size;
474 
475     assert(This->hic != NULL);
476 
477     /* get memory for input format */
478     This->lpbiInput = HeapAlloc(GetProcessHeap(), 0, formatsize);
479     if (This->lpbiInput == NULL)
480       return AVIERR_MEMORY;
481     This->cbInput = formatsize;
482     memcpy(This->lpbiInput, format, formatsize);
483 
484     /* get output format */
485     size = ICCompressGetFormatSize(This->hic, This->lpbiInput);
486     if (size < sizeof(BITMAPINFOHEADER))
487       return AVIERR_COMPRESSOR;
488     This->lpbiOutput = HeapAlloc(GetProcessHeap(), 0, size);
489     if (This->lpbiOutput == NULL)
490       return AVIERR_MEMORY;
491     This->cbOutput = size;
492     if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK)
493       return AVIERR_COMPRESSOR;
494 
495     /* update AVISTREAMINFO structure */
496     This->sInfo.rcFrame.right  =
497       This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
498     This->sInfo.rcFrame.bottom =
499       This->sInfo.rcFrame.top  + This->lpbiOutput->biHeight;
500 
501     /* prepare codec for compression */
502     if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK)
503       return AVIERR_COMPRESSOR;
504 
505     /* allocate memory for compressed frame */
506     size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput);
507     This->lpbiCur = HeapAlloc(GetProcessHeap(), 0, This->cbOutput + size);
508     if (This->lpbiCur == NULL)
509       return AVIERR_MEMORY;
510     memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
511     This->lpCur = DIBPTR(This->lpbiCur);
512 
513     /* allocate memory for last frame if needed */
514     if (This->lKeyFrameEvery != 1 &&
515         (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
516       size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
517       This->lpbiPrev = HeapAlloc(GetProcessHeap(), 0, size);
518       if (This->lpbiPrev == NULL)
519         return AVIERR_MEMORY;
520       if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
521         return AVIERR_COMPRESSOR;
522 
523       if (This->lpbiPrev->biSizeImage == 0) {
524         This->lpbiPrev->biSizeImage =
525           DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
526       }
527 
528       /* get memory for format and picture */
529       size += This->lpbiPrev->biSizeImage;
530       This->lpbiPrev = HeapReAlloc(GetProcessHeap(), 0, This->lpbiPrev, size);
531       if (This->lpbiPrev == NULL)
532         return AVIERR_MEMORY;
533       This->lpPrev = DIBPTR(This->lpbiPrev);
534 
535       /* prepare codec also for decompression */
536       if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
537         return AVIERR_COMPRESSOR;
538     }
539   } else {
540     /* format change -- check that's only the palette */
541     LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
542 
543     if (lpbi->biSize != This->lpbiInput->biSize ||
544         lpbi->biWidth != This->lpbiInput->biWidth ||
545         lpbi->biHeight != This->lpbiInput->biHeight ||
546         lpbi->biBitCount != This->lpbiInput->biBitCount ||
547         lpbi->biPlanes != This->lpbiInput->biPlanes ||
548         lpbi->biCompression != This->lpbiInput->biCompression ||
549         lpbi->biClrUsed != This->lpbiInput->biClrUsed)
550       return AVIERR_UNSUPPORTED;
551 
552     /* get new output format */
553     if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
554       return AVIERR_BADFORMAT;
555 
556     /* restart compression */
557     ICCompressEnd(This->hic);
558     if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
559       return AVIERR_COMPRESSOR;
560 
561     /* check if we need to restart decompression also */
562     if (This->lKeyFrameEvery != 1 &&
563         (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
564       ICDecompressEnd(This->hic);
565       if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
566         return AVIERR_COMPRESSOR;
567       if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
568         return AVIERR_COMPRESSOR;
569     }
570   }
571 
572   /* tell nested stream the new format */
573   return IAVIStream_SetFormat(This->pStream, pos,
574                               This->lpbiOutput, This->cbOutput);
575 }
576 
577 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
578                                         LONG samples, LPVOID buffer,
579                                         LONG buffersize, LPLONG bytesread,
580                                         LPLONG samplesread)
581 {
582   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
583 
584   LPBITMAPINFOHEADER lpbi;
585 
586   TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
587         buffersize, bytesread, samplesread);
588 
589   /* clear return parameters if given */
590   if (bytesread != NULL)
591     *bytesread = 0;
592   if (samplesread != NULL)
593     *samplesread = 0;
594 
595   if (samples == 0)
596     return AVIERR_OK;
597 
598   /* check parameters */
599   if (samples != 1 && (bytesread == NULL && samplesread == NULL))
600     return AVIERR_BADPARAM;
601   if (samples == -1) /* read as much as we could */
602     samples = 1;
603 
604   if (This->pg == NULL) {
605     HRESULT hr = AVIFILE_OpenGetFrame(This);
606 
607     if (FAILED(hr))
608       return hr;
609   }
610 
611   /* compress or decompress? */
612   if (This->hic == NULL) {
613     /* decompress */
614     lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
615     if (lpbi == NULL)
616       return AVIERR_MEMORY;
617 
618     if (buffer != NULL && buffersize > 0) {
619       /* check buffersize */
620       if (buffersize < lpbi->biSizeImage)
621         return AVIERR_BUFFERTOOSMALL;
622 
623       memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
624     }
625 
626     /* fill out return parameters if given */
627     if (bytesread != NULL)
628       *bytesread = lpbi->biSizeImage;
629   } else {
630     /* compress */
631     if (This->lCurrent > start)
632       AVIFILE_Reset(This);
633 
634     while (start > This->lCurrent) {
635       HRESULT hr;
636 
637       lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
638       if (lpbi == NULL) {
639         AVIFILE_Reset(This);
640         return AVIERR_MEMORY;
641       }
642 
643       hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
644       if (FAILED(hr)) {
645         AVIFILE_Reset(This);
646         return hr;
647       }
648     }
649 
650     if (buffer != NULL && buffersize > 0) {
651       /* check buffersize */
652       if (This->lpbiCur->biSizeImage > buffersize)
653         return AVIERR_BUFFERTOOSMALL;
654 
655       memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
656     }
657 
658     /* fill out return parameters if given */
659     if (bytesread != NULL)
660       *bytesread = This->lpbiCur->biSizeImage;
661   }
662 
663   /* fill out return parameters if given */
664   if (samplesread != NULL)
665     *samplesread = 1;
666 
667   return AVIERR_OK;
668 }
669 
670 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
671                                          LONG samples, LPVOID buffer,
672                                          LONG buffersize, DWORD flags,
673                                          LPLONG sampwritten,
674                                          LPLONG byteswritten)
675 {
676   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
677 
678   HRESULT hr;
679 
680   TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
681         buffer, buffersize, flags, sampwritten, byteswritten);
682 
683   /* clear return parameters if given */
684   if (sampwritten != NULL)
685     *sampwritten = 0;
686   if (byteswritten != NULL)
687     *byteswritten = 0;
688 
689   /* check parameters */
690   if (buffer == NULL && (buffersize > 0 || samples > 0))
691     return AVIERR_BADPARAM;
692 
693   if (This->sInfo.fccHandler == comptypeDIB) {
694     /* only pass through */
695     flags |= AVIIF_KEYFRAME;
696 
697     return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
698                             flags, sampwritten, byteswritten);
699   } else {
700     /* compress data before writing to pStream */
701     if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
702       return AVIERR_UNSUPPORTED;
703 
704     This->lCurrent = start;
705     hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
706     if (FAILED(hr))
707       return hr;
708 
709     if (This->lLastKey == start)
710       flags |= AVIIF_KEYFRAME;
711 
712     return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
713                             This->lpbiCur->biSizeImage, flags, byteswritten,
714                             sampwritten);
715   }
716 }
717 
718 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
719                                           LONG samples)
720 {
721   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
722 
723   TRACE("(%p,%d,%d)\n", iface, start, samples);
724 
725   return IAVIStream_Delete(This->pStream, start, samples);
726 }
727 
728 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
729                                             LPVOID lp, LPLONG lpread)
730 {
731   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
732 
733   TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
734 
735   assert(This->pStream != NULL);
736 
737   return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
738 }
739 
740 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
741                                              LPVOID lp, LONG size)
742 {
743   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
744 
745   TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
746 
747   assert(This->pStream != NULL);
748 
749   return IAVIStream_WriteData(This->pStream, fcc, lp, size);
750 }
751 
752 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
753                                            LPAVISTREAMINFOW info, LONG infolen)
754 {
755   FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
756 
757   return E_FAIL;
758 }
759 
760 /***********************************************************************/
761 
762 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
763                                    LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
764 {
765   DWORD dwMinQual, dwMaxQual, dwCurQual;
766   DWORD dwRequest;
767   DWORD icmFlags = 0;
768   DWORD idxFlags = 0;
769   BOOL  bDecreasedQual = FALSE;
770   BOOL  doSizeCheck;
771   BOOL  noPrev;
772 
773   /* make lKeyFrameEvery and at start a keyframe */
774   if ((This->lKeyFrameEvery != 0 &&
775        (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
776       This->lCurrent == This->sInfo.dwStart) {
777     idxFlags = AVIIF_KEYFRAME;
778     icmFlags = ICCOMPRESS_KEYFRAME;
779   }
780 
781   if (This->lKeyFrameEvery != 0) {
782     if (This->lCurrent == This->sInfo.dwStart) {
783       if (idxFlags & AVIIF_KEYFRAME) {
784         /* for keyframes allow to consume all unused bytes */
785         dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
786         This->dwUnusedBytes = 0;
787       } else {
788         /* for non-keyframes only allow something of the unused bytes to be consumed */
789         DWORD tmp1 = 0;
790         DWORD tmp2;
791 
792         if (This->dwBytesPerFrame >= This->dwUnusedBytes)
793           tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
794         tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
795 
796         dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
797         This->dwUnusedBytes -= tmp2;
798       }
799     } else
800       dwRequest = MAX_FRAMESIZE;
801   } else {
802     /* only one keyframe at start desired */
803     if (This->lCurrent == This->sInfo.dwStart) {
804       dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
805       This->dwUnusedBytes = 0;
806     } else
807       dwRequest = MAX_FRAMESIZE;
808   }
809 
810   /* must we check for framesize to gain requested
811    * datarate or could we trust codec? */
812   doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
813 
814   dwMaxQual = dwCurQual = This->sInfo.dwQuality;
815   dwMinQual = ICQUALITY_LOW;
816 
817   noPrev = TRUE;
818   if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 && 
819       (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
820     noPrev = FALSE;
821 
822   do {
823     DWORD   idxCkid = 0;
824     HRESULT hr;
825 
826     hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
827                     &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
828                     noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
829     if (hr == ICERR_NEWPALETTE) {
830       FIXME(": codec has changed palette -- unhandled!\n");
831     } else if (hr != ICERR_OK)
832       return AVIERR_COMPRESSOR;
833 
834     /* need to check for framesize */
835     if (! doSizeCheck)
836       break;
837 
838     if (dwRequest >= This->lpbiCur->biSizeImage) {
839       /* frame is smaller -- try to maximize quality */
840       if (dwMaxQual - dwCurQual > 10) {
841         DWORD tmp = dwRequest / 8;
842 
843         if (tmp < MAX_FRAMESIZE_DIFF)
844           tmp = MAX_FRAMESIZE_DIFF;
845 
846         if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
847           tmp = dwCurQual;
848           dwCurQual = (dwMinQual + dwMaxQual) / 2;