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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,REFIID refiid
,LPVOID
*obj
);
42 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
);
43 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
);
44 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
,LPARAM lParam1
,LPARAM lParam2
);
45 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,AVISTREAMINFOW
*psi
,LONG size
);
46 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
,LONG pos
,LONG flags
);
47 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG
*formatsize
);
48 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG formatsize
);
49 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,LONG
*bytesread
,LONG
*samplesread
);
50 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,DWORD flags
,LONG
*sampwritten
,LONG
*byteswritten
);
51 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
,LONG start
,LONG samples
);
52 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG
*lpread
);
53 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG size
);
54 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,AVISTREAMINFOW
*info
,LONG infolen
);
56 struct ICOM_VTABLE(IAVIStream
) iicmst
= {
57 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
58 ICMStream_fnQueryInterface
,
63 ICMStream_fnFindSample
,
64 ICMStream_fnReadFormat
,
65 ICMStream_fnSetFormat
,
70 ICMStream_fnWriteData
,
74 typedef struct _IAVIStreamImpl
{
76 ICOM_VFIELD(IAVIStream
);
79 /* IAVIStream stuff */
91 DWORD dwBytesPerFrame
;
94 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
96 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
99 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
101 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
105 /***********************************************************************/
107 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
108 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
109 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
111 inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
115 This
->dwLastQuality
= ICQUALITY_HIGH
;
116 This
->dwUnusedBytes
= 0;
119 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
121 IAVIStreamImpl
*pstream
;
124 assert(riid
!= NULL
&& ppv
!= NULL
);
128 pstream
= (IAVIStreamImpl
*)LocalAlloc(LPTR
, sizeof(IAVIStreamImpl
));
130 return AVIERR_MEMORY
;
132 ICOM_VTBL(pstream
) = &iicmst
;
133 AVIFILE_Reset(pstream
);
135 hr
= IUnknown_QueryInterface((IUnknown
*)pstream
, riid
, ppv
);
137 LocalFree((HLOCAL
)pstream
);
142 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
143 REFIID refiid
, LPVOID
*obj
)
145 ICOM_THIS(IAVIStreamImpl
,iface
);
147 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
149 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
150 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
152 IAVIStream_AddRef(iface
);
157 return OLE_E_ENUM_NOMORE
;
160 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
162 ICOM_THIS(IAVIStreamImpl
,iface
);
164 TRACE("(%p) -> %ld\n", iface
, This
->ref
+ 1);
166 /* also add reference to the nested stream */
167 if (This
->pStream
!= NULL
)
168 IAVIStream_AddRef(This
->pStream
);
170 return ++(This
->ref
);
173 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
175 ICOM_THIS(IAVIStreamImpl
,iface
);
177 TRACE("(%p) -> %ld\n", iface
, This
->ref
- 1);
179 if (This
->ref
== 0) {
181 if (This
->pg
!= NULL
) {
182 AVIStreamGetFrameClose(This
->pg
);
185 if (This
->pStream
!= NULL
) {
186 IAVIStream_Release(This
->pStream
);
187 This
->pStream
= NULL
;
189 if (This
->hic
!= (HIC
)NULL
) {
190 if (This
->lpbiPrev
!= NULL
) {
191 ICDecompressEnd(This
->hic
);
192 GlobalFreePtr(This
->lpbiPrev
);
193 This
->lpbiPrev
= NULL
;
196 ICCompressEnd(This
->hic
);
197 This
->hic
= (HIC
)NULL
;
199 if (This
->lpbiCur
!= NULL
) {
200 GlobalFreePtr(This
->lpbiCur
);
201 This
->lpbiCur
= NULL
;
204 if (This
->lpbiOutput
!= NULL
) {
205 GlobalFreePtr(This
->lpbiOutput
);
206 This
->lpbiOutput
= NULL
;
209 if (This
->lpbiInput
!= NULL
) {
210 GlobalFreePtr(This
->lpbiInput
);
211 This
->lpbiInput
= NULL
;
215 LocalFree((HLOCAL
)This
);
220 /* also release reference to the nested stream */
221 if (This
->pStream
!= NULL
)
222 IAVIStream_Release(This
->pStream
);
227 /* lParam1: PAVISTREAM
228 * lParam2: LPAVICOMPRESSOPTIONS
230 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
233 ICOM_THIS(IAVIStreamImpl
,iface
);
236 ICCOMPRESSFRAMES icFrames
;
237 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
239 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
241 /* check parameter */
242 if ((LPVOID
)lParam1
== NULL
)
243 return AVIERR_BADPARAM
;
245 /* get infos from stream */
246 IAVIStream_Info((PAVISTREAM
)lParam1
, &This
->sInfo
, sizeof(This
->sInfo
));
247 if (This
->sInfo
.fccType
!= streamtypeVIDEO
)
248 return AVIERR_ERROR
; /* error in registry or AVIMakeCompressedStream */
250 /* add reference to the stream */
251 This
->pStream
= (PAVISTREAM
)lParam1
;
252 IAVIStream_AddRef(This
->pStream
);
256 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
257 /* we should compress */
258 This
->sInfo
.fccHandler
= pco
->fccHandler
;
260 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
261 if (This
->hic
== (HIC
)NULL
)
262 return AVIERR_NOCOMPRESSOR
;
264 /* restore saved state of codec */
265 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
266 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
269 /* set quality -- resolve default quality */
270 This
->sInfo
.dwQuality
= pco
->dwQuality
;
271 if (pco
->dwQuality
== ICQUALITY_DEFAULT
)
272 This
->sInfo
.dwQuality
= ICGetDefaultQuality(This
->hic
);
274 /* get capabilities of codec */
275 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
276 This
->dwICMFlags
= icinfo
.dwFlags
;
279 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
280 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
281 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
283 This
->lKeyFrameEvery
= 1;
286 if ((pco
->dwFlags
& AVICOMPRESSF_DATARATE
)) {
287 /* Do we have a chance to reduce size to desired one? */
288 if ((icinfo
.dwFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0)
289 return AVIERR_NOCOMPRESSOR
;
291 assert(This
->sInfo
.dwRate
!= 0);
293 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
294 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
296 pco
->dwBytesPerSecond
= 0;
297 This
->dwBytesPerFrame
= 0;
300 if (icinfo
.dwFlags
& VIDCF_COMPRESSFRAMES
) {
301 memset(&icFrames
, 0, sizeof(icFrames
));
302 icFrames
.lpbiOutput
= This
->lpbiOutput
;
303 icFrames
.lpbiInput
= This
->lpbiInput
;
304 icFrames
.lFrameCount
= This
->sInfo
.dwLength
;
305 icFrames
.lQuality
= This
->sInfo
.dwQuality
;
306 icFrames
.lDataRate
= pco
->dwBytesPerSecond
;
307 icFrames
.lKeyRate
= This
->lKeyFrameEvery
;
308 icFrames
.dwRate
= This
->sInfo
.dwRate
;
309 icFrames
.dwScale
= This
->sInfo
.dwScale
;
310 ICSendMessage(This
->hic
, ICM_COMPRESS_FRAMES_INFO
,
311 (LPARAM
)&icFrames
, (LPARAM
)sizeof(icFrames
));
314 This
->sInfo
.fccHandler
= comptypeDIB
;
319 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
322 ICOM_THIS(IAVIStreamImpl
,iface
);
324 TRACE("(%p,%p,%ld)\n", iface
, psi
, size
);
327 return AVIERR_BADPARAM
;
329 return AVIERR_BADSIZE
;
331 memcpy(psi
, &This
->sInfo
, min(size
, sizeof(This
->sInfo
)));
333 if (size
< sizeof(This
->sInfo
))
334 return AVIERR_BUFFERTOOSMALL
;
338 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
341 ICOM_THIS(IAVIStreamImpl
,iface
);
343 TRACE("(%p,%ld,0x%08lX)\n",iface
,pos
,flags
);
345 if (flags
& FIND_FROM_START
) {
346 pos
= This
->sInfo
.dwStart
;
347 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
351 if (flags
& FIND_RET
)
352 WARN(": FIND_RET flags will be ignored!\n");
354 if (flags
& FIND_KEY
) {
355 if (This
->hic
== (HIC
)NULL
)
356 return pos
; /* we decompress so every frame is a keyframe */
358 if (flags
& FIND_PREV
) {
359 /* need to read old or new frames? */
360 if (This
->lLastKey
<= pos
|| pos
< This
->lCurrent
)
361 IAVIStream_Read(iface
, pos
, 1, NULL
, 0, NULL
, NULL
);
363 return This
->lLastKey
;
365 } else if (flags
& FIND_ANY
) {
366 return pos
; /* We really don't know, reread is to expensive, so guess. */
367 } else if (flags
& FIND_FORMAT
) {
368 if (flags
& FIND_PREV
)
375 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
376 LPVOID format
, LONG
*formatsize
)
378 ICOM_THIS(IAVIStreamImpl
,iface
);
380 LPBITMAPINFOHEADER lpbi
;
383 TRACE("(%p,%ld,%p,%p)\n", iface
, pos
, format
, formatsize
);
385 if (formatsize
== NULL
)
386 return AVIERR_BADPARAM
;
388 if (This
->pg
== NULL
) {
389 hr
= AVIFILE_OpenGetFrame(This
);
395 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, pos
);
397 return AVIERR_MEMORY
;
399 if (This
->hic
== (HIC
)NULL
) {
400 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
403 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
404 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
406 This
->cbOutput
= size
;
407 if (format
!= NULL
) {
408 if (This
->lpbiOutput
!= NULL
)
409 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
411 memcpy(format
, lpbi
, min(*formatsize
, size
));
414 } else if (format
!= NULL
)
415 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
417 if (*formatsize
< This
->cbOutput
)
418 hr
= AVIERR_BUFFERTOOSMALL
;
422 *formatsize
= This
->cbOutput
;
426 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
427 LPVOID format
, LONG formatsize
)
429 ICOM_THIS(IAVIStreamImpl
,iface
);
431 TRACE("(%p,%ld,%p,%ld)\n", iface
, pos
, format
, formatsize
);
433 /* check parameters */
434 if (format
== NULL
|| formatsize
<= 0)
435 return AVIERR_BADPARAM
;
437 /* We can only accept RGB data for writing */
438 if (((LPBITMAPINFOHEADER
)format
)->biCompression
!= BI_RGB
) {
439 WARN(": need RGB data as input\n");
440 return AVIERR_UNSUPPORTED
;
443 /* Input format already known?
444 * Changing of palette is supported, but be quiet if it's the same */
445 if (This
->lpbiInput
!= NULL
) {
446 if (This
->cbInput
!= formatsize
)
447 return AVIERR_UNSUPPORTED
;
449 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
453 /* Does the nested stream support writing? */
454 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
455 return AVIERR_READONLY
;
457 /* check if frame is already written */
458 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
459 return AVIERR_UNSUPPORTED
;
461 /* check if we should compress */
462 if (This
->sInfo
.fccHandler
== 0 ||
463 This
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E'))
464 This
->sInfo
.fccHandler
= comptypeDIB
;
466 /* only pass through? */
467 if (This
->sInfo
.fccHandler
== comptypeDIB
)
468 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
470 /* initial format setting? */
471 if (This
->lpbiInput
== NULL
) {
474 assert(This
->hic
!= (HIC
)NULL
);
476 /* get memory for input format */
477 This
->lpbiInput
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, formatsize
);
478 if (This
->lpbiInput
== NULL
)
479 return AVIERR_MEMORY
;
480 This
->cbInput
= formatsize
;
481 memcpy(This
->lpbiInput
, format
, formatsize
);
483 /* get output format */
484 size
= ICCompressGetFormatSize(This
->hic
, This
->lpbiInput
);
485 if (size
< sizeof(BITMAPINFOHEADER
))
486 return AVIERR_COMPRESSOR
;
487 This
->lpbiOutput
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
488 if (This
->lpbiOutput
== NULL
)
489 return AVIERR_MEMORY
;
490 This
->cbOutput
= size
;
491 if (ICCompressGetFormat(This
->hic
,This
->lpbiInput
,This
->lpbiOutput
) < S_OK
)
492 return AVIERR_COMPRESSOR
;
494 /* update AVISTREAMINFO structure */
495 This
->sInfo
.rcFrame
.right
=
496 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
497 This
->sInfo
.rcFrame
.bottom
=
498 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
500 /* prepare codec for compression */
501 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
502 return AVIERR_COMPRESSOR
;
504 /* allocate memory for compressed frame */
505 size
= ICCompressGetSize(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
);
507 (LPBITMAPINFOHEADER
)GlobalAllocPtr(GMEM_MOVEABLE
, This
->cbOutput
+ size
);
508 if (This
->lpbiCur
== NULL
)
509 return AVIERR_MEMORY
;
510 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
511 This
->lpCur
= DIBPTR(This
->lpbiCur
);
513 /* allocate memory for last frame if needed */
514 if (This
->lKeyFrameEvery
!= 1 &&
515 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
516 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
517 This
->lpbiPrev
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
518 if (This
->lpbiPrev
== NULL
)
519 return AVIERR_MEMORY
;
520 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
521 return AVIERR_COMPRESSOR
;
523 if (This
->lpbiPrev
->biSizeImage
== 0) {
524 This
->lpbiPrev
->biSizeImage
=
525 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
528 /* get memory for format and picture */
529 size
+= This
->lpbiPrev
->biSizeImage
;
531 (LPBITMAPINFOHEADER
)GlobalReAllocPtr(This
->lpbiPrev
,size
,GMEM_MOVEABLE
);
532 if (This
->lpbiPrev
== NULL
)
533 return AVIERR_MEMORY
;
534 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
536 /* prepare codec also for decompression */
537 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
538 return AVIERR_COMPRESSOR
;
541 /* format change -- check that's only the palette */
542 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)format
;
544 if (lpbi
->biSize
!= This
->lpbiInput
->biSize
||
545 lpbi
->biWidth
!= This
->lpbiInput
->biWidth
||
546 lpbi
->biHeight
!= This
->lpbiInput
->biHeight
||
547 lpbi
->biBitCount
!= This
->lpbiInput
->biBitCount
||
548 lpbi
->biPlanes
!= This
->lpbiInput
->biPlanes
||
549 lpbi
->biCompression
!= This
->lpbiInput
->biCompression
||
550 lpbi
->biClrUsed
!= This
->lpbiInput
->biClrUsed
)
551 return AVIERR_UNSUPPORTED
;
553 /* get new output format */
554 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
555 return AVIERR_BADFORMAT
;
557 /* restart compression */
558 ICCompressEnd(This
->hic
);
559 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
560 return AVIERR_COMPRESSOR
;
562 /* check if we need to restart decompresion also */
563 if (This
->lKeyFrameEvery
!= 1 &&
564 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
565 ICDecompressEnd(This
->hic
);
566 if (ICDecompressGetFormat(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) < S_OK
)
567 return AVIERR_COMPRESSOR
;
568 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
569 return AVIERR_COMPRESSOR
;
573 /* tell nested stream the new format */
574 return IAVIStream_SetFormat(This
->pStream
, pos
,
575 This
->lpbiOutput
, This
->cbOutput
);
578 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
579 LONG samples
, LPVOID buffer
,
580 LONG buffersize
, LPLONG bytesread
,
583 ICOM_THIS(IAVIStreamImpl
,iface
);
585 LPBITMAPINFOHEADER lpbi
;
587 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface
, start
, samples
, buffer
,
588 buffersize
, bytesread
, samplesread
);
590 /* clear return parameters if given */
591 if (bytesread
!= NULL
)
593 if (samplesread
!= NULL
)
599 /* check parameters */
600 if (samples
!= 1 && (bytesread
== NULL
&& samplesread
== NULL
))
601 return AVIERR_BADPARAM
;
602 if (samples
== -1) /* read as much as we could */
605 if (This
->pg
== NULL
) {
606 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
612 /* compress or decompress? */
613 if (This
->hic
== (HIC
)NULL
) {
615 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, start
);
617 return AVIERR_MEMORY
;
619 if (buffer
!= NULL
&& buffersize
> 0) {
620 /* check buffersize */
621 if (buffersize
< lpbi
->biSizeImage
)
622 return AVIERR_BUFFERTOOSMALL
;
624 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
627 /* fill out return parameters if given */
628 if (bytesread
!= NULL
)
629 *bytesread
= lpbi
->biSizeImage
;
632 if (This
->lCurrent
> start
)
635 while (start
> This
->lCurrent
) {
638 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
641 return AVIERR_MEMORY
;
644 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
651 if (buffer
!= NULL
&& buffersize
> 0) {
652 /* check buffersize */
653 if (This
->lpbiCur
->biSizeImage
> buffersize
)
654 return AVIERR_BUFFERTOOSMALL
;
656 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
659 /* fill out return parameters if given */
660 if (bytesread
!= NULL
)
661 *bytesread
= This
->lpbiCur
->biSizeImage
;
664 /* fill out return parameters if given */
665 if (samplesread
!= NULL
)
671 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
672 LONG samples
, LPVOID buffer
,
673 LONG buffersize
, DWORD flags
,
677 ICOM_THIS(IAVIStreamImpl
,iface
);
681 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface
, start
, samples
,
682 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
684 /* clear return parameters if given */
685 if (sampwritten
!= NULL
)
687 if (byteswritten
!= NULL
)
690 /* check parameters */
691 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
692 return AVIERR_BADPARAM
;
694 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
695 /* only pass through */
696 flags
|= AVIIF_KEYFRAME
;
698 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
699 flags
, sampwritten
, byteswritten
);
701 /* compress data before writing to pStream */
702 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
703 return AVIERR_UNSUPPORTED
;
705 This
->lCurrent
= start
;
706 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
710 if (This
->lLastKey
== start
)
711 flags
|= AVIIF_KEYFRAME
;
713 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
714 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
719 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
722 ICOM_THIS(IAVIStreamImpl
,iface
);
724 TRACE("(%p,%ld,%ld)\n", iface
, start
, samples
);
726 return IAVIStream_Delete(This
->pStream
, start
, samples
);
729 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
730 LPVOID lp
, LPLONG lpread
)
732 ICOM_THIS(IAVIStreamImpl
,iface
);
734 TRACE("(%p,0x%08lX,%p,%p)\n", iface
, fcc
, lp
, lpread
);
736 assert(This
->pStream
!= NULL
);
738 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
741 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
742 LPVOID lp
, LONG size
)
744 ICOM_THIS(IAVIStreamImpl
,iface
);
746 TRACE("(%p,0x%08lx,%p,%ld)\n", iface
, fcc
, lp
, size
);
748 assert(This
->pStream
!= NULL
);
750 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
753 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
754 LPAVISTREAMINFOW info
, LONG infolen
)
756 FIXME("(%p,%p,%ld): stub\n", iface
, info
, infolen
);
761 /***********************************************************************/
763 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
764 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
766 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
770 BOOL bDecreasedQual
= FALSE
;
774 /* make lKeyFrameEvery and at start a keyframe */
775 if ((This
->lKeyFrameEvery
!= 0 &&
776 (This
->lCurrent
- This
->lLastKey
) >= This
->lKeyFrameEvery
) ||
777 This
->lCurrent
== This
->sInfo
.dwStart
) {
778 idxFlags
= AVIIF_KEYFRAME
;
779 icmFlags
= ICCOMPRESS_KEYFRAME
;
782 if (This
->lKeyFrameEvery
!= 0) {
783 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
784 if (idxFlags
& AVIIF_KEYFRAME
) {
785 /* for keyframes allow to consume all unused bytes */
786 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
787 This
->dwUnusedBytes
= 0;
789 /* for non-keyframes only allow something of the unused bytes to be consumed */
793 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
794 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
795 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
797 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
798 This
->dwUnusedBytes
-= tmp2
;
801 dwRequest
= MAX_FRAMESIZE
;
803 /* only one keyframe at start desired */
804 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
805 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
806 This
->dwUnusedBytes
= 0;
808 dwRequest
= MAX_FRAMESIZE
;
811 /* must we check for framesize to gain requested
812 * datarate or could we trust codec? */
813 doSizeCheck
= (dwRequest
!= 0 && ((This
->dwICMFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0));
815 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
816 dwMinQual
= ICQUALITY_LOW
;
819 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
820 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
827 hr
= ICCompress(This
->hic
,icmFlags
,This
->lpbiCur
,This
->lpCur
,lpbi
,lpBits
,
828 &idxCkid
, &idxFlags
, This
->lCurrent
, dwRequest
, dwCurQual
,
829 noPrev
? NULL
:This
->lpbiPrev
, noPrev
? NULL
:This
->lpPrev
);
830 if (hr
== ICERR_NEWPALETTE
) {
831 FIXME(": codec has changed palette -- unhandled!\n");
832 } else if (hr
!= ICERR_OK
)
833 return AVIERR_COMPRESSOR
;
835 /* need to check for framesize */
839 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
840 /* frame is smaller -- try to maximize quality */
841 if (dwMaxQual
- dwCurQual
> 10) {
842 DWORD tmp
= dwRequest
/ 8;
844 if (tmp
< MAX_FRAMESIZE_DIFF
)
845 tmp
= MAX_FRAMESIZE_DIFF
;
847 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
849 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
855 } else if (dwMaxQual
- dwMinQual
<= 1) {
858 dwMaxQual
= dwCurQual
;
860 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
861 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
863 FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
864 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
866 bDecreasedQual
= TRUE
;
870 /* remember some values */
871 This
->dwLastQuality
= dwCurQual
;
872 This
->dwUnusedBytes
= dwRequest
- This
->lpbiCur
->biSizeImage
;
873 if (icmFlags
& ICCOMPRESS_KEYFRAME
)
874 This
->lLastKey
= This
->lCurrent
;
876 /* Does we manage previous frame? */
877 if (This
->lpPrev
!= NULL
&& This
->lKeyFrameEvery
!= 1)
878 ICDecompress(This
->hic
, 0, This
->lpbiCur
, This
->lpCur
,
879 This
->lpbiPrev
, This
->lpPrev
);
884 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
886 LPBITMAPINFOHEADER lpbi
;
890 assert(This
!= NULL
);
891 assert(This
->pStream
!= NULL
);
892 assert(This
->pg
== NULL
);
894 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
895 if (This
->pg
== NULL
)
898 /* When we only decompress this is enough */
899 if (This
->sInfo
.fccHandler
== comptypeDIB
)
902 assert(This
->hic
!= (HIC
)NULL
);
903 assert(This
->lpbiOutput
== NULL
);
905 /* get input format */
906 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
908 return AVIERR_MEMORY
;
910 /* get memory for output format */
911 size
= ICCompressGetFormatSize(This
->hic
, lpbi
);
912 if (size
< sizeof(BITMAPINFOHEADER
))
913 return AVIERR_COMPRESSOR
;
914 This
->lpbiOutput
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
915 if (This
->lpbiOutput
== NULL
)
916 return AVIERR_MEMORY
;
917 This
->cbOutput
= size
;
919 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
920 return AVIERR_BADFORMAT
;
922 /* update AVISTREAMINFO structure */
923 This
->sInfo
.rcFrame
.right
=
924 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
925 This
->sInfo
.rcFrame
.bottom
=
926 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
927 This
->sInfo
.dwSuggestedBufferSize
=
928 ICCompressGetSize(This
->hic
, lpbi
, This
->lpbiOutput
);
930 /* prepare codec for compression */
931 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
932 return AVIERR_COMPRESSOR
;
934 /* allocate memory for current frame */
935 size
+= This
->sInfo
.dwSuggestedBufferSize
;
936 This
->lpbiCur
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GMEM_MOVEABLE
, size
);
937 if (This
->lpbiCur
== NULL
)
938 return AVIERR_MEMORY
;
939 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
940 This
->lpCur
= DIBPTR(This
->lpbiCur
);
942 /* allocate memory for last frame if needed */
943 if (This
->lKeyFrameEvery
!= 1 &&
944 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
945 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
946 This
->lpbiPrev
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
947 if (This
->lpbiPrev
== NULL
)
948 return AVIERR_MEMORY
;
949 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
950 return AVIERR_COMPRESSOR
;
952 if (This
->lpbiPrev
->biSizeImage
== 0) {
953 This
->lpbiPrev
->biSizeImage
=
954 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
957 /* get memory for format and picture */
958 size
+= This
->lpbiPrev
->biSizeImage
;
960 (LPBITMAPINFOHEADER
)GlobalReAllocPtr(This
->lpbiPrev
,size
,GMEM_MOVEABLE
);
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
;