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 #define COM_NO_WINDOWS_H
33 #include "avifile_private.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
39 #define MAX_FRAMESIZE (16 * 1024 * 1024)
40 #define MAX_FRAMESIZE_DIFF 512
42 /***********************************************************************/
44 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,REFIID refiid
,LPVOID
*obj
);
45 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
);
46 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
);
47 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
,LPARAM lParam1
,LPARAM lParam2
);
48 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,AVISTREAMINFOW
*psi
,LONG size
);
49 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
,LONG pos
,LONG flags
);
50 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG
*formatsize
);
51 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG formatsize
);
52 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,LONG
*bytesread
,LONG
*samplesread
);
53 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,DWORD flags
,LONG
*sampwritten
,LONG
*byteswritten
);
54 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
,LONG start
,LONG samples
);
55 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG
*lpread
);
56 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG size
);
57 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,AVISTREAMINFOW
*info
,LONG infolen
);
59 static const struct IAVIStreamVtbl iicmst
= {
60 ICMStream_fnQueryInterface
,
65 ICMStream_fnFindSample
,
66 ICMStream_fnReadFormat
,
67 ICMStream_fnSetFormat
,
72 ICMStream_fnWriteData
,
76 typedef struct _IAVIStreamImpl
{
78 const IAVIStreamVtbl
*lpVtbl
;
81 /* IAVIStream stuff */
93 DWORD dwBytesPerFrame
;
96 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
98 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
101 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
103 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
107 /***********************************************************************/
109 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
110 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
111 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
113 static inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
117 This
->dwLastQuality
= ICQUALITY_HIGH
;
118 This
->dwUnusedBytes
= 0;
121 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
123 IAVIStreamImpl
*pstream
;
126 assert(riid
!= NULL
&& ppv
!= NULL
);
130 pstream
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIStreamImpl
));
132 return AVIERR_MEMORY
;
134 pstream
->lpVtbl
= &iicmst
;
135 AVIFILE_Reset(pstream
);
137 hr
= IAVIStream_QueryInterface((IAVIStream
*)pstream
, riid
, ppv
);
139 HeapFree(GetProcessHeap(), 0, pstream
);
144 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
145 REFIID refiid
, LPVOID
*obj
)
147 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
149 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
151 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
152 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
154 IAVIStream_AddRef(iface
);
159 return OLE_E_ENUM_NOMORE
;
162 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
164 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
165 ULONG ref
= InterlockedIncrement(&This
->ref
);
167 TRACE("(%p) -> %d\n", iface
, ref
);
169 /* also add reference to the nested stream */
170 if (This
->pStream
!= NULL
)
171 IAVIStream_AddRef(This
->pStream
);
176 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
178 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
179 ULONG ref
= InterlockedDecrement(&This
->ref
);
181 TRACE("(%p) -> %d\n", iface
, ref
);
185 if (This
->pg
!= NULL
) {
186 AVIStreamGetFrameClose(This
->pg
);
189 if (This
->pStream
!= NULL
) {
190 IAVIStream_Release(This
->pStream
);
191 This
->pStream
= NULL
;
193 if (This
->hic
!= NULL
) {
194 if (This
->lpbiPrev
!= NULL
) {
195 ICDecompressEnd(This
->hic
);
196 HeapFree(GetProcessHeap(), 0, This
->lpbiPrev
);
197 This
->lpbiPrev
= NULL
;
200 ICCompressEnd(This
->hic
);
203 if (This
->lpbiCur
!= NULL
) {
204 HeapFree(GetProcessHeap(), 0, This
->lpbiCur
);
205 This
->lpbiCur
= NULL
;
208 if (This
->lpbiOutput
!= NULL
) {
209 HeapFree(GetProcessHeap(), 0, This
->lpbiOutput
);
210 This
->lpbiOutput
= NULL
;
213 if (This
->lpbiInput
!= NULL
) {
214 HeapFree(GetProcessHeap(), 0, This
->lpbiInput
);
215 This
->lpbiInput
= NULL
;
219 HeapFree(GetProcessHeap(), 0, This
);
224 /* also release reference to the nested stream */
225 if (This
->pStream
!= NULL
)
226 IAVIStream_Release(This
->pStream
);
231 /* lParam1: PAVISTREAM
232 * lParam2: LPAVICOMPRESSOPTIONS
234 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
237 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
240 ICCOMPRESSFRAMES icFrames
;
241 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
243 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
245 /* check parameter */
246 if ((LPVOID
)lParam1
== NULL
)
247 return AVIERR_BADPARAM
;
249 /* get infos from stream */
250 IAVIStream_Info((PAVISTREAM
)lParam1
, &This
->sInfo
, sizeof(This
->sInfo
));
251 if (This
->sInfo
.fccType
!= streamtypeVIDEO
)
252 return AVIERR_ERROR
; /* error in registry or AVIMakeCompressedStream */
254 /* add reference to the stream */
255 This
->pStream
= (PAVISTREAM
)lParam1
;
256 IAVIStream_AddRef(This
->pStream
);
260 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
261 /* we should compress */
262 This
->sInfo
.fccHandler
= pco
->fccHandler
;
264 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
265 if (This
->hic
== NULL
)
266 return AVIERR_NOCOMPRESSOR
;
268 /* restore saved state of codec */
269 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
270 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
273 /* set quality -- resolve default quality */
274 This
->sInfo
.dwQuality
= pco
->dwQuality
;
275 if (pco
->dwQuality
== ICQUALITY_DEFAULT
)
276 This
->sInfo
.dwQuality
= ICGetDefaultQuality(This
->hic
);
278 /* get capabilities of codec */
279 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
280 This
->dwICMFlags
= icinfo
.dwFlags
;
283 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
284 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
285 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
287 This
->lKeyFrameEvery
= 1;
290 if ((pco
->dwFlags
& AVICOMPRESSF_DATARATE
)) {
291 /* Do we have a chance to reduce size to desired one? */
292 if ((icinfo
.dwFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0)
293 return AVIERR_NOCOMPRESSOR
;
295 assert(This
->sInfo
.dwRate
!= 0);
297 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
298 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
300 pco
->dwBytesPerSecond
= 0;
301 This
->dwBytesPerFrame
= 0;
304 if (icinfo
.dwFlags
& VIDCF_COMPRESSFRAMES
) {
305 memset(&icFrames
, 0, sizeof(icFrames
));
306 icFrames
.lpbiOutput
= This
->lpbiOutput
;
307 icFrames
.lpbiInput
= This
->lpbiInput
;
308 icFrames
.lFrameCount
= This
->sInfo
.dwLength
;
309 icFrames
.lQuality
= This
->sInfo
.dwQuality
;
310 icFrames
.lDataRate
= pco
->dwBytesPerSecond
;
311 icFrames
.lKeyRate
= This
->lKeyFrameEvery
;
312 icFrames
.dwRate
= This
->sInfo
.dwRate
;
313 icFrames
.dwScale
= This
->sInfo
.dwScale
;
314 ICSendMessage(This
->hic
, ICM_COMPRESS_FRAMES_INFO
,
315 (LPARAM
)&icFrames
, (LPARAM
)sizeof(icFrames
));
318 This
->sInfo
.fccHandler
= comptypeDIB
;
323 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
326 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
328 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
331 return AVIERR_BADPARAM
;
333 return AVIERR_BADSIZE
;
335 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
337 if ((DWORD
)size
< sizeof(This
->sInfo
))
338 return AVIERR_BUFFERTOOSMALL
;
342 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
345 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
347 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
349 if (flags
& FIND_FROM_START
) {
350 pos
= This
->sInfo
.dwStart
;
351 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
355 if (flags
& FIND_RET
)
356 WARN(": FIND_RET flags will be ignored!\n");
358 if (flags
& FIND_KEY
) {
359 if (This
->hic
== NULL
)
360 return pos
; /* we decompress so every frame is a keyframe */
362 if (flags
& FIND_PREV
) {
363 /* need to read old or new frames? */
364 if (This
->lLastKey
<= pos
|| pos
< This
->lCurrent
)
365 IAVIStream_Read(iface
, pos
, 1, NULL
, 0, NULL
, NULL
);
367 return This
->lLastKey
;
369 } else if (flags
& FIND_ANY
) {
370 return pos
; /* We really don't know, reread is to expensive, so guess. */
371 } else if (flags
& FIND_FORMAT
) {
372 if (flags
& FIND_PREV
)
379 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
380 LPVOID format
, LONG
*formatsize
)
382 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
384 LPBITMAPINFOHEADER lpbi
;
387 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
389 if (formatsize
== NULL
)
390 return AVIERR_BADPARAM
;
392 if (This
->pg
== NULL
) {
393 hr
= AVIFILE_OpenGetFrame(This
);
399 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, pos
);
401 return AVIERR_MEMORY
;
403 if (This
->hic
== NULL
) {
404 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
407 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
408 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
410 This
->cbOutput
= size
;
411 if (format
!= NULL
) {
412 if (This
->lpbiOutput
!= NULL
)
413 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
415 memcpy(format
, lpbi
, min(*formatsize
, size
));
418 } else if (format
!= NULL
)
419 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
421 if (*formatsize
< This
->cbOutput
)
422 hr
= AVIERR_BUFFERTOOSMALL
;
426 *formatsize
= This
->cbOutput
;
430 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
431 LPVOID format
, LONG formatsize
)
433 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
435 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
437 /* check parameters */
438 if (format
== NULL
|| formatsize
<= 0)
439 return AVIERR_BADPARAM
;
441 /* We can only accept RGB data for writing */
442 if (((LPBITMAPINFOHEADER
)format
)->biCompression
!= BI_RGB
) {
443 WARN(": need RGB data as input\n");
444 return AVIERR_UNSUPPORTED
;
447 /* Input format already known?
448 * Changing of palette is supported, but be quiet if it's the same */
449 if (This
->lpbiInput
!= NULL
) {
450 if (This
->cbInput
!= formatsize
)
451 return AVIERR_UNSUPPORTED
;
453 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
457 /* Does the nested stream support writing? */
458 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
459 return AVIERR_READONLY
;
461 /* check if frame is already written */
462 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
463 return AVIERR_UNSUPPORTED
;
465 /* check if we should compress */
466 if (This
->sInfo
.fccHandler
== 0 ||
467 This
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E'))
468 This
->sInfo
.fccHandler
= comptypeDIB
;
470 /* only pass through? */
471 if (This
->sInfo
.fccHandler
== comptypeDIB
)
472 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
474 /* initial format setting? */
475 if (This
->lpbiInput
== NULL
) {
478 assert(This
->hic
!= NULL
);
480 /* get memory for input format */
481 This
->lpbiInput
= HeapAlloc(GetProcessHeap(), 0, formatsize
);
482 if (This
->lpbiInput
== NULL
)
483 return AVIERR_MEMORY
;
484 This
->cbInput
= formatsize
;
485 memcpy(This
->lpbiInput
, format
, formatsize
);
487 /* get output format */
488 size
= ICCompressGetFormatSize(This
->hic
, This
->lpbiInput
);
489 if (size
< sizeof(BITMAPINFOHEADER
))
490 return AVIERR_COMPRESSOR
;
491 This
->lpbiOutput
= HeapAlloc(GetProcessHeap(), 0, size
);
492 if (This
->lpbiOutput
== NULL
)
493 return AVIERR_MEMORY
;
494 This
->cbOutput
= size
;
495 if (ICCompressGetFormat(This
->hic
,This
->lpbiInput
,This
->lpbiOutput
) < S_OK
)
496 return AVIERR_COMPRESSOR
;
498 /* update AVISTREAMINFO structure */
499 This
->sInfo
.rcFrame
.right
=
500 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
501 This
->sInfo
.rcFrame
.bottom
=
502 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
504 /* prepare codec for compression */
505 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
506 return AVIERR_COMPRESSOR
;
508 /* allocate memory for compressed frame */
509 size
= ICCompressGetSize(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
);
510 This
->lpbiCur
= HeapAlloc(GetProcessHeap(), 0, This
->cbOutput
+ size
);
511 if (This
->lpbiCur
== NULL
)
512 return AVIERR_MEMORY
;
513 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
514 This
->lpCur
= DIBPTR(This
->lpbiCur
);
516 /* allocate memory for last frame if needed */
517 if (This
->lKeyFrameEvery
!= 1 &&
518 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
519 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
520 This
->lpbiPrev
= HeapAlloc(GetProcessHeap(), 0, size
);
521 if (This
->lpbiPrev
== NULL
)
522 return AVIERR_MEMORY
;
523 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
524 return AVIERR_COMPRESSOR
;
526 if (This
->lpbiPrev
->biSizeImage
== 0) {
527 This
->lpbiPrev
->biSizeImage
=
528 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
531 /* get memory for format and picture */
532 size
+= This
->lpbiPrev
->biSizeImage
;
533 This
->lpbiPrev
= HeapReAlloc(GetProcessHeap(), 0, This
->lpbiPrev
, size
);
534 if (This
->lpbiPrev
== NULL
)
535 return AVIERR_MEMORY
;
536 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
538 /* prepare codec also for decompression */
539 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
540 return AVIERR_COMPRESSOR
;
543 /* format change -- check that's only the palette */
544 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)format
;
546 if (lpbi
->biSize
!= This
->lpbiInput
->biSize
||
547 lpbi
->biWidth
!= This
->lpbiInput
->biWidth
||
548 lpbi
->biHeight
!= This
->lpbiInput
->biHeight
||
549 lpbi
->biBitCount
!= This
->lpbiInput
->biBitCount
||
550 lpbi
->biPlanes
!= This
->lpbiInput
->biPlanes
||
551 lpbi
->biCompression
!= This
->lpbiInput
->biCompression
||
552 lpbi
->biClrUsed
!= This
->lpbiInput
->biClrUsed
)
553 return AVIERR_UNSUPPORTED
;
555 /* get new output format */
556 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
557 return AVIERR_BADFORMAT
;
559 /* restart compression */
560 ICCompressEnd(This
->hic
);
561 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
562 return AVIERR_COMPRESSOR
;
564 /* check if we need to restart decompresion also */
565 if (This
->lKeyFrameEvery
!= 1 &&
566 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
567 ICDecompressEnd(This
->hic
);
568 if (ICDecompressGetFormat(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) < S_OK
)
569 return AVIERR_COMPRESSOR
;
570 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
571 return AVIERR_COMPRESSOR
;
575 /* tell nested stream the new format */
576 return IAVIStream_SetFormat(This
->pStream
, pos
,
577 This
->lpbiOutput
, This
->cbOutput
);
580 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
581 LONG samples
, LPVOID buffer
,
582 LONG buffersize
, LPLONG bytesread
,
585 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
587 LPBITMAPINFOHEADER lpbi
;
589 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
590 buffersize
, bytesread
, samplesread
);
592 /* clear return parameters if given */
593 if (bytesread
!= NULL
)
595 if (samplesread
!= NULL
)
601 /* check parameters */
602 if (samples
!= 1 && (bytesread
== NULL
&& samplesread
== NULL
))
603 return AVIERR_BADPARAM
;
604 if (samples
== -1) /* read as much as we could */
607 if (This
->pg
== NULL
) {
608 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
614 /* compress or decompress? */
615 if (This
->hic
== NULL
) {
617 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, start
);
619 return AVIERR_MEMORY
;
621 if (buffer
!= NULL
&& buffersize
> 0) {
622 /* check buffersize */
623 if (buffersize
< lpbi
->biSizeImage
)
624 return AVIERR_BUFFERTOOSMALL
;
626 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
629 /* fill out return parameters if given */
630 if (bytesread
!= NULL
)
631 *bytesread
= lpbi
->biSizeImage
;
634 if (This
->lCurrent
> start
)
637 while (start
> This
->lCurrent
) {
640 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
643 return AVIERR_MEMORY
;
646 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
653 if (buffer
!= NULL
&& buffersize
> 0) {
654 /* check buffersize */
655 if (This
->lpbiCur
->biSizeImage
> buffersize
)
656 return AVIERR_BUFFERTOOSMALL
;
658 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
661 /* fill out return parameters if given */
662 if (bytesread
!= NULL
)
663 *bytesread
= This
->lpbiCur
->biSizeImage
;
666 /* fill out return parameters if given */
667 if (samplesread
!= NULL
)
673 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
674 LONG samples
, LPVOID buffer
,
675 LONG buffersize
, DWORD flags
,
679 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
683 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
684 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
686 /* clear return parameters if given */
687 if (sampwritten
!= NULL
)
689 if (byteswritten
!= NULL
)
692 /* check parameters */
693 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
694 return AVIERR_BADPARAM
;
696 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
697 /* only pass through */
698 flags
|= AVIIF_KEYFRAME
;
700 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
701 flags
, sampwritten
, byteswritten
);
703 /* compress data before writing to pStream */
704 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
705 return AVIERR_UNSUPPORTED
;
707 This
->lCurrent
= start
;
708 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
712 if (This
->lLastKey
== start
)
713 flags
|= AVIIF_KEYFRAME
;
715 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
716 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
721 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
724 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
726 TRACE("(%p,%d,%d)\n", iface
, start
, samples
);
728 return IAVIStream_Delete(This
->pStream
, start
, samples
);
731 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
732 LPVOID lp
, LPLONG lpread
)
734 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
736 TRACE("(%p,0x%08X,%p,%p)\n", iface
, fcc
, lp
, lpread
);
738 assert(This
->pStream
!= NULL
);
740 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
743 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
744 LPVOID lp
, LONG size
)
746 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
748 TRACE("(%p,0x%08x,%p,%d)\n", iface
, fcc
, lp
, size
);
750 assert(This
->pStream
!= NULL
);
752 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
755 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
756 LPAVISTREAMINFOW info
, LONG infolen
)
758 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
763 /***********************************************************************/
765 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
766 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
768 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
772 BOOL bDecreasedQual
= FALSE
;
776 /* make lKeyFrameEvery and at start a keyframe */
777 if ((This
->lKeyFrameEvery
!= 0 &&
778 (This
->lCurrent
- This
->lLastKey
) >= This
->lKeyFrameEvery
) ||
779 This
->lCurrent
== This
->sInfo
.dwStart
) {
780 idxFlags
= AVIIF_KEYFRAME
;
781 icmFlags
= ICCOMPRESS_KEYFRAME
;
784 if (This
->lKeyFrameEvery
!= 0) {
785 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
786 if (idxFlags
& AVIIF_KEYFRAME
) {
787 /* for keyframes allow to consume all unused bytes */
788 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
789 This
->dwUnusedBytes
= 0;
791 /* for non-keyframes only allow something of the unused bytes to be consumed */
795 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
796 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
797 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
799 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
800 This
->dwUnusedBytes
-= tmp2
;
803 dwRequest
= MAX_FRAMESIZE
;
805 /* only one keyframe at start desired */
806 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
807 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
808 This
->dwUnusedBytes
= 0;
810 dwRequest
= MAX_FRAMESIZE
;
813 /* must we check for framesize to gain requested
814 * datarate or could we trust codec? */
815 doSizeCheck
= (dwRequest
!= 0 && ((This
->dwICMFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0));
817 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
818 dwMinQual
= ICQUALITY_LOW
;
821 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
822 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
829 hr
= ICCompress(This
->hic
,icmFlags
,This
->lpbiCur
,This
->lpCur
,lpbi
,lpBits
,
830 &idxCkid
, &idxFlags
, This
->lCurrent
, dwRequest
, dwCurQual
,
831 noPrev
? NULL
:This
->lpbiPrev
, noPrev
? NULL
:This
->lpPrev
);
832 if (hr
== ICERR_NEWPALETTE
) {
833 FIXME(": codec has changed palette -- unhandled!\n");
834 } else if (hr
!= ICERR_OK
)
835 return AVIERR_COMPRESSOR
;
837 /* need to check for framesize */
841 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
842 /* frame is smaller -- try to maximize quality */
843 if (dwMaxQual
- dwCurQual
> 10) {
844 DWORD tmp
= dwRequest
/ 8;
846 if (tmp
< MAX_FRAMESIZE_DIFF
)
847 tmp
= MAX_FRAMESIZE_DIFF
;
849 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
851 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
857 } else if (dwMaxQual
- dwMinQual
<= 1) {
860 dwMaxQual
= dwCurQual
;
862 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
863 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
865 FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n",
866 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
868 bDecreasedQual
= TRUE
;
872 /* remember some values */
873 This
->dwLastQuality
= dwCurQual
;
874 This
->dwUnusedBytes
= dwRequest
- This
->lpbiCur
->biSizeImage
;
875 if (icmFlags
& ICCOMPRESS_KEYFRAME
)
876 This
->lLastKey
= This
->lCurrent
;
878 /* Does we manage previous frame? */
879 if (This
->lpPrev
!= NULL
&& This
->lKeyFrameEvery
!= 1)
880 ICDecompress(This
->hic
, 0, This
->lpbiCur
, This
->lpCur
,
881 This
->lpbiPrev
, This
->lpPrev
);
886 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
888 LPBITMAPINFOHEADER lpbi
;
892 assert(This
!= NULL
);
893 assert(This
->pStream
!= NULL
);
894 assert(This
->pg
== NULL
);
896 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
897 if (This
->pg
== NULL
)
900 /* When we only decompress this is enough */
901 if (This
->sInfo
.fccHandler
== comptypeDIB
)
904 assert(This
->hic
!= NULL
);
905 assert(This
->lpbiOutput
== NULL
);
907 /* get input format */
908 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
910 return AVIERR_MEMORY
;
912 /* get memory for output format */
913 size
= ICCompressGetFormatSize(This
->hic
, lpbi
);
914 if ((LONG
)size
< (LONG
)sizeof(BITMAPINFOHEADER
))
915 return AVIERR_COMPRESSOR
;
916 This
->lpbiOutput
= HeapAlloc(GetProcessHeap(), 0, size
);
917 if (This
->lpbiOutput
== NULL
)
918 return AVIERR_MEMORY
;
919 This
->cbOutput
= size
;
921 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
922 return AVIERR_BADFORMAT
;
924 /* update AVISTREAMINFO structure */
925 This
->sInfo
.rcFrame
.right
=
926 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
927 This
->sInfo
.rcFrame
.bottom
=
928 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
929 This
->sInfo
.dwSuggestedBufferSize
=
930 ICCompressGetSize(This
->hic
, lpbi
, This
->lpbiOutput
);
932 /* prepare codec for compression */
933 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
934 return AVIERR_COMPRESSOR
;
936 /* allocate memory for current frame */
937 size
+= This
->sInfo
.dwSuggestedBufferSize
;
938 This
->lpbiCur
= HeapAlloc(GetProcessHeap(), 0, size
);
939 if (This
->lpbiCur
== NULL
)
940 return AVIERR_MEMORY
;
941 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
942 This
->lpCur
= DIBPTR(This
->lpbiCur
);
944 /* allocate memory for last frame if needed */
945 if (This
->lKeyFrameEvery
!= 1 &&
946 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
947 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
948 This
->lpbiPrev
= HeapAlloc(GetProcessHeap(), 0, size
);
949 if (This
->lpbiPrev
== NULL
)
950 return AVIERR_MEMORY
;
951 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
952 return AVIERR_COMPRESSOR
;
954 if (This
->lpbiPrev
->biSizeImage
== 0) {
955 This
->lpbiPrev
->biSizeImage
=
956 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
959 /* get memory for format and picture */
960 size
+= This
->lpbiPrev
->biSizeImage
;
961 This
->lpbiPrev
= HeapReAlloc(GetProcessHeap(), 0, This
->lpbiPrev
, size
);
962 if (This
->lpbiPrev
== NULL
)
963 return AVIERR_MEMORY
;
964 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
966 /* prepare codec also for decompression */
967 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
968 return AVIERR_COMPRESSOR
;