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
32 #include "avifile_private.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
38 #define MAX_FRAMESIZE (16 * 1024 * 1024)
39 #define MAX_FRAMESIZE_DIFF 512
41 /***********************************************************************/
43 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,REFIID refiid
,LPVOID
*obj
);
44 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
);
45 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
);
46 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
,LPARAM lParam1
,LPARAM lParam2
);
47 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,AVISTREAMINFOW
*psi
,LONG size
);
48 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
,LONG pos
,LONG flags
);
49 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG
*formatsize
);
50 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG formatsize
);
51 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,LONG
*bytesread
,LONG
*samplesread
);
52 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,DWORD flags
,LONG
*sampwritten
,LONG
*byteswritten
);
53 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
,LONG start
,LONG samples
);
54 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG
*lpread
);
55 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG size
);
56 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,AVISTREAMINFOW
*info
,LONG infolen
);
58 static const struct IAVIStreamVtbl iicmst
= {
59 ICMStream_fnQueryInterface
,
64 ICMStream_fnFindSample
,
65 ICMStream_fnReadFormat
,
66 ICMStream_fnSetFormat
,
71 ICMStream_fnWriteData
,
75 typedef struct _IAVIStreamImpl
{
77 const IAVIStreamVtbl
*lpVtbl
;
80 /* IAVIStream stuff */
92 DWORD dwBytesPerFrame
;
95 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
97 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
100 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
102 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
106 /***********************************************************************/
108 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
109 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
110 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
112 static inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
116 This
->dwLastQuality
= ICQUALITY_HIGH
;
117 This
->dwUnusedBytes
= 0;
120 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
122 IAVIStreamImpl
*pstream
;
125 assert(riid
!= NULL
&& ppv
!= NULL
);
129 pstream
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIStreamImpl
));
131 return AVIERR_MEMORY
;
133 pstream
->lpVtbl
= &iicmst
;
134 AVIFILE_Reset(pstream
);
136 hr
= IAVIStream_QueryInterface((IAVIStream
*)pstream
, riid
, ppv
);
138 HeapFree(GetProcessHeap(), 0, pstream
);
143 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
144 REFIID refiid
, LPVOID
*obj
)
146 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
148 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
150 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
151 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
153 IAVIStream_AddRef(iface
);
158 return OLE_E_ENUM_NOMORE
;
161 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
163 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
164 ULONG ref
= InterlockedIncrement(&This
->ref
);
166 TRACE("(%p) -> %d\n", iface
, ref
);
168 /* also add reference to the nested stream */
169 if (This
->pStream
!= NULL
)
170 IAVIStream_AddRef(This
->pStream
);
175 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
177 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
178 ULONG ref
= InterlockedDecrement(&This
->ref
);
180 TRACE("(%p) -> %d\n", iface
, ref
);
184 if (This
->pg
!= NULL
) {
185 AVIStreamGetFrameClose(This
->pg
);
188 if (This
->pStream
!= NULL
) {
189 IAVIStream_Release(This
->pStream
);
190 This
->pStream
= NULL
;
192 if (This
->hic
!= NULL
) {
193 if (This
->lpbiPrev
!= NULL
) {
194 ICDecompressEnd(This
->hic
);
195 HeapFree(GetProcessHeap(), 0, This
->lpbiPrev
);
196 This
->lpbiPrev
= NULL
;
199 ICCompressEnd(This
->hic
);
202 if (This
->lpbiCur
!= NULL
) {
203 HeapFree(GetProcessHeap(), 0, This
->lpbiCur
);
204 This
->lpbiCur
= NULL
;
207 if (This
->lpbiOutput
!= NULL
) {
208 HeapFree(GetProcessHeap(), 0, This
->lpbiOutput
);
209 This
->lpbiOutput
= NULL
;
212 if (This
->lpbiInput
!= NULL
) {
213 HeapFree(GetProcessHeap(), 0, This
->lpbiInput
);
214 This
->lpbiInput
= NULL
;
218 HeapFree(GetProcessHeap(), 0, This
);
223 /* also release reference to the nested stream */
224 if (This
->pStream
!= NULL
)
225 IAVIStream_Release(This
->pStream
);
230 /* lParam1: PAVISTREAM
231 * lParam2: LPAVICOMPRESSOPTIONS
233 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
236 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
239 ICCOMPRESSFRAMES icFrames
;
240 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
242 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
244 /* check parameter */
245 if ((LPVOID
)lParam1
== NULL
)
246 return AVIERR_BADPARAM
;
248 /* get infos from stream */
249 IAVIStream_Info((PAVISTREAM
)lParam1
, &This
->sInfo
, sizeof(This
->sInfo
));
250 if (This
->sInfo
.fccType
!= streamtypeVIDEO
)
251 return AVIERR_ERROR
; /* error in registry or AVIMakeCompressedStream */
253 /* add reference to the stream */
254 This
->pStream
= (PAVISTREAM
)lParam1
;
255 IAVIStream_AddRef(This
->pStream
);
259 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
260 /* we should compress */
261 This
->sInfo
.fccHandler
= pco
->fccHandler
;
263 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
264 if (This
->hic
== NULL
)
265 return AVIERR_NOCOMPRESSOR
;
267 /* restore saved state of codec */
268 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
269 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
272 /* set quality -- resolve default quality */
273 This
->sInfo
.dwQuality
= pco
->dwQuality
;
274 if (pco
->dwQuality
== ICQUALITY_DEFAULT
)
275 This
->sInfo
.dwQuality
= ICGetDefaultQuality(This
->hic
);
277 /* get capabilities of codec */
278 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
279 This
->dwICMFlags
= icinfo
.dwFlags
;
282 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
283 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
284 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
286 This
->lKeyFrameEvery
= 1;
289 if ((pco
->dwFlags
& AVICOMPRESSF_DATARATE
)) {
290 /* Do we have a chance to reduce size to desired one? */
291 if ((icinfo
.dwFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0)
292 return AVIERR_NOCOMPRESSOR
;
294 assert(This
->sInfo
.dwRate
!= 0);
296 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
297 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
299 pco
->dwBytesPerSecond
= 0;
300 This
->dwBytesPerFrame
= 0;
303 if (icinfo
.dwFlags
& VIDCF_COMPRESSFRAMES
) {
304 memset(&icFrames
, 0, sizeof(icFrames
));
305 icFrames
.lpbiOutput
= This
->lpbiOutput
;
306 icFrames
.lpbiInput
= This
->lpbiInput
;
307 icFrames
.lFrameCount
= This
->sInfo
.dwLength
;
308 icFrames
.lQuality
= This
->sInfo
.dwQuality
;
309 icFrames
.lDataRate
= pco
->dwBytesPerSecond
;
310 icFrames
.lKeyRate
= This
->lKeyFrameEvery
;
311 icFrames
.dwRate
= This
->sInfo
.dwRate
;
312 icFrames
.dwScale
= This
->sInfo
.dwScale
;
313 ICSendMessage(This
->hic
, ICM_COMPRESS_FRAMES_INFO
,
314 (LPARAM
)&icFrames
, (LPARAM
)sizeof(icFrames
));
317 This
->sInfo
.fccHandler
= comptypeDIB
;
322 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
325 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
327 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
330 return AVIERR_BADPARAM
;
332 return AVIERR_BADSIZE
;
334 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
336 if ((DWORD
)size
< sizeof(This
->sInfo
))
337 return AVIERR_BUFFERTOOSMALL
;
341 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
344 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
346 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
348 if (flags
& FIND_FROM_START
) {
349 pos
= This
->sInfo
.dwStart
;
350 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
354 if (flags
& FIND_RET
)
355 WARN(": FIND_RET flags will be ignored!\n");
357 if (flags
& FIND_KEY
) {
358 if (This
->hic
== NULL
)
359 return pos
; /* we decompress so every frame is a keyframe */
361 if (flags
& FIND_PREV
) {
362 /* need to read old or new frames? */
363 if (This
->lLastKey
<= pos
|| pos
< This
->lCurrent
)
364 IAVIStream_Read(iface
, pos
, 1, NULL
, 0, NULL
, NULL
);
366 return This
->lLastKey
;
368 } else if (flags
& FIND_ANY
) {
369 return pos
; /* We really don't know, reread is to expensive, so guess. */
370 } else if (flags
& FIND_FORMAT
) {
371 if (flags
& FIND_PREV
)
378 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
379 LPVOID format
, LONG
*formatsize
)
381 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
383 LPBITMAPINFOHEADER lpbi
;
386 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
388 if (formatsize
== NULL
)
389 return AVIERR_BADPARAM
;
391 if (This
->pg
== NULL
) {
392 hr
= AVIFILE_OpenGetFrame(This
);
398 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, pos
);
400 return AVIERR_MEMORY
;
402 if (This
->hic
== NULL
) {
403 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
406 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
407 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
409 This
->cbOutput
= size
;
410 if (format
!= NULL
) {
411 if (This
->lpbiOutput
!= NULL
)
412 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
414 memcpy(format
, lpbi
, min(*formatsize
, size
));
417 } else if (format
!= NULL
)
418 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
420 if (*formatsize
< This
->cbOutput
)
421 hr
= AVIERR_BUFFERTOOSMALL
;
425 *formatsize
= This
->cbOutput
;
429 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
430 LPVOID format
, LONG formatsize
)
432 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
434 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
436 /* check parameters */
437 if (format
== NULL
|| formatsize
<= 0)
438 return AVIERR_BADPARAM
;
440 /* We can only accept RGB data for writing */
441 if (((LPBITMAPINFOHEADER
)format
)->biCompression
!= BI_RGB
) {
442 WARN(": need RGB data as input\n");
443 return AVIERR_UNSUPPORTED
;
446 /* Input format already known?
447 * Changing of palette is supported, but be quiet if it's the same */
448 if (This
->lpbiInput
!= NULL
) {
449 if (This
->cbInput
!= formatsize
)
450 return AVIERR_UNSUPPORTED
;
452 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
456 /* Does the nested stream support writing? */
457 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
458 return AVIERR_READONLY
;
460 /* check if frame is already written */
461 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
462 return AVIERR_UNSUPPORTED
;
464 /* check if we should compress */
465 if (This
->sInfo
.fccHandler
== 0 ||
466 This
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E'))
467 This
->sInfo
.fccHandler
= comptypeDIB
;
469 /* only pass through? */
470 if (This
->sInfo
.fccHandler
== comptypeDIB
)
471 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
473 /* initial format setting? */
474 if (This
->lpbiInput
== NULL
) {
477 assert(This
->hic
!= NULL
);
479 /* get memory for input format */
480 This
->lpbiInput
= HeapAlloc(GetProcessHeap(), 0, formatsize
);
481 if (This
->lpbiInput
== NULL
)
482 return AVIERR_MEMORY
;
483 This
->cbInput
= formatsize
;
484 memcpy(This
->lpbiInput
, format
, formatsize
);
486 /* get output format */
487 size
= ICCompressGetFormatSize(This
->hic
, This
->lpbiInput
);
488 if (size
< sizeof(BITMAPINFOHEADER
))
489 return AVIERR_COMPRESSOR
;
490 This
->lpbiOutput
= HeapAlloc(GetProcessHeap(), 0, size
);
491 if (This
->lpbiOutput
== NULL
)
492 return AVIERR_MEMORY
;
493 This
->cbOutput
= size
;
494 if (ICCompressGetFormat(This
->hic
,This
->lpbiInput
,This
->lpbiOutput
) < S_OK
)
495 return AVIERR_COMPRESSOR
;
497 /* update AVISTREAMINFO structure */
498 This
->sInfo
.rcFrame
.right
=
499 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
500 This
->sInfo
.rcFrame
.bottom
=
501 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
503 /* prepare codec for compression */
504 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
505 return AVIERR_COMPRESSOR
;
507 /* allocate memory for compressed frame */
508 size
= ICCompressGetSize(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
);
509 This
->lpbiCur
= HeapAlloc(GetProcessHeap(), 0, This
->cbOutput
+ size
);
510 if (This
->lpbiCur
== NULL
)
511 return AVIERR_MEMORY
;
512 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
513 This
->lpCur
= DIBPTR(This
->lpbiCur
);
515 /* allocate memory for last frame if needed */
516 if (This
->lKeyFrameEvery
!= 1 &&
517 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
518 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
519 This
->lpbiPrev
= HeapAlloc(GetProcessHeap(), 0, size
);
520 if (This
->lpbiPrev
== NULL
)
521 return AVIERR_MEMORY
;
522 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
523 return AVIERR_COMPRESSOR
;
525 if (This
->lpbiPrev
->biSizeImage
== 0) {
526 This
->lpbiPrev
->biSizeImage
=
527 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
530 /* get memory for format and picture */
531 size
+= This
->lpbiPrev
->biSizeImage
;
532 This
->lpbiPrev
= HeapReAlloc(GetProcessHeap(), 0, This
->lpbiPrev
, size
);
533 if (This
->lpbiPrev
== NULL
)
534 return AVIERR_MEMORY
;
535 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
537 /* prepare codec also for decompression */
538 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
539 return AVIERR_COMPRESSOR
;
542 /* format change -- check that's only the palette */
543 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)format
;
545 if (lpbi
->biSize
!= This
->lpbiInput
->biSize
||
546 lpbi
->biWidth
!= This
->lpbiInput
->biWidth
||
547 lpbi
->biHeight
!= This
->lpbiInput
->biHeight
||
548 lpbi
->biBitCount
!= This
->lpbiInput
->biBitCount
||
549 lpbi
->biPlanes
!= This
->lpbiInput
->biPlanes
||
550 lpbi
->biCompression
!= This
->lpbiInput
->biCompression
||
551 lpbi
->biClrUsed
!= This
->lpbiInput
->biClrUsed
)
552 return AVIERR_UNSUPPORTED
;
554 /* get new output format */
555 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
556 return AVIERR_BADFORMAT
;
558 /* restart compression */
559 ICCompressEnd(This
->hic
);
560 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
561 return AVIERR_COMPRESSOR
;
563 /* check if we need to restart decompresion also */
564 if (This
->lKeyFrameEvery
!= 1 &&
565 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
566 ICDecompressEnd(This
->hic
);
567 if (ICDecompressGetFormat(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) < S_OK
)
568 return AVIERR_COMPRESSOR
;
569 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
570 return AVIERR_COMPRESSOR
;
574 /* tell nested stream the new format */
575 return IAVIStream_SetFormat(This
->pStream
, pos
,
576 This
->lpbiOutput
, This
->cbOutput
);
579 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
580 LONG samples
, LPVOID buffer
,
581 LONG buffersize
, LPLONG bytesread
,
584 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
586 LPBITMAPINFOHEADER lpbi
;
588 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
589 buffersize
, bytesread
, samplesread
);
591 /* clear return parameters if given */
592 if (bytesread
!= NULL
)
594 if (samplesread
!= NULL
)
600 /* check parameters */
601 if (samples
!= 1 && (bytesread
== NULL
&& samplesread
== NULL
))
602 return AVIERR_BADPARAM
;
603 if (samples
== -1) /* read as much as we could */
606 if (This
->pg
== NULL
) {
607 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
613 /* compress or decompress? */
614 if (This
->hic
== NULL
) {
616 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, start
);
618 return AVIERR_MEMORY
;
620 if (buffer
!= NULL
&& buffersize
> 0) {
621 /* check buffersize */
622 if (buffersize
< lpbi
->biSizeImage
)
623 return AVIERR_BUFFERTOOSMALL
;
625 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
628 /* fill out return parameters if given */
629 if (bytesread
!= NULL
)
630 *bytesread
= lpbi
->biSizeImage
;
633 if (This
->lCurrent
> start
)
636 while (start
> This
->lCurrent
) {
639 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
642 return AVIERR_MEMORY
;
645 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
652 if (buffer
!= NULL
&& buffersize
> 0) {
653 /* check buffersize */
654 if (This
->lpbiCur
->biSizeImage
> buffersize
)
655 return AVIERR_BUFFERTOOSMALL
;
657 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
660 /* fill out return parameters if given */
661 if (bytesread
!= NULL
)
662 *bytesread
= This
->lpbiCur
->biSizeImage
;
665 /* fill out return parameters if given */
666 if (samplesread
!= NULL
)
672 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
673 LONG samples
, LPVOID buffer
,
674 LONG buffersize
, DWORD flags
,
678 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
682 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
683 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
685 /* clear return parameters if given */
686 if (sampwritten
!= NULL
)
688 if (byteswritten
!= NULL
)
691 /* check parameters */
692 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
693 return AVIERR_BADPARAM
;
695 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
696 /* only pass through */
697 flags
|= AVIIF_KEYFRAME
;
699 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
700 flags
, sampwritten
, byteswritten
);
702 /* compress data before writing to pStream */
703 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
704 return AVIERR_UNSUPPORTED
;
706 This
->lCurrent
= start
;
707 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
711 if (This
->lLastKey
== start
)
712 flags
|= AVIIF_KEYFRAME
;
714 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
715 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
720 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
723 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
725 TRACE("(%p,%d,%d)\n", iface
, start
, samples
);
727 return IAVIStream_Delete(This
->pStream
, start
, samples
);
730 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
731 LPVOID lp
, LPLONG lpread
)
733 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
735 TRACE("(%p,0x%08X,%p,%p)\n", iface
, fcc
, lp
, lpread
);
737 assert(This
->pStream
!= NULL
);
739 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
742 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
743 LPVOID lp
, LONG size
)
745 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
747 TRACE("(%p,0x%08x,%p,%d)\n", iface
, fcc
, lp
, size
);
749 assert(This
->pStream
!= NULL
);
751 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
754 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
755 LPAVISTREAMINFOW info
, LONG infolen
)
757 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
762 /***********************************************************************/
764 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
765 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
767 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
771 BOOL bDecreasedQual
= FALSE
;
775 /* make lKeyFrameEvery and at start a keyframe */
776 if ((This
->lKeyFrameEvery
!= 0 &&
777 (This
->lCurrent
- This
->lLastKey
) >= This
->lKeyFrameEvery
) ||
778 This
->lCurrent
== This
->sInfo
.dwStart
) {
779 idxFlags
= AVIIF_KEYFRAME
;
780 icmFlags
= ICCOMPRESS_KEYFRAME
;
783 if (This
->lKeyFrameEvery
!= 0) {
784 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
785 if (idxFlags
& AVIIF_KEYFRAME
) {
786 /* for keyframes allow to consume all unused bytes */
787 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
788 This
->dwUnusedBytes
= 0;
790 /* for non-keyframes only allow something of the unused bytes to be consumed */
794 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
795 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
796 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
798 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
799 This
->dwUnusedBytes
-= tmp2
;
802 dwRequest
= MAX_FRAMESIZE
;
804 /* only one keyframe at start desired */
805 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
806 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
807 This
->dwUnusedBytes
= 0;
809 dwRequest
= MAX_FRAMESIZE
;
812 /* must we check for framesize to gain requested
813 * datarate or could we trust codec? */
814 doSizeCheck
= (dwRequest
!= 0 && ((This
->dwICMFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0));
816 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
817 dwMinQual
= ICQUALITY_LOW
;
820 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
821 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
828 hr
= ICCompress(This
->hic
,icmFlags
,This
->lpbiCur
,This
->lpCur
,lpbi
,lpBits
,
829 &idxCkid
, &idxFlags
, This
->lCurrent
, dwRequest
, dwCurQual
,
830 noPrev
? NULL
:This
->lpbiPrev
, noPrev
? NULL
:This
->lpPrev
);
831 if (hr
== ICERR_NEWPALETTE
) {
832 FIXME(": codec has changed palette -- unhandled!\n");
833 } else if (hr
!= ICERR_OK
)
834 return AVIERR_COMPRESSOR
;
836 /* need to check for framesize */
840 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
841 /* frame is smaller -- try to maximize quality */
842 if (dwMaxQual
- dwCurQual
> 10) {
843 DWORD tmp
= dwRequest
/ 8;
845 if (tmp
< MAX_FRAMESIZE_DIFF
)
846 tmp
= MAX_FRAMESIZE_DIFF
;
848 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
850 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
856 } else if (dwMaxQual
- dwMinQual
<= 1) {
859 dwMaxQual
= dwCurQual
;
861 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
862 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
864 FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n",
865 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
867 bDecreasedQual
= TRUE
;
871 /* remember some values */
872 This
->dwLastQuality
= dwCurQual
;
873 This
->dwUnusedBytes
= dwRequest
- This
->lpbiCur
->biSizeImage
;
874 if (icmFlags
& ICCOMPRESS_KEYFRAME
)
875 This
->lLastKey
= This
->lCurrent
;
877 /* Does we manage previous frame? */
878 if (This
->lpPrev
!= NULL
&& This
->lKeyFrameEvery
!= 1)
879 ICDecompress(This
->hic
, 0, This
->lpbiCur
, This
->lpCur
,
880 This
->lpbiPrev
, This
->lpPrev
);
885 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
887 LPBITMAPINFOHEADER lpbi
;
891 assert(This
!= NULL
);
892 assert(This
->pStream
!= NULL
);
893 assert(This
->pg
== NULL
);
895 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
896 if (This
->pg
== NULL
)
899 /* When we only decompress this is enough */
900 if (This
->sInfo
.fccHandler
== comptypeDIB
)
903 assert(This
->hic
!= NULL
);
904 assert(This
->lpbiOutput
== NULL
);
906 /* get input format */
907 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
909 return AVIERR_MEMORY
;
911 /* get memory for output format */
912 size
= ICCompressGetFormatSize(This
->hic
, lpbi
);
913 if ((LONG
)size
< (LONG
)sizeof(BITMAPINFOHEADER
))
914 return AVIERR_COMPRESSOR
;
915 This
->lpbiOutput
= HeapAlloc(GetProcessHeap(), 0, size
);
916 if (This
->lpbiOutput
== NULL
)
917 return AVIERR_MEMORY
;
918 This
->cbOutput
= size
;
920 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
921 return AVIERR_BADFORMAT
;
923 /* update AVISTREAMINFO structure */
924 This
->sInfo
.rcFrame
.right
=
925 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
926 This
->sInfo
.rcFrame
.bottom
=
927 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
928 This
->sInfo
.dwSuggestedBufferSize
=
929 ICCompressGetSize(This
->hic
, lpbi
, This
->lpbiOutput
);
931 /* prepare codec for compression */
932 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
933 return AVIERR_COMPRESSOR
;
935 /* allocate memory for current frame */
936 size
+= This
->sInfo
.dwSuggestedBufferSize
;
937 This
->lpbiCur
= HeapAlloc(GetProcessHeap(), 0, size
);
938 if (This
->lpbiCur
== NULL
)
939 return AVIERR_MEMORY
;
940 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
941 This
->lpCur
= DIBPTR(This
->lpbiCur
);
943 /* allocate memory for last frame if needed */
944 if (This
->lKeyFrameEvery
!= 1 &&
945 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
946 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
947 This
->lpbiPrev
= HeapAlloc(GetProcessHeap(), 0, size
);
948 if (This
->lpbiPrev
== NULL
)
949 return AVIERR_MEMORY
;
950 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
951 return AVIERR_COMPRESSOR
;
953 if (This
->lpbiPrev
->biSizeImage
== 0) {
954 This
->lpbiPrev
->biSizeImage
=
955 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
958 /* get memory for format and picture */
959 size
+= This
->lpbiPrev
->biSizeImage
;
960 This
->lpbiPrev
= HeapReAlloc(GetProcessHeap(), 0, This
->lpbiPrev
, size
);
961 if (This
->lpbiPrev
== NULL
)
962 return AVIERR_MEMORY
;
963 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
965 /* prepare codec also for decompression */
966 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
967 return AVIERR_COMPRESSOR
;