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