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