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 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 static const struct IAVIStreamVtbl iicmst
= {
57 ICMStream_fnQueryInterface
,
62 ICMStream_fnFindSample
,
63 ICMStream_fnReadFormat
,
64 ICMStream_fnSetFormat
,
69 ICMStream_fnWriteData
,
73 typedef struct _IAVIStreamImpl
{
75 const IAVIStreamVtbl
*lpVtbl
;
78 /* IAVIStream stuff */
90 DWORD dwBytesPerFrame
;
93 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
95 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
98 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
100 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
104 /***********************************************************************/
106 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
107 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
108 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
110 static inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
114 This
->dwLastQuality
= ICQUALITY_HIGH
;
115 This
->dwUnusedBytes
= 0;
118 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
120 IAVIStreamImpl
*pstream
;
123 assert(riid
!= NULL
&& ppv
!= NULL
);
127 pstream
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIStreamImpl
));
129 return AVIERR_MEMORY
;
131 pstream
->lpVtbl
= &iicmst
;
132 AVIFILE_Reset(pstream
);
134 hr
= IAVIStream_QueryInterface((IAVIStream
*)pstream
, riid
, ppv
);
136 HeapFree(GetProcessHeap(), 0, pstream
);
141 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
142 REFIID refiid
, LPVOID
*obj
)
144 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
146 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
148 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
149 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
151 IAVIStream_AddRef(iface
);
156 return OLE_E_ENUM_NOMORE
;
159 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
161 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
162 ULONG ref
= InterlockedIncrement(&This
->ref
);
164 TRACE("(%p) -> %d\n", iface
, ref
);
166 /* also add reference to the nested stream */
167 if (This
->pStream
!= NULL
)
168 IAVIStream_AddRef(This
->pStream
);
173 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
175 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
176 ULONG ref
= InterlockedDecrement(&This
->ref
);
178 TRACE("(%p) -> %d\n", iface
, ref
);
182 if (This
->pg
!= NULL
) {
183 AVIStreamGetFrameClose(This
->pg
);
186 if (This
->pStream
!= NULL
) {
187 IAVIStream_Release(This
->pStream
);
188 This
->pStream
= NULL
;
190 if (This
->hic
!= NULL
) {
191 if (This
->lpbiPrev
!= NULL
) {
192 ICDecompressEnd(This
->hic
);
193 HeapFree(GetProcessHeap(), 0, This
->lpbiPrev
);
194 This
->lpbiPrev
= NULL
;
197 ICCompressEnd(This
->hic
);
200 if (This
->lpbiCur
!= NULL
) {
201 HeapFree(GetProcessHeap(), 0, This
->lpbiCur
);
202 This
->lpbiCur
= NULL
;
205 if (This
->lpbiOutput
!= NULL
) {
206 HeapFree(GetProcessHeap(), 0, This
->lpbiOutput
);
207 This
->lpbiOutput
= NULL
;
210 if (This
->lpbiInput
!= NULL
) {
211 HeapFree(GetProcessHeap(), 0, This
->lpbiInput
);
212 This
->lpbiInput
= NULL
;
216 HeapFree(GetProcessHeap(), 0, This
);
221 /* also release reference to the nested stream */
222 if (This
->pStream
!= NULL
)
223 IAVIStream_Release(This
->pStream
);
228 /* lParam1: PAVISTREAM
229 * lParam2: LPAVICOMPRESSOPTIONS
231 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
234 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
237 ICCOMPRESSFRAMES icFrames
;
238 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
240 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
242 /* check parameter */
243 if ((LPVOID
)lParam1
== NULL
)
244 return AVIERR_BADPARAM
;
246 /* get infos from stream */
247 IAVIStream_Info((PAVISTREAM
)lParam1
, &This
->sInfo
, sizeof(This
->sInfo
));
248 if (This
->sInfo
.fccType
!= streamtypeVIDEO
)
249 return AVIERR_ERROR
; /* error in registry or AVIMakeCompressedStream */
251 /* add reference to the stream */
252 This
->pStream
= (PAVISTREAM
)lParam1
;
253 IAVIStream_AddRef(This
->pStream
);
257 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
258 /* we should compress */
259 This
->sInfo
.fccHandler
= pco
->fccHandler
;
261 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
262 if (This
->hic
== NULL
)
263 return AVIERR_NOCOMPRESSOR
;
265 /* restore saved state of codec */
266 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
267 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
270 /* set quality -- resolve default quality */
271 This
->sInfo
.dwQuality
= pco
->dwQuality
;
272 if (pco
->dwQuality
== ICQUALITY_DEFAULT
)
273 This
->sInfo
.dwQuality
= ICGetDefaultQuality(This
->hic
);
275 /* get capabilities of codec */
276 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
277 This
->dwICMFlags
= icinfo
.dwFlags
;
280 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
281 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
282 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
284 This
->lKeyFrameEvery
= 1;
287 if ((pco
->dwFlags
& AVICOMPRESSF_DATARATE
)) {
288 /* Do we have a chance to reduce size to desired one? */
289 if ((icinfo
.dwFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0)
290 return AVIERR_NOCOMPRESSOR
;
292 assert(This
->sInfo
.dwRate
!= 0);
294 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
295 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
297 pco
->dwBytesPerSecond
= 0;
298 This
->dwBytesPerFrame
= 0;
301 if (icinfo
.dwFlags
& VIDCF_COMPRESSFRAMES
) {
302 memset(&icFrames
, 0, sizeof(icFrames
));
303 icFrames
.lpbiOutput
= This
->lpbiOutput
;
304 icFrames
.lpbiInput
= This
->lpbiInput
;
305 icFrames
.lFrameCount
= This
->sInfo
.dwLength
;
306 icFrames
.lQuality
= This
->sInfo
.dwQuality
;
307 icFrames
.lDataRate
= pco
->dwBytesPerSecond
;
308 icFrames
.lKeyRate
= This
->lKeyFrameEvery
;
309 icFrames
.dwRate
= This
->sInfo
.dwRate
;
310 icFrames
.dwScale
= This
->sInfo
.dwScale
;
311 ICSendMessage(This
->hic
, ICM_COMPRESS_FRAMES_INFO
,
312 (LPARAM
)&icFrames
, (LPARAM
)sizeof(icFrames
));
315 This
->sInfo
.fccHandler
= comptypeDIB
;
320 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
323 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
325 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
328 return AVIERR_BADPARAM
;
330 return AVIERR_BADSIZE
;
332 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
334 if ((DWORD
)size
< sizeof(This
->sInfo
))
335 return AVIERR_BUFFERTOOSMALL
;
339 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
342 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
344 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
346 if (flags
& FIND_FROM_START
) {
347 pos
= This
->sInfo
.dwStart
;
348 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
352 if (flags
& FIND_RET
)
353 WARN(": FIND_RET flags will be ignored!\n");
355 if (flags
& FIND_KEY
) {
356 if (This
->hic
== NULL
)
357 return pos
; /* we decompress so every frame is a keyframe */
359 if (flags
& FIND_PREV
) {
360 /* need to read old or new frames? */
361 if (This
->lLastKey
<= pos
|| pos
< This
->lCurrent
)
362 IAVIStream_Read(iface
, pos
, 1, NULL
, 0, NULL
, NULL
);
364 return This
->lLastKey
;
366 } else if (flags
& FIND_ANY
) {
367 return pos
; /* We really don't know, reread is to expensive, so guess. */
368 } else if (flags
& FIND_FORMAT
) {
369 if (flags
& FIND_PREV
)
376 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
377 LPVOID format
, LONG
*formatsize
)
379 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
381 LPBITMAPINFOHEADER lpbi
;
384 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
386 if (formatsize
== NULL
)
387 return AVIERR_BADPARAM
;
389 if (This
->pg
== NULL
) {
390 hr
= AVIFILE_OpenGetFrame(This
);
396 lpbi
= AVIStreamGetFrame(This
->pg
, pos
);
398 return AVIERR_MEMORY
;
400 if (This
->hic
== NULL
) {
401 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
404 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
405 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
407 This
->cbOutput
= size
;
408 if (format
!= NULL
) {
409 if (This
->lpbiOutput
!= NULL
)
410 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
412 memcpy(format
, lpbi
, min(*formatsize
, size
));
415 } else if (format
!= NULL
)
416 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
418 if (*formatsize
< This
->cbOutput
)
419 hr
= AVIERR_BUFFERTOOSMALL
;
423 *formatsize
= This
->cbOutput
;
427 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
428 LPVOID format
, LONG formatsize
)
430 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
432 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
434 /* check parameters */
435 if (format
== NULL
|| formatsize
<= 0)
436 return AVIERR_BADPARAM
;
438 /* We can only accept RGB data for writing */
439 if (((LPBITMAPINFOHEADER
)format
)->biCompression
!= BI_RGB
) {
440 WARN(": need RGB data as input\n");
441 return AVIERR_UNSUPPORTED
;
444 /* Input format already known?
445 * Changing of palette is supported, but be quiet if it's the same */
446 if (This
->lpbiInput
!= NULL
) {
447 if (This
->cbInput
!= formatsize
)
448 return AVIERR_UNSUPPORTED
;
450 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
454 /* Does the nested stream support writing? */
455 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
456 return AVIERR_READONLY
;
458 /* check if frame is already written */
459 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
460 return AVIERR_UNSUPPORTED
;
462 /* check if we should compress */
463 if (This
->sInfo
.fccHandler
== 0 ||
464 This
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E'))
465 This
->sInfo
.fccHandler
= comptypeDIB
;
467 /* only pass through? */
468 if (This
->sInfo
.fccHandler
== comptypeDIB
)
469 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
471 /* initial format setting? */
472 if (This
->lpbiInput
== NULL
) {
475 assert(This
->hic
!= NULL
);
477 /* get memory for input format */
478 This
->lpbiInput
= HeapAlloc(GetProcessHeap(), 0, formatsize
);
479 if (This
->lpbiInput
== NULL
)
480 return AVIERR_MEMORY
;
481 This
->cbInput
= formatsize
;
482 memcpy(This
->lpbiInput
, format
, formatsize
);
484 /* get output format */
485 size
= ICCompressGetFormatSize(This
->hic
, This
->lpbiInput
);
486 if (size
< sizeof(BITMAPINFOHEADER
))
487 return AVIERR_COMPRESSOR
;
488 This
->lpbiOutput
= HeapAlloc(GetProcessHeap(), 0, size
);
489 if (This
->lpbiOutput
== NULL
)
490 return AVIERR_MEMORY
;
491 This
->cbOutput
= size
;
492 if (ICCompressGetFormat(This
->hic
,This
->lpbiInput
,This
->lpbiOutput
) < S_OK
)
493 return AVIERR_COMPRESSOR
;
495 /* update AVISTREAMINFO structure */
496 This
->sInfo
.rcFrame
.right
=
497 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
498 This
->sInfo
.rcFrame
.bottom
=
499 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
501 /* prepare codec for compression */
502 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
503 return AVIERR_COMPRESSOR
;
505 /* allocate memory for compressed frame */
506 size
= ICCompressGetSize(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
);
507 This
->lpbiCur
= HeapAlloc(GetProcessHeap(), 0, 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
= HeapAlloc(GetProcessHeap(), 0, 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
;
530 This
->lpbiPrev
= HeapReAlloc(GetProcessHeap(), 0, This
->lpbiPrev
, size
);
531 if (This
->lpbiPrev
== NULL
)
532 return AVIERR_MEMORY
;
533 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
535 /* prepare codec also for decompression */
536 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
537 return AVIERR_COMPRESSOR
;
540 /* format change -- check that's only the palette */
541 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)format
;
543 if (lpbi
->biSize
!= This
->lpbiInput
->biSize
||
544 lpbi
->biWidth
!= This
->lpbiInput
->biWidth
||
545 lpbi
->biHeight
!= This
->lpbiInput
->biHeight
||
546 lpbi
->biBitCount
!= This
->lpbiInput
->biBitCount
||
547 lpbi
->biPlanes
!= This
->lpbiInput
->biPlanes
||
548 lpbi
->biCompression
!= This
->lpbiInput
->biCompression
||
549 lpbi
->biClrUsed
!= This
->lpbiInput
->biClrUsed
)
550 return AVIERR_UNSUPPORTED
;
552 /* get new output format */
553 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
554 return AVIERR_BADFORMAT
;
556 /* restart compression */
557 ICCompressEnd(This
->hic
);
558 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
559 return AVIERR_COMPRESSOR
;
561 /* check if we need to restart decompression also */
562 if (This
->lKeyFrameEvery
!= 1 &&
563 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
564 ICDecompressEnd(This
->hic
);
565 if (ICDecompressGetFormat(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) < S_OK
)
566 return AVIERR_COMPRESSOR
;
567 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
568 return AVIERR_COMPRESSOR
;
572 /* tell nested stream the new format */
573 return IAVIStream_SetFormat(This
->pStream
, pos
,
574 This
->lpbiOutput
, This
->cbOutput
);
577 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
578 LONG samples
, LPVOID buffer
,
579 LONG buffersize
, LPLONG bytesread
,
582 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
584 LPBITMAPINFOHEADER lpbi
;
586 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
587 buffersize
, bytesread
, samplesread
);
589 /* clear return parameters if given */
590 if (bytesread
!= NULL
)
592 if (samplesread
!= NULL
)
598 /* check parameters */
599 if (samples
!= 1 && (bytesread
== NULL
&& samplesread
== NULL
))
600 return AVIERR_BADPARAM
;
601 if (samples
== -1) /* read as much as we could */
604 if (This
->pg
== NULL
) {
605 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
611 /* compress or decompress? */
612 if (This
->hic
== NULL
) {
614 lpbi
= AVIStreamGetFrame(This
->pg
, start
);
616 return AVIERR_MEMORY
;
618 if (buffer
!= NULL
&& buffersize
> 0) {
619 /* check buffersize */
620 if (buffersize
< lpbi
->biSizeImage
)
621 return AVIERR_BUFFERTOOSMALL
;
623 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
626 /* fill out return parameters if given */
627 if (bytesread
!= NULL
)
628 *bytesread
= lpbi
->biSizeImage
;
631 if (This
->lCurrent
> start
)
634 while (start
> This
->lCurrent
) {
637 lpbi
= AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
640 return AVIERR_MEMORY
;
643 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
650 if (buffer
!= NULL
&& buffersize
> 0) {
651 /* check buffersize */
652 if (This
->lpbiCur
->biSizeImage
> buffersize
)
653 return AVIERR_BUFFERTOOSMALL
;
655 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
658 /* fill out return parameters if given */
659 if (bytesread
!= NULL
)
660 *bytesread
= This
->lpbiCur
->biSizeImage
;
663 /* fill out return parameters if given */
664 if (samplesread
!= NULL
)
670 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
671 LONG samples
, LPVOID buffer
,
672 LONG buffersize
, DWORD flags
,
676 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
680 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
681 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
683 /* clear return parameters if given */
684 if (sampwritten
!= NULL
)
686 if (byteswritten
!= NULL
)
689 /* check parameters */
690 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
691 return AVIERR_BADPARAM
;
693 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
694 /* only pass through */
695 flags
|= AVIIF_KEYFRAME
;
697 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
698 flags
, sampwritten
, byteswritten
);
700 /* compress data before writing to pStream */
701 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
702 return AVIERR_UNSUPPORTED
;
704 This
->lCurrent
= start
;
705 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
709 if (This
->lLastKey
== start
)
710 flags
|= AVIIF_KEYFRAME
;
712 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
713 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
718 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
721 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
723 TRACE("(%p,%d,%d)\n", iface
, start
, samples
);
725 return IAVIStream_Delete(This
->pStream
, start
, samples
);
728 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
729 LPVOID lp
, LPLONG lpread
)
731 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
733 TRACE("(%p,0x%08X,%p,%p)\n", iface
, fcc
, lp
, lpread
);
735 assert(This
->pStream
!= NULL
);
737 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
740 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
741 LPVOID lp
, LONG size
)
743 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
745 TRACE("(%p,0x%08x,%p,%d)\n", iface
, fcc
, lp
, size
);
747 assert(This
->pStream
!= NULL
);
749 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
752 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
753 LPAVISTREAMINFOW info
, LONG infolen
)
755 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
760 /***********************************************************************/
762 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
763 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
765 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
769 BOOL bDecreasedQual
= FALSE
;
773 /* make lKeyFrameEvery and at start a keyframe */
774 if ((This
->lKeyFrameEvery
!= 0 &&
775 (This
->lCurrent
- This
->lLastKey
) >= This
->lKeyFrameEvery
) ||
776 This
->lCurrent
== This
->sInfo
.dwStart
) {
777 idxFlags
= AVIIF_KEYFRAME
;
778 icmFlags
= ICCOMPRESS_KEYFRAME
;
781 if (This
->lKeyFrameEvery
!= 0) {
782 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
783 if (idxFlags
& AVIIF_KEYFRAME
) {
784 /* for keyframes allow to consume all unused bytes */
785 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
786 This
->dwUnusedBytes
= 0;
788 /* for non-keyframes only allow something of the unused bytes to be consumed */
792 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
793 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
794 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
796 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
797 This
->dwUnusedBytes
-= tmp2
;
800 dwRequest
= MAX_FRAMESIZE
;
802 /* only one keyframe at start desired */
803 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
804 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
805 This
->dwUnusedBytes
= 0;
807 dwRequest
= MAX_FRAMESIZE
;
810 /* must we check for framesize to gain requested
811 * datarate or could we trust codec? */
812 doSizeCheck
= (dwRequest
!= 0 && ((This
->dwICMFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0));
814 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
815 dwMinQual
= ICQUALITY_LOW
;
818 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
819 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
826 res
= ICCompress(This
->hic
,icmFlags
,This
->lpbiCur
,This
->lpCur
,lpbi
,lpBits
,
827 &idxCkid
, &idxFlags
, This
->lCurrent
, dwRequest
, dwCurQual
,
828 noPrev
? NULL
:This
->lpbiPrev
, noPrev
? NULL
:This
->lpPrev
);
829 if (res
== ICERR_NEWPALETTE
) {
830 FIXME(": codec has changed palette -- unhandled!\n");
831 } else if (res
!= ICERR_OK
)
832 return AVIERR_COMPRESSOR
;
834 /* need to check for framesize */
838 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
839 /* frame is smaller -- try to maximize quality */
840 if (dwMaxQual
- dwCurQual
> 10) {
841 DWORD tmp
= dwRequest
/ 8;
843 if (tmp
< MAX_FRAMESIZE_DIFF
)
844 tmp
= MAX_FRAMESIZE_DIFF
;
846 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
848 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
854 } else if (dwMaxQual
- dwMinQual
<= 1) {
857 dwMaxQual
= dwCurQual
;
859 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
860 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
862 FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n",
863 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
865 bDecreasedQual
= TRUE
;
869 /* remember some values */
870 This
->dwLastQuality
= dwCurQual
;
871 This
->dwUnusedBytes
= dwRequest
- This
->lpbiCur
->biSizeImage
;
872 if (icmFlags
& ICCOMPRESS_KEYFRAME
)
873 This
->lLastKey
= This
->lCurrent
;
875 /* Does we manage previous frame? */
876 if (This
->lpPrev
!= NULL
&& This
->lKeyFrameEvery
!= 1)
877 ICDecompress(This
->hic
, 0, This
->lpbiCur
, This
->lpCur
,
878 This
->lpbiPrev
, This
->lpPrev
);
883 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
885 LPBITMAPINFOHEADER lpbi
;
889 assert(This
!= NULL
);
890 assert(This
->pStream
!= NULL
);
891 assert(This
->pg
== NULL
);
893 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
894 if (This
->pg
== NULL
)
897 /* When we only decompress this is enough */
898 if (This
->sInfo
.fccHandler
== comptypeDIB
)
901 assert(This
->hic
!= NULL
);
902 assert(This
->lpbiOutput
== NULL
);
904 /* get input format */
905 lpbi
= AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
907 return AVIERR_MEMORY
;
909 /* get memory for output format */
910 size
= ICCompressGetFormatSize(This
->hic
, lpbi
);
911 if ((LONG
)size
< (LONG
)sizeof(BITMAPINFOHEADER
))
912 return AVIERR_COMPRESSOR
;
913 This
->lpbiOutput
= HeapAlloc(GetProcessHeap(), 0, size
);
914 if (This
->lpbiOutput
== NULL
)
915 return AVIERR_MEMORY
;
916 This
->cbOutput
= size
;
918 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
919 return AVIERR_BADFORMAT
;
921 /* update AVISTREAMINFO structure */
922 This
->sInfo
.rcFrame
.right
=
923 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
924 This
->sInfo
.rcFrame
.bottom
=
925 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
926 This
->sInfo
.dwSuggestedBufferSize
=
927 ICCompressGetSize(This
->hic
, lpbi
, This
->lpbiOutput
);
929 /* prepare codec for compression */
930 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
931 return AVIERR_COMPRESSOR
;
933 /* allocate memory for current frame */
934 size
+= This
->sInfo
.dwSuggestedBufferSize
;
935 This
->lpbiCur
= HeapAlloc(GetProcessHeap(), 0, size
);
936 if (This
->lpbiCur
== NULL
)
937 return AVIERR_MEMORY
;
938 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
939 This
->lpCur
= DIBPTR(This
->lpbiCur
);
941 /* allocate memory for last frame if needed */
942 if (This
->lKeyFrameEvery
!= 1 &&
943 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
944 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
945 This
->lpbiPrev
= HeapAlloc(GetProcessHeap(), 0, size
);
946 if (This
->lpbiPrev
== NULL
)
947 return AVIERR_MEMORY
;
948 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
949 return AVIERR_COMPRESSOR
;
951 if (This
->lpbiPrev
->biSizeImage
== 0) {
952 This
->lpbiPrev
->biSizeImage
=
953 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
956 /* get memory for format and picture */
957 size
+= This
->lpbiPrev
->biSizeImage
;
958 This
->lpbiPrev
= HeapReAlloc(GetProcessHeap(), 0, This
->lpbiPrev
, size
);
959 if (This
->lpbiPrev
== NULL
)
960 return AVIERR_MEMORY
;
961 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
963 /* prepare codec also for decompression */
964 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
965 return AVIERR_COMPRESSOR
;