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
19 #include "avifile_private.h"
21 #define MAX_FRAMESIZE (16 * 1024 * 1024)
22 #define MAX_FRAMESIZE_DIFF 512
24 /***********************************************************************/
26 typedef struct _IAVIStreamImpl
{
28 IAVIStream IAVIStream_iface
;
31 /* IAVIStream stuff */
43 DWORD dwBytesPerFrame
;
46 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
48 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
51 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
53 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
57 /***********************************************************************/
59 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
60 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
61 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
63 static inline IAVIStreamImpl
*impl_from_IAVIStream(IAVIStream
*iface
)
65 return CONTAINING_RECORD(iface
, IAVIStreamImpl
, IAVIStream_iface
);
68 static inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
72 This
->dwLastQuality
= ICQUALITY_HIGH
;
73 This
->dwUnusedBytes
= 0;
76 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
77 REFIID refiid
, LPVOID
*obj
)
79 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
81 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
83 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
84 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
85 *obj
= &This
->IAVIStream_iface
;
86 IAVIStream_AddRef(iface
);
91 return OLE_E_ENUM_NOMORE
;
94 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
96 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
97 ULONG ref
= InterlockedIncrement(&This
->ref
);
99 TRACE("(%p) -> %d\n", iface
, ref
);
101 /* also add reference to the nested stream */
102 if (This
->pStream
!= NULL
)
103 IAVIStream_AddRef(This
->pStream
);
108 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
110 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
111 ULONG ref
= InterlockedDecrement(&This
->ref
);
113 TRACE("(%p) -> %d\n", iface
, ref
);
117 if (This
->pg
!= NULL
) {
118 AVIStreamGetFrameClose(This
->pg
);
121 if (This
->pStream
!= NULL
) {
122 IAVIStream_Release(This
->pStream
);
123 This
->pStream
= NULL
;
125 if (This
->hic
!= NULL
) {
126 if (This
->lpbiPrev
!= NULL
) {
127 ICDecompressEnd(This
->hic
);
128 HeapFree(GetProcessHeap(), 0, This
->lpbiPrev
);
129 This
->lpbiPrev
= NULL
;
132 ICCompressEnd(This
->hic
);
135 if (This
->lpbiCur
!= NULL
) {
136 HeapFree(GetProcessHeap(), 0, This
->lpbiCur
);
137 This
->lpbiCur
= NULL
;
140 if (This
->lpbiOutput
!= NULL
) {
141 HeapFree(GetProcessHeap(), 0, This
->lpbiOutput
);
142 This
->lpbiOutput
= NULL
;
145 if (This
->lpbiInput
!= NULL
) {
146 HeapFree(GetProcessHeap(), 0, This
->lpbiInput
);
147 This
->lpbiInput
= NULL
;
151 HeapFree(GetProcessHeap(), 0, This
);
156 /* also release reference to the nested stream */
157 if (This
->pStream
!= NULL
)
158 IAVIStream_Release(This
->pStream
);
163 /* lParam1: PAVISTREAM
164 * lParam2: LPAVICOMPRESSOPTIONS
166 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
169 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
172 ICCOMPRESSFRAMES icFrames
;
173 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
175 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
177 /* check parameter */
178 if ((LPVOID
)lParam1
== NULL
)
179 return AVIERR_BADPARAM
;
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 */
186 /* add reference to the stream */
187 This
->pStream
= (PAVISTREAM
)lParam1
;
188 IAVIStream_AddRef(This
->pStream
);
192 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
193 /* we should compress */
194 This
->sInfo
.fccHandler
= pco
->fccHandler
;
196 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
197 if (This
->hic
== NULL
)
198 return AVIERR_NOCOMPRESSOR
;
200 /* restore saved state of codec */
201 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
202 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
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
);
210 /* get capabilities of codec */
211 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
212 This
->dwICMFlags
= icinfo
.dwFlags
;
215 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
216 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
217 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
219 This
->lKeyFrameEvery
= 1;
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
;
227 assert(This
->sInfo
.dwRate
!= 0);
229 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
230 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
232 pco
->dwBytesPerSecond
= 0;
233 This
->dwBytesPerFrame
= 0;
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
));
250 This
->sInfo
.fccHandler
= comptypeDIB
;
255 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
258 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
260 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
263 return AVIERR_BADPARAM
;
265 return AVIERR_BADSIZE
;
267 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
269 if ((DWORD
)size
< sizeof(This
->sInfo
))
270 return AVIERR_BUFFERTOOSMALL
;
274 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
277 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
279 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
281 if (flags
& FIND_FROM_START
) {
282 pos
= This
->sInfo
.dwStart
;
283 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
287 if (flags
& FIND_RET
)
288 WARN(": FIND_RET flags will be ignored!\n");
290 if (flags
& FIND_KEY
) {
291 if (This
->hic
== NULL
)
292 return pos
; /* we decompress so every frame is a keyframe */
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
);
299 return This
->lLastKey
;
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
)
311 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
312 LPVOID format
, LONG
*formatsize
)
314 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
316 LPBITMAPINFOHEADER lpbi
;
319 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
321 if (formatsize
== NULL
)
322 return AVIERR_BADPARAM
;
324 if (This
->pg
== NULL
) {
325 hr
= AVIFILE_OpenGetFrame(This
);
331 lpbi
= AVIStreamGetFrame(This
->pg
, pos
);
333 return AVIERR_MEMORY
;
335 if (This
->hic
== NULL
) {
336 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
339 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
340 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
342 This
->cbOutput
= size
;
343 if (format
!= NULL
) {
344 if (This
->lpbiOutput
!= NULL
)
345 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
347 memcpy(format
, lpbi
, min(*formatsize
, size
));
350 } else if (format
!= NULL
)
351 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
353 if (*formatsize
< This
->cbOutput
)
354 hr
= AVIERR_BUFFERTOOSMALL
;
358 *formatsize
= This
->cbOutput
;
362 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
363 LPVOID format
, LONG formatsize
)
365 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
367 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
369 /* check parameters */
370 if (format
== NULL
|| formatsize
<= 0)
371 return AVIERR_BADPARAM
;
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
;
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
;
385 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
389 /* Does the nested stream support writing? */
390 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
391 return AVIERR_READONLY
;
393 /* check if frame is already written */
394 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
395 return AVIERR_UNSUPPORTED
;
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
;
402 /* only pass through? */
403 if (This
->sInfo
.fccHandler
== comptypeDIB
)
404 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
406 /* initial format setting? */
407 if (This
->lpbiInput
== NULL
) {
410 assert(This
->hic
!= NULL
);
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
);
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
;
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
;
436 /* prepare codec for compression */
437 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
438 return AVIERR_COMPRESSOR
;
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
);
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
;
458 if (This
->lpbiPrev
->biSizeImage
== 0) {
459 This
->lpbiPrev
->biSizeImage
=
460 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
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
);
470 /* prepare codec also for decompression */
471 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
472 return AVIERR_COMPRESSOR
;
475 /* format change -- check that's only the palette */
476 LPBITMAPINFOHEADER lpbi
= format
;
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
;
487 /* get new output format */
488 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
489 return AVIERR_BADFORMAT
;
491 /* restart compression */
492 ICCompressEnd(This
->hic
);
493 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
494 return AVIERR_COMPRESSOR
;
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
;
507 /* tell nested stream the new format */
508 return IAVIStream_SetFormat(This
->pStream
, pos
,
509 This
->lpbiOutput
, This
->cbOutput
);
512 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
513 LONG samples
, LPVOID buffer
,
514 LONG buffersize
, LPLONG bytesread
,
517 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
519 LPBITMAPINFOHEADER lpbi
;
521 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
522 buffersize
, bytesread
, samplesread
);
524 /* clear return parameters if given */
525 if (bytesread
!= NULL
)
527 if (samplesread
!= NULL
)
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 */
539 if (This
->pg
== NULL
) {
540 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
546 /* compress or decompress? */
547 if (This
->hic
== NULL
) {
549 lpbi
= AVIStreamGetFrame(This
->pg
, start
);
551 return AVIERR_MEMORY
;
553 if (buffer
!= NULL
&& buffersize
> 0) {
554 /* check buffersize */
555 if (buffersize
< lpbi
->biSizeImage
)
556 return AVIERR_BUFFERTOOSMALL
;
558 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
561 /* fill out return parameters if given */
562 if (bytesread
!= NULL
)
563 *bytesread
= lpbi
->biSizeImage
;
566 if (This
->lCurrent
> start
)
569 while (start
> This
->lCurrent
) {
572 lpbi
= AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
575 return AVIERR_MEMORY
;
578 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
585 if (buffer
!= NULL
&& buffersize
> 0) {
586 /* check buffersize */
587 if (This
->lpbiCur
->biSizeImage
> buffersize
)
588 return AVIERR_BUFFERTOOSMALL
;
590 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
593 /* fill out return parameters if given */
594 if (bytesread
!= NULL
)
595 *bytesread
= This
->lpbiCur
->biSizeImage
;
598 /* fill out return parameters if given */
599 if (samplesread
!= NULL
)
605 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
606 LONG samples
, LPVOID buffer
,
607 LONG buffersize
, DWORD flags
,
611 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
615 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
616 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
618 /* clear return parameters if given */
619 if (sampwritten
!= NULL
)
621 if (byteswritten
!= NULL
)
624 /* check parameters */
625 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
626 return AVIERR_BADPARAM
;
628 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
629 /* only pass through */
630 flags
|= AVIIF_KEYFRAME
;
632 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
633 flags
, sampwritten
, byteswritten
);
635 /* compress data before writing to pStream */
636 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
637 return AVIERR_UNSUPPORTED
;
639 This
->lCurrent
= start
;
640 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
644 if (This
->lLastKey
== start
)
645 flags
|= AVIIF_KEYFRAME
;
647 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
648 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
653 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
656 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
658 TRACE("(%p,%d,%d)\n", iface
, start
, samples
);
660 return IAVIStream_Delete(This
->pStream
, start
, samples
);
663 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
664 LPVOID lp
, LPLONG lpread
)
666 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
668 TRACE("(%p,0x%08X,%p,%p)\n", iface
, fcc
, lp
, lpread
);
670 assert(This
->pStream
!= NULL
);
672 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
675 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
676 LPVOID lp
, LONG size
)
678 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
680 TRACE("(%p,0x%08x,%p,%d)\n", iface
, fcc
, lp
, size
);
682 assert(This
->pStream
!= NULL
);
684 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
687 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
688 LPAVISTREAMINFOW info
, LONG infolen
)
690 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
695 static const struct IAVIStreamVtbl iicmst
= {
696 ICMStream_fnQueryInterface
,
701 ICMStream_fnFindSample
,
702 ICMStream_fnReadFormat
,
703 ICMStream_fnSetFormat
,
707 ICMStream_fnReadData
,
708 ICMStream_fnWriteData
,
712 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
714 IAVIStreamImpl
*pstream
;
717 assert(riid
!= NULL
&& ppv
!= NULL
);
721 pstream
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIStreamImpl
));
723 return AVIERR_MEMORY
;
725 pstream
->IAVIStream_iface
.lpVtbl
= &iicmst
;
726 AVIFILE_Reset(pstream
);
728 hr
= IAVIStream_QueryInterface(&pstream
->IAVIStream_iface
, riid
, ppv
);
730 HeapFree(GetProcessHeap(), 0, pstream
);
735 /***********************************************************************/
737 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
738 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
740 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
744 BOOL bDecreasedQual
= FALSE
;
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
;
756 if (This
->lKeyFrameEvery
!= 0) {
757 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
758 if (idxFlags
& AVIIF_KEYFRAME
) {
759 /* allow keyframes to consume all unused bytes */
760 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
761 This
->dwUnusedBytes
= 0;
763 /* for non-keyframes only allow some of the unused bytes to be consumed */
767 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
768 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
769 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
771 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
772 This
->dwUnusedBytes
-= tmp2
;
775 dwRequest
= MAX_FRAMESIZE
;
777 /* only one keyframe at start desired */
778 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
779 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
780 This
->dwUnusedBytes
= 0;
782 dwRequest
= MAX_FRAMESIZE
;
785 /* must we check for frame size to gain the requested
786 * data rate or can we trust the codec? */
787 doSizeCheck
= (dwRequest
!= 0 && ((This
->dwICMFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0));
789 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
790 dwMinQual
= ICQUALITY_LOW
;
793 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
794 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
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
;
809 /* need to check for framesize */
813 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
814 /* frame is smaller -- try to maximize quality */
815 if (dwMaxQual
- dwCurQual
> 10) {
816 DWORD tmp
= dwRequest
/ 8;
818 if (tmp
< MAX_FRAMESIZE_DIFF
)
819 tmp
= MAX_FRAMESIZE_DIFF
;
821 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
823 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
829 } else if (dwMaxQual
- dwMinQual
<= 1) {
832 dwMaxQual
= dwCurQual
;
834 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
835 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
837 FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n",
838 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
840 bDecreasedQual
= TRUE
;
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
;
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
);
858 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
860 LPBITMAPINFOHEADER lpbi
;
864 assert(This
!= NULL
);
865 assert(This
->pStream
!= NULL
);
866 assert(This
->pg
== NULL
);
868 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
869 if (This
->pg
== NULL
)
872 /* When we only decompress this is enough */
873 if (This
->sInfo
.fccHandler
== comptypeDIB
)
876 assert(This
->hic
!= NULL
);
877 assert(This
->lpbiOutput
== NULL
);
879 /* get input format */
880 lpbi
= AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
882 return AVIERR_MEMORY
;
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
;
893 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
894 return AVIERR_BADFORMAT
;
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
);
904 /* prepare codec for compression */
905 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
906 return AVIERR_COMPRESSOR
;
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
);
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
;
926 if (This
->lpbiPrev
->biSizeImage
== 0) {
927 This
->lpbiPrev
->biSizeImage
=
928 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
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
);
938 /* prepare codec also for decompression */
939 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
940 return AVIERR_COMPRESSOR
;