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