xref: /reactos/dll/win32/avifil32/icmstream.c (revision 44836a6e)
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 
impl_from_IAVIStream(IAVIStream * iface)78 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
79 {
80   return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
81 }
82 
AVIFILE_Reset(IAVIStreamImpl * This)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 
ICMStream_fnQueryInterface(IAVIStream * iface,REFIID refiid,LPVOID * obj)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->IAVIStream_iface;
101     IAVIStream_AddRef(iface);
102 
103     return S_OK;
104   }
105 
106   return OLE_E_ENUM_NOMORE;
107 }
108 
ICMStream_fnAddRef(IAVIStream * iface)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 
ICMStream_fnRelease(IAVIStream * iface)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  */
ICMStream_fnCreate(IAVIStream * iface,LPARAM lParam1,LPARAM lParam2)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 
ICMStream_fnInfo(IAVIStream * iface,LPAVISTREAMINFOW psi,LONG size)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 
ICMStream_fnFindSample(IAVIStream * iface,LONG pos,LONG flags)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 too expensive, so guess. */
318   } else if (flags & FIND_FORMAT) {
319     if (flags & FIND_PREV)
320       return 0;
321   }
322 
323   return -1;
324 }
325 
ICMStream_fnReadFormat(IAVIStream * iface,LONG pos,LPVOID format,LONG * formatsize)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 
ICMStream_fnSetFormat(IAVIStream * iface,LONG pos,LPVOID format,LONG formatsize)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 
ICMStream_fnRead(IAVIStream * iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LPLONG bytesread,LPLONG samplesread)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 
ICMStream_fnWrite(IAVIStream * iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LPLONG sampwritten,LPLONG byteswritten)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 
ICMStream_fnDelete(IAVIStream * iface,LONG start,LONG samples)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 
ICMStream_fnReadData(IAVIStream * iface,DWORD fcc,LPVOID lp,LPLONG lpread)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 
ICMStream_fnWriteData(IAVIStream * iface,DWORD fcc,LPVOID lp,LONG size)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 
ICMStream_fnSetInfo(IAVIStream * iface,LPAVISTREAMINFOW info,LONG infolen)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 
AVIFILE_CreateICMStream(REFIID riid,LPVOID * ppv)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 
AVIFILE_EncodeFrame(IAVIStreamImpl * This,LPBITMAPINFOHEADER lpbi,LPVOID lpBits)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 	/* allow keyframes to consume all unused bytes */
775 	dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
776 	This->dwUnusedBytes = 0;
777       } else {
778 	/* for non-keyframes only allow some 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 frame size to gain the requested
801    * data rate or can we trust the 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 
AVIFILE_OpenGetFrame(IAVIStreamImpl * This)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 }
960