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