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