kernel32: Add some simple tests for GetVersionExA.
[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., 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 decompresion 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;
849           dwMinQual = tmp;
850           continue;
851         }
852       } else
853         break;
854     } else if (dwMaxQual - dwMinQual <= 1) {
855       break;
856     } else {
857       dwMaxQual = dwCurQual;
858
859       if (bDecreasedQual || dwCurQual == This->dwLastQuality)
860         dwCurQual = (dwMinQual + dwMaxQual) / 2;
861       else
862         FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n",
863               dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
864
865       bDecreasedQual = TRUE;
866     }
867   } while (TRUE);
868
869   /* remember some values */
870   This->dwLastQuality = dwCurQual;
871   This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
872   if (icmFlags & ICCOMPRESS_KEYFRAME)
873     This->lLastKey = This->lCurrent;
874
875   /* Does we manage previous frame? */
876   if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
877     ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
878                  This->lpbiPrev, This->lpPrev);
879
880   return AVIERR_OK;
881 }
882
883 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
884 {
885   LPBITMAPINFOHEADER lpbi;
886   DWORD              size;
887
888   /* pre-conditions */
889   assert(This != NULL);
890   assert(This->pStream != NULL);
891   assert(This->pg == NULL);
892
893   This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
894   if (This->pg == NULL)
895     return AVIERR_ERROR;
896
897   /* When we only decompress this is enough */
898   if (This->sInfo.fccHandler == comptypeDIB)
899     return AVIERR_OK;
900
901   assert(This->hic != NULL);
902   assert(This->lpbiOutput == NULL);
903
904   /* get input format */
905   lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
906   if (lpbi == NULL)
907     return AVIERR_MEMORY;
908
909   /* get memory for output format */
910   size = ICCompressGetFormatSize(This->hic, lpbi);
911   if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER))
912     return AVIERR_COMPRESSOR;
913   This->lpbiOutput = HeapAlloc(GetProcessHeap(), 0, size);
914   if (This->lpbiOutput == NULL)
915     return AVIERR_MEMORY;
916   This->cbOutput = size;
917
918   if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
919     return AVIERR_BADFORMAT;
920
921   /* update AVISTREAMINFO structure */
922   This->sInfo.rcFrame.right  =
923     This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
924   This->sInfo.rcFrame.bottom =
925     This->sInfo.rcFrame.top  + This->lpbiOutput->biHeight;
926   This->sInfo.dwSuggestedBufferSize =
927     ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
928
929   /* prepare codec for compression */
930   if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
931     return AVIERR_COMPRESSOR;
932
933   /* allocate memory for current frame */
934   size += This->sInfo.dwSuggestedBufferSize;
935   This->lpbiCur = HeapAlloc(GetProcessHeap(), 0, size);
936   if (This->lpbiCur == NULL)
937     return AVIERR_MEMORY;
938   memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
939   This->lpCur = DIBPTR(This->lpbiCur);
940
941   /* allocate memory for last frame if needed */
942   if (This->lKeyFrameEvery != 1 &&
943       (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
944     size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
945     This->lpbiPrev = HeapAlloc(GetProcessHeap(), 0, size);
946     if (This->lpbiPrev == NULL)
947       return AVIERR_MEMORY;
948     if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
949       return AVIERR_COMPRESSOR;
950
951     if (This->lpbiPrev->biSizeImage == 0) {
952       This->lpbiPrev->biSizeImage =
953         DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
954     }
955
956     /* get memory for format and picture */
957     size += This->lpbiPrev->biSizeImage;
958     This->lpbiPrev = HeapReAlloc(GetProcessHeap(), 0, This->lpbiPrev, size );
959     if (This->lpbiPrev == NULL)
960       return AVIERR_MEMORY;
961     This->lpPrev = DIBPTR(This->lpbiPrev);
962
963     /* prepare codec also for decompression */
964     if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
965       return AVIERR_COMPRESSOR;
966   }
967
968   return AVIERR_OK;
969 }