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