- Fixed some signed/unsigned mismatches.
[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
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "winnls.h"
25 #include "winerror.h"
26 #include "windowsx.h"
27 #include "mmsystem.h"
28 #include "vfw.h"
29 #include "msacm.h"
30
31 #include "avifile_private.h"
32
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
36
37 #define MAX_FRAMESIZE       (16 * 1024 * 1024)
38 #define MAX_FRAMESIZE_DIFF  512
39
40 /***********************************************************************/
41
42 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
43 static ULONG   WINAPI ICMStream_fnAddRef(IAVIStream*iface);
44 static ULONG   WINAPI ICMStream_fnRelease(IAVIStream* iface);
45 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
46 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
47 static LONG    WINAPI ICMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
48 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
49 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
50 static HRESULT WINAPI ICMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
51 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
52 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
53 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
54 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
55 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
56
57 struct ICOM_VTABLE(IAVIStream) iicmst = {
58   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
59   ICMStream_fnQueryInterface,
60   ICMStream_fnAddRef,
61   ICMStream_fnRelease,
62   ICMStream_fnCreate,
63   ICMStream_fnInfo,
64   ICMStream_fnFindSample,
65   ICMStream_fnReadFormat,
66   ICMStream_fnSetFormat,
67   ICMStream_fnRead,
68   ICMStream_fnWrite,
69   ICMStream_fnDelete,
70   ICMStream_fnReadData,
71   ICMStream_fnWriteData,
72   ICMStream_fnSetInfo
73 };
74
75 typedef struct _IAVIStreamImpl {
76   /* IUnknown stuff */
77   ICOM_VFIELD(IAVIStream);
78   DWORD              ref;
79
80   /* IAVIStream stuff */
81   PAVISTREAM         pStream;
82   AVISTREAMINFOW     sInfo;
83
84   PGETFRAME          pg;
85   HIC                hic;
86   DWORD              dwICMFlags;
87
88   LONG               lCurrent;
89   LONG               lLastKey;
90   LONG               lKeyFrameEvery;
91   DWORD              dwLastQuality;
92   DWORD              dwBytesPerFrame;
93   DWORD              dwUnusedBytes;
94
95   LPBITMAPINFOHEADER lpbiCur;  /* current frame */
96   LPVOID             lpCur;
97   LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
98   LPVOID             lpPrev;
99
100   LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
101   LONG               cbOutput;
102   LPBITMAPINFOHEADER lpbiInput;  /* input format for codec */
103   LONG               cbInput;
104 } IAVIStreamImpl;
105
106 /***********************************************************************/
107
108 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
109                                    LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
110 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
111
112 inline void AVIFILE_Reset(IAVIStreamImpl *This)
113 {
114   This->lCurrent      = -1;
115   This->lLastKey      = 0;
116   This->dwLastQuality = ICQUALITY_HIGH;
117   This->dwUnusedBytes = 0;
118 }
119
120 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
121 {
122   IAVIStreamImpl *pstream;
123   HRESULT         hr;
124
125   assert(riid != NULL && ppv != NULL);
126
127   *ppv = NULL;
128
129   pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
130   if (pstream == NULL)
131     return AVIERR_MEMORY;
132
133   pstream->lpVtbl  = &iicmst;
134   AVIFILE_Reset(pstream);
135
136   hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
137   if (FAILED(hr))
138     LocalFree((HLOCAL)pstream);
139
140   return hr;
141 }
142
143 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface,
144                                                   REFIID refiid, LPVOID *obj)
145 {
146   ICOM_THIS(IAVIStreamImpl,iface);
147
148   TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
149
150   if (IsEqualGUID(&IID_IUnknown, refiid) ||
151       IsEqualGUID(&IID_IAVIStream, refiid)) {
152     *obj = This;
153     IAVIStream_AddRef(iface);
154
155     return S_OK;
156   }
157
158   return OLE_E_ENUM_NOMORE;
159 }
160
161 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface)
162 {
163   ICOM_THIS(IAVIStreamImpl,iface);
164
165   TRACE("(%p) -> %ld\n", iface, This->ref + 1);
166
167   /* also add reference to the nested stream */
168   if (This->pStream != NULL)
169     IAVIStream_AddRef(This->pStream);
170
171   return ++(This->ref);
172 }
173
174 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
175 {
176   ICOM_THIS(IAVIStreamImpl,iface);
177
178   TRACE("(%p) -> %ld\n", iface, This->ref - 1);
179
180   if (This->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         GlobalFreePtr(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       GlobalFreePtr(This->lpbiCur);
202       This->lpbiCur = NULL;
203       This->lpCur   = NULL;
204     }
205     if (This->lpbiOutput != NULL) {
206       GlobalFreePtr(This->lpbiOutput);
207       This->lpbiOutput = NULL;
208       This->cbOutput   = 0;
209     }
210     if (This->lpbiInput != NULL) {
211       GlobalFreePtr(This->lpbiInput);
212       This->lpbiInput = NULL;
213       This->cbInput   = 0;
214     }
215
216     LocalFree((HLOCAL)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 --This->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   ICOM_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   ICOM_THIS(IAVIStreamImpl,iface);
324
325   TRACE("(%p,%p,%ld)\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   ICOM_THIS(IAVIStreamImpl,iface);
343
344   TRACE("(%p,%ld,0x%08lX)\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   ICOM_THIS(IAVIStreamImpl,iface);
380
381   LPBITMAPINFOHEADER lpbi;
382   HRESULT            hr;
383
384   TRACE("(%p,%ld,%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   ICOM_THIS(IAVIStreamImpl,iface);
431
432   TRACE("(%p,%ld,%p,%ld)\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     LONG size;
474
475     assert(This->hic != NULL);
476
477     /* get memory for input format */
478     This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, 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 = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, 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 =
508       (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size);
509     if (This->lpbiCur == NULL)
510       return AVIERR_MEMORY;
511     memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
512     This->lpCur = DIBPTR(This->lpbiCur);
513
514     /* allocate memory for last frame if needed */
515     if (This->lKeyFrameEvery != 1 &&
516         (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
517       size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
518       This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
519       if (This->lpbiPrev == NULL)
520         return AVIERR_MEMORY;
521       if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
522         return AVIERR_COMPRESSOR;
523
524       if (This->lpbiPrev->biSizeImage == 0) {
525         This->lpbiPrev->biSizeImage =
526           DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
527       }
528
529       /* get memory for format and picture */
530       size += This->lpbiPrev->biSizeImage;
531       This->lpbiPrev =
532        (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
533       if (This->lpbiPrev == NULL)
534         return AVIERR_MEMORY;
535       This->lpPrev = DIBPTR(This->lpbiPrev);
536
537       /* prepare codec also for decompression */
538       if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
539         return AVIERR_COMPRESSOR;
540     }
541   } else {
542     /* format change -- check that's only the palette */
543     LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
544
545     if (lpbi->biSize != This->lpbiInput->biSize ||
546         lpbi->biWidth != This->lpbiInput->biWidth ||
547         lpbi->biHeight != This->lpbiInput->biHeight ||
548         lpbi->biBitCount != This->lpbiInput->biBitCount ||
549         lpbi->biPlanes != This->lpbiInput->biPlanes ||
550         lpbi->biCompression != This->lpbiInput->biCompression ||
551         lpbi->biClrUsed != This->lpbiInput->biClrUsed)
552       return AVIERR_UNSUPPORTED;
553
554     /* get new output format */
555     if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
556       return AVIERR_BADFORMAT;
557
558     /* restart compression */
559     ICCompressEnd(This->hic);
560     if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
561       return AVIERR_COMPRESSOR;
562
563     /* check if we need to restart decompresion also */
564     if (This->lKeyFrameEvery != 1 &&
565         (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
566       ICDecompressEnd(This->hic);
567       if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
568         return AVIERR_COMPRESSOR;
569       if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
570         return AVIERR_COMPRESSOR;
571     }
572   }
573
574   /* tell nested stream the new format */
575   return IAVIStream_SetFormat(This->pStream, pos,
576                               This->lpbiOutput, This->cbOutput);
577 }
578
579 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
580                                         LONG samples, LPVOID buffer,
581                                         LONG buffersize, LPLONG bytesread,
582                                         LPLONG samplesread)
583 {
584   ICOM_THIS(IAVIStreamImpl,iface);
585
586   LPBITMAPINFOHEADER lpbi;
587
588   TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
589         buffersize, bytesread, samplesread);
590
591   /* clear return parameters if given */
592   if (bytesread != NULL)
593     *bytesread = 0;
594   if (samplesread != NULL)
595     *samplesread = 0;
596
597   if (samples == 0)
598     return AVIERR_OK;
599
600   /* check parameters */
601   if (samples != 1 && (bytesread == NULL && samplesread == NULL))
602     return AVIERR_BADPARAM;
603   if (samples == -1) /* read as much as we could */
604     samples = 1;
605
606   if (This->pg == NULL) {
607     HRESULT hr = AVIFILE_OpenGetFrame(This);
608
609     if (FAILED(hr))
610       return hr;
611   }
612
613   /* compress or decompress? */
614   if (This->hic == NULL) {
615     /* decompress */
616     lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
617     if (lpbi == NULL)
618       return AVIERR_MEMORY;
619
620     if (buffer != NULL && buffersize > 0) {
621       /* check buffersize */
622       if (buffersize < lpbi->biSizeImage)
623         return AVIERR_BUFFERTOOSMALL;
624
625       memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
626     }
627
628     /* fill out return parameters if given */
629     if (bytesread != NULL)
630       *bytesread = lpbi->biSizeImage;
631   } else {
632     /* compress */
633     if (This->lCurrent > start)
634       AVIFILE_Reset(This);
635
636     while (start > This->lCurrent) {
637       HRESULT hr;
638
639       lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
640       if (lpbi == NULL) {
641         AVIFILE_Reset(This);
642         return AVIERR_MEMORY;
643       }
644
645       hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
646       if (FAILED(hr)) {
647         AVIFILE_Reset(This);
648         return hr;
649       }
650     }
651
652     if (buffer != NULL && buffersize > 0) {
653       /* check buffersize */
654       if (This->lpbiCur->biSizeImage > buffersize)
655         return AVIERR_BUFFERTOOSMALL;
656
657       memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
658     }
659
660     /* fill out return parameters if given */
661     if (bytesread != NULL)
662       *bytesread = This->lpbiCur->biSizeImage;
663   }
664
665   /* fill out return parameters if given */
666   if (samplesread != NULL)
667     *samplesread = 1;
668
669   return AVIERR_OK;
670 }
671
672 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
673                                          LONG samples, LPVOID buffer,
674                                          LONG buffersize, DWORD flags,
675                                          LPLONG sampwritten,
676                                          LPLONG byteswritten)
677 {
678   ICOM_THIS(IAVIStreamImpl,iface);
679
680   HRESULT hr;
681
682   TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
683         buffer, buffersize, flags, sampwritten, byteswritten);
684
685   /* clear return parameters if given */
686   if (sampwritten != NULL)
687     *sampwritten = 0;
688   if (byteswritten != NULL)
689     *byteswritten = 0;
690
691   /* check parameters */
692   if (buffer == NULL && (buffersize > 0 || samples > 0))
693     return AVIERR_BADPARAM;
694
695   if (This->sInfo.fccHandler == comptypeDIB) {
696     /* only pass through */
697     flags |= AVIIF_KEYFRAME;
698
699     return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
700                             flags, sampwritten, byteswritten);
701   } else {
702     /* compress data before writing to pStream */
703     if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
704       return AVIERR_UNSUPPORTED;
705
706     This->lCurrent = start;
707     hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
708     if (FAILED(hr))
709       return hr;
710
711     if (This->lLastKey == start)
712       flags |= AVIIF_KEYFRAME;
713
714     return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
715                             This->lpbiCur->biSizeImage, flags, byteswritten,
716                             sampwritten);
717   }
718 }
719
720 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
721                                           LONG samples)
722 {
723   ICOM_THIS(IAVIStreamImpl,iface);
724
725   TRACE("(%p,%ld,%ld)\n", iface, start, samples);
726
727   return IAVIStream_Delete(This->pStream, start, samples);
728 }
729
730 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
731                                             LPVOID lp, LPLONG lpread)
732 {
733   ICOM_THIS(IAVIStreamImpl,iface);
734
735   TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
736
737   assert(This->pStream != NULL);
738
739   return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
740 }
741
742 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
743                                              LPVOID lp, LONG size)
744 {
745   ICOM_THIS(IAVIStreamImpl,iface);
746
747   TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
748
749   assert(This->pStream != NULL);
750
751   return IAVIStream_WriteData(This->pStream, fcc, lp, size);
752 }
753
754 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
755                                            LPAVISTREAMINFOW info, LONG infolen)
756 {
757   FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
758
759   return E_FAIL;
760 }
761
762 /***********************************************************************/
763
764 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
765                                    LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
766 {
767   DWORD dwMinQual, dwMaxQual, dwCurQual;
768   DWORD dwRequest;
769   DWORD icmFlags = 0;
770   DWORD idxFlags = 0;
771   BOOL  bDecreasedQual = FALSE;
772   BOOL  doSizeCheck;
773   BOOL  noPrev;
774
775   /* make lKeyFrameEvery and at start a keyframe */
776   if ((This->lKeyFrameEvery != 0 &&
777        (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
778       This->lCurrent == This->sInfo.dwStart) {
779     idxFlags = AVIIF_KEYFRAME;
780     icmFlags = ICCOMPRESS_KEYFRAME;
781   }
782
783   if (This->lKeyFrameEvery != 0) {
784     if (This->lCurrent == This->sInfo.dwStart) {
785       if (idxFlags & AVIIF_KEYFRAME) {
786         /* for keyframes allow to consume all unused bytes */
787         dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
788         This->dwUnusedBytes = 0;
789       } else {
790         /* for non-keyframes only allow something of the unused bytes to be consumed */
791         DWORD tmp1 = 0;
792         DWORD tmp2;
793
794         if (This->dwBytesPerFrame >= This->dwUnusedBytes)
795           tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
796         tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
797
798         dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
799         This->dwUnusedBytes -= tmp2;
800       }
801     } else
802       dwRequest = MAX_FRAMESIZE;
803   } else {
804     /* only one keyframe at start desired */
805     if (This->lCurrent == This->sInfo.dwStart) {
806       dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
807       This->dwUnusedBytes = 0;
808     } else
809       dwRequest = MAX_FRAMESIZE;
810   }
811
812   /* must we check for framesize to gain requested
813    * datarate or could we trust codec? */
814   doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
815
816   dwMaxQual = dwCurQual = This->sInfo.dwQuality;
817   dwMinQual = ICQUALITY_LOW;
818
819   noPrev = TRUE;
820   if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 && 
821       (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
822     noPrev = FALSE;
823
824   do {
825     DWORD   idxCkid = 0;
826     HRESULT hr;
827
828     hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
829                     &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
830                     noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
831     if (hr == ICERR_NEWPALETTE) {
832       FIXME(": codec has changed palette -- unhandled!\n");
833     } else if (hr != ICERR_OK)
834       return AVIERR_COMPRESSOR;
835
836     /* need to check for framesize */
837     if (! doSizeCheck)
838       break;
839
840     if (dwRequest >= This->lpbiCur->biSizeImage) {
841       /* frame is smaller -- try to maximize quality */
842       if (dwMaxQual - dwCurQual > 10) {
843         DWORD tmp = dwRequest / 8;
844
845         if (tmp < MAX_FRAMESIZE_DIFF)
846           tmp = MAX_FRAMESIZE_DIFF;
847
848         if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
849           tmp = dwCurQual;
850           dwCurQual = (dwMinQual + dwMaxQual) / 2;
851           dwMinQual = tmp;
852           continue;
853         }
854       } else
855         break;
856     } else if (dwMaxQual - dwMinQual <= 1) {
857       break;
858     } else {
859       dwMaxQual = dwCurQual;
860
861       if (bDecreasedQual || dwCurQual == This->dwLastQuality)
862         dwCurQual = (dwMinQual + dwMaxQual) / 2;
863       else
864         FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
865               dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
866
867       bDecreasedQual = TRUE;
868     }
869   } while (TRUE);
870
871   /* remember some values */
872   This->dwLastQuality = dwCurQual;
873   This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
874   if (icmFlags & ICCOMPRESS_KEYFRAME)
875     This->lLastKey = This->lCurrent;
876
877   /* Does we manage previous frame? */
878   if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
879     ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
880                  This->lpbiPrev, This->lpPrev);
881
882   return AVIERR_OK;
883 }
884
885 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
886 {
887   LPBITMAPINFOHEADER lpbi;
888   DWORD              size;
889
890   /* pre-conditions */
891   assert(This != NULL);
892   assert(This->pStream != NULL);
893   assert(This->pg == NULL);
894
895   This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
896   if (This->pg == NULL)
897     return AVIERR_ERROR;
898
899   /* When we only decompress this is enough */
900   if (This->sInfo.fccHandler == comptypeDIB)
901     return AVIERR_OK;
902
903   assert(This->hic != NULL);
904   assert(This->lpbiOutput == NULL);
905
906   /* get input format */
907   lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
908   if (lpbi == NULL)
909     return AVIERR_MEMORY;
910
911   /* get memory for output format */
912   size = ICCompressGetFormatSize(This->hic, lpbi);
913   if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER))
914     return AVIERR_COMPRESSOR;
915   This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
916   if (This->lpbiOutput == NULL)
917     return AVIERR_MEMORY;
918   This->cbOutput = size;
919
920   if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
921     return AVIERR_BADFORMAT;
922
923   /* update AVISTREAMINFO structure */
924   This->sInfo.rcFrame.right  =
925     This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
926   This->sInfo.rcFrame.bottom =
927     This->sInfo.rcFrame.top  + This->lpbiOutput->biHeight;
928   This->sInfo.dwSuggestedBufferSize =
929     ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
930
931   /* prepare codec for compression */
932   if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
933     return AVIERR_COMPRESSOR;
934
935   /* allocate memory for current frame */
936   size += This->sInfo.dwSuggestedBufferSize;
937   This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size);
938   if (This->lpbiCur == NULL)
939     return AVIERR_MEMORY;
940   memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
941   This->lpCur = DIBPTR(This->lpbiCur);
942
943   /* allocate memory for last frame if needed */
944   if (This->lKeyFrameEvery != 1 &&
945       (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
946     size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
947     This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
948     if (This->lpbiPrev == NULL)
949       return AVIERR_MEMORY;
950     if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
951       return AVIERR_COMPRESSOR;
952
953     if (This->lpbiPrev->biSizeImage == 0) {
954       This->lpbiPrev->biSizeImage =
955         DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
956     }
957
958     /* get memory for format and picture */
959     size += This->lpbiPrev->biSizeImage;
960     This->lpbiPrev =
961       (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
962     if (This->lpbiPrev == NULL)
963       return AVIERR_MEMORY;
964     This->lpPrev = DIBPTR(This->lpbiPrev);
965
966     /* prepare codec also for decompression */
967     if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
968       return AVIERR_COMPRESSOR;
969   }
970
971   return AVIERR_OK;
972 }