2 * Copyright 2002 Michael Günnewig
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.
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.
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
30 #include "avifile_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
36 #define MAX_FRAMESIZE (16 * 1024 * 1024)
37 #define MAX_FRAMESIZE_DIFF 512
39 /***********************************************************************/
41 typedef struct _IAVIStreamImpl
{
43 IAVIStream IAVIStream_iface
;
46 /* IAVIStream stuff */
58 DWORD dwBytesPerFrame
;
61 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
63 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
66 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
68 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
72 /***********************************************************************/
74 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
75 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
76 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
78 static inline IAVIStreamImpl
*impl_from_IAVIStream(IAVIStream
*iface
)
80 return CONTAINING_RECORD(iface
, IAVIStreamImpl
, IAVIStream_iface
);
83 static inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
87 This
->dwLastQuality
= ICQUALITY_HIGH
;
88 This
->dwUnusedBytes
= 0;
91 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
92 REFIID refiid
, LPVOID
*obj
)
94 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
96 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
98 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
99 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
101 IAVIStream_AddRef(iface
);
106 return OLE_E_ENUM_NOMORE
;
109 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
111 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
112 ULONG ref
= InterlockedIncrement(&This
->ref
);
114 TRACE("(%p) -> %d\n", iface
, ref
);
116 /* also add reference to the nested stream */
117 if (This
->pStream
!= NULL
)
118 IAVIStream_AddRef(This
->pStream
);
123 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
125 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
126 ULONG ref
= InterlockedDecrement(&This
->ref
);
128 TRACE("(%p) -> %d\n", iface
, ref
);
132 if (This
->pg
!= NULL
) {
133 AVIStreamGetFrameClose(This
->pg
);
136 if (This
->pStream
!= NULL
) {
137 IAVIStream_Release(This
->pStream
);
138 This
->pStream
= NULL
;
140 if (This
->hic
!= NULL
) {
141 if (This
->lpbiPrev
!= NULL
) {
142 ICDecompressEnd(This
->hic
);
143 HeapFree(GetProcessHeap(), 0, This
->lpbiPrev
);
144 This
->lpbiPrev
= NULL
;
147 ICCompressEnd(This
->hic
);
150 if (This
->lpbiCur
!= NULL
) {
151 HeapFree(GetProcessHeap(), 0, This
->lpbiCur
);
152 This
->lpbiCur
= NULL
;
155 if (This
->lpbiOutput
!= NULL
) {
156 HeapFree(GetProcessHeap(), 0, This
->lpbiOutput
);
157 This
->lpbiOutput
= NULL
;
160 if (This
->lpbiInput
!= NULL
) {
161 HeapFree(GetProcessHeap(), 0, This
->lpbiInput
);
162 This
->lpbiInput
= NULL
;
166 HeapFree(GetProcessHeap(), 0, This
);
171 /* also release reference to the nested stream */
172 if (This
->pStream
!= NULL
)
173 IAVIStream_Release(This
->pStream
);
178 /* lParam1: PAVISTREAM
179 * lParam2: LPAVICOMPRESSOPTIONS
181 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
184 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
187 ICCOMPRESSFRAMES icFrames
;
188 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
190 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
192 /* check parameter */
193 if ((LPVOID
)lParam1
== NULL
)
194 return AVIERR_BADPARAM
;
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 */
201 /* add reference to the stream */
202 This
->pStream
= (PAVISTREAM
)lParam1
;
203 IAVIStream_AddRef(This
->pStream
);
207 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
208 /* we should compress */
209 This
->sInfo
.fccHandler
= pco
->fccHandler
;
211 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
212 if (This
->hic
== NULL
)
213 return AVIERR_NOCOMPRESSOR
;
215 /* restore saved state of codec */
216 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
217 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
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
);
225 /* get capabilities of codec */
226 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
227 This
->dwICMFlags
= icinfo
.dwFlags
;
230 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
231 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
232 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
234 This
->lKeyFrameEvery
= 1;
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
;
242 assert(This
->sInfo
.dwRate
!= 0);
244 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
245 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
247 pco
->dwBytesPerSecond
= 0;
248 This
->dwBytesPerFrame
= 0;
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
));
265 This
->sInfo
.fccHandler
= comptypeDIB
;
270 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
273 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
275 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
278 return AVIERR_BADPARAM
;
280 return AVIERR_BADSIZE
;
282 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
284 if ((DWORD
)size
< sizeof(This
->sInfo
))
285 return AVIERR_BUFFERTOOSMALL
;
289 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
292 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
294 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
296 if (flags
& FIND_FROM_START
) {
297 pos
= This
->sInfo
.dwStart
;
298 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
302 if (flags
& FIND_RET
)
303 WARN(": FIND_RET flags will be ignored!\n");
305 if (flags
& FIND_KEY
) {
306 if (This
->hic
== NULL
)
307 return pos
; /* we decompress so every frame is a keyframe */
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
);
314 return This
->lLastKey
;
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
)
326 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
327 LPVOID format
, LONG
*formatsize
)
329 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
331 LPBITMAPINFOHEADER lpbi
;
334 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
336 if (formatsize
== NULL
)
337 return AVIERR_BADPARAM
;
339 if (This
->pg
== NULL
) {
340 hr
= AVIFILE_OpenGetFrame(This
);
346 lpbi
= AVIStreamGetFrame(This
->pg
, pos
);
348 return AVIERR_MEMORY
;
350 if (This
->hic
== NULL
) {
351 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
354 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
355 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
357 This
->cbOutput
= size
;
358 if (format
!= NULL
) {
359 if (This
->lpbiOutput
!= NULL
)
360 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
362 memcpy(format
, lpbi
, min(*formatsize
, size
));
365 } else if (format
!= NULL
)
366 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
368 if (*formatsize
< This
->cbOutput
)
369 hr
= AVIERR_BUFFERTOOSMALL
;
373 *formatsize
= This
->cbOutput
;
377 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
378 LPVOID format
, LONG formatsize
)
380 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
382 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
384 /* check parameters */
385 if (format
== NULL
|| formatsize
<= 0)
386 return AVIERR_BADPARAM
;
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
;
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
;
400 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
404 /* Does the nested stream support writing? */
405 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
406 return AVIERR_READONLY
;
408 /* check if frame is already written */
409 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
410 return AVIERR_UNSUPPORTED
;
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
;
417 /* only pass through? */
418 if (This
->sInfo
.fccHandler
== comptypeDIB
)
419 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
421 /* initial format setting? */
422 if (This
->lpbiInput
== NULL
) {
425 assert(This
->hic
!= NULL
);
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
);
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
;
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
;
451 /* prepare codec for compression */
452 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
453 return AVIERR_COMPRESSOR
;
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
);
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
;
473 if (This
->lpbiPrev
->biSizeImage
== 0) {
474 This
->lpbiPrev
->biSizeImage
=
475 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
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
);
485 /* prepare codec also for decompression */
486 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
487 return AVIERR_COMPRESSOR
;
490 /* format change -- check that's only the palette */
491 LPBITMAPINFOHEADER lpbi
= format
;
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
;
502 /* get new output format */
503 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
504 return AVIERR_BADFORMAT
;
506 /* restart compression */
507 ICCompressEnd(This
->hic
);
508 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
509 return AVIERR_COMPRESSOR
;
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
;
522 /* tell nested stream the new format */
523 return IAVIStream_SetFormat(This
->pStream
, pos
,
524 This
->lpbiOutput
, This
->cbOutput
);
527 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
528 LONG samples
, LPVOID buffer
,
529 LONG buffersize
, LPLONG bytesread
,
532 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
534 LPBITMAPINFOHEADER lpbi
;
536 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
537 buffersize
, bytesread
, samplesread
);
539 /* clear return parameters if given */
540 if (bytesread
!= NULL
)
542 if (samplesread
!= NULL
)
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 */
554 if (This
->pg
== NULL
) {
555 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
561 /* compress or decompress? */
562 if (This
->hic
== NULL
) {
564 lpbi
= AVIStreamGetFrame(This
->pg
, start
);
566 return AVIERR_MEMORY
;
568 if (buffer
!= NULL
&& buffersize
> 0) {
569 /* check buffersize */
570 if (buffersize
< lpbi
->biSizeImage
)
571 return AVIERR_BUFFERTOOSMALL
;
573 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
576 /* fill out return parameters if given */
577 if (bytesread
!= NULL
)
578 *bytesread
= lpbi
->biSizeImage
;
581 if (This
->lCurrent
> start
)
584 while (start
> This
->lCurrent
) {
587 lpbi
= AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
590 return AVIERR_MEMORY
;
593 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
600 if (buffer
!= NULL
&& buffersize
> 0) {
601 /* check buffersize */
602 if (This
->lpbiCur
->biSizeImage
> buffersize
)
603 return AVIERR_BUFFERTOOSMALL
;
605 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
608 /* fill out return parameters if given */
609 if (bytesread
!= NULL
)
610 *bytesread
= This
->lpbiCur
->biSizeImage
;
613 /* fill out return parameters if given */
614 if (samplesread
!= NULL
)
620 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
621 LONG samples
, LPVOID buffer
,
622 LONG buffersize
, DWORD flags
,
626 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
630 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
631 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
633 /* clear return parameters if given */
634 if (sampwritten
!= NULL
)
636 if (byteswritten
!= NULL
)
639 /* check parameters */
640 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
641 return AVIERR_BADPARAM
;
643 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
644 /* only pass through */
645 flags
|= AVIIF_KEYFRAME
;
647 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
648 flags
, sampwritten
, byteswritten
);
650 /* compress data before writing to pStream */
651 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
652 return AVIERR_UNSUPPORTED
;
654 This
->lCurrent
= start
;
655 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
659 if (This
->lLastKey
== start
)
660 flags
|= AVIIF_KEYFRAME
;
662 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
663 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
668 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
671 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
673 TRACE("(%p,%d,%d)\n", iface
, start
, samples
);
675 return IAVIStream_Delete(This
->pStream
, start
, samples
);
678 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
679 LPVOID lp
, LPLONG lpread
)
681 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
683 TRACE("(%p,0x%08X,%p,%p)\n", iface
, fcc
, lp
, lpread
);
685 assert(This
->pStream
!= NULL
);
687 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
690 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
691 LPVOID lp
, LONG size
)
693 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
695 TRACE("(%p,0x%08x,%p,%d)\n", iface
, fcc
, lp
, size
);
697 assert(This
->pStream
!= NULL
);
699 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
702 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
703 LPAVISTREAMINFOW info
, LONG infolen
)
705 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
710 static const struct IAVIStreamVtbl iicmst
= {
711 ICMStream_fnQueryInterface
,
716 ICMStream_fnFindSample
,
717 ICMStream_fnReadFormat
,
718 ICMStream_fnSetFormat
,
722 ICMStream_fnReadData
,
723 ICMStream_fnWriteData
,
727 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
729 IAVIStreamImpl
*pstream
;
732 assert(riid
!= NULL
&& ppv
!= NULL
);
736 pstream
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIStreamImpl
));
738 return AVIERR_MEMORY
;
740 pstream
->IAVIStream_iface
.lpVtbl
= &iicmst
;
741 AVIFILE_Reset(pstream
);
743 hr
= IAVIStream_QueryInterface(&pstream
->IAVIStream_iface
, riid
, ppv
);
745 HeapFree(GetProcessHeap(), 0, pstream
);
750 /***********************************************************************/
752 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
753 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
755 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
759 BOOL bDecreasedQual
= FALSE
;
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
;
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;
778 /* for non-keyframes only allow something of the unused bytes to be consumed */
782 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
783 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
784 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
786 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
787 This
->dwUnusedBytes
-= tmp2
;
790 dwRequest
= MAX_FRAMESIZE
;
792 /* only one keyframe at start desired */
793 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
794 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
795 This
->dwUnusedBytes
= 0;
797 dwRequest
= MAX_FRAMESIZE
;
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));
804 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
805 dwMinQual
= ICQUALITY_LOW
;
808 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
809 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
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
;
824 /* need to check for framesize */
828 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
829 /* frame is smaller -- try to maximize quality */
830 if (dwMaxQual
- dwCurQual
> 10) {
831 DWORD tmp
= dwRequest
/ 8;
833 if (tmp
< MAX_FRAMESIZE_DIFF
)
834 tmp
= MAX_FRAMESIZE_DIFF
;
836 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
838 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
844 } else if (dwMaxQual
- dwMinQual
<= 1) {
847 dwMaxQual
= dwCurQual
;
849 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
850 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
852 FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n",
853 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
855 bDecreasedQual
= TRUE
;
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
;
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
);
873 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
875 LPBITMAPINFOHEADER lpbi
;
879 assert(This
!= NULL
);
880 assert(This
->pStream
!= NULL
);
881 assert(This
->pg
== NULL
);
883 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
884 if (This
->pg
== NULL
)
887 /* When we only decompress this is enough */
888 if (This
->sInfo
.fccHandler
== comptypeDIB
)
891 assert(This
->hic
!= NULL
);
892 assert(This
->lpbiOutput
== NULL
);
894 /* get input format */
895 lpbi
= AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
897 return AVIERR_MEMORY
;
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
;
908 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
909 return AVIERR_BADFORMAT
;
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
);
919 /* prepare codec for compression */
920 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
921 return AVIERR_COMPRESSOR
;
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
);
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
;
941 if (This
->lpbiPrev
->biSizeImage
== 0) {
942 This
->lpbiPrev
->biSizeImage
=
943 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
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
);
953 /* prepare codec also for decompression */
954 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
955 return AVIERR_COMPRESSOR
;