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
19 #define COM_NO_WINDOWS_H
34 #include "avifile_private.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
40 #define MAX_FRAMESIZE (16 * 1024 * 1024)
41 #define MAX_FRAMESIZE_DIFF 512
43 /***********************************************************************/
45 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,REFIID refiid
,LPVOID
*obj
);
46 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
);
47 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
);
48 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
,LPARAM lParam1
,LPARAM lParam2
);
49 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,AVISTREAMINFOW
*psi
,LONG size
);
50 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
,LONG pos
,LONG flags
);
51 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG
*formatsize
);
52 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG formatsize
);
53 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,LONG
*bytesread
,LONG
*samplesread
);
54 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,DWORD flags
,LONG
*sampwritten
,LONG
*byteswritten
);
55 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
,LONG start
,LONG samples
);
56 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG
*lpread
);
57 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG size
);
58 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,AVISTREAMINFOW
*info
,LONG infolen
);
60 struct IAVIStreamVtbl iicmst
= {
61 ICMStream_fnQueryInterface
,
66 ICMStream_fnFindSample
,
67 ICMStream_fnReadFormat
,
68 ICMStream_fnSetFormat
,
73 ICMStream_fnWriteData
,
77 typedef struct _IAVIStreamImpl
{
79 IAVIStreamVtbl
*lpVtbl
;
82 /* IAVIStream stuff */
94 DWORD dwBytesPerFrame
;
97 LPBITMAPINFOHEADER lpbiCur
; /* current frame */
99 LPBITMAPINFOHEADER lpbiPrev
; /* previous frame */
102 LPBITMAPINFOHEADER lpbiOutput
; /* output format of codec */
104 LPBITMAPINFOHEADER lpbiInput
; /* input format for codec */
108 /***********************************************************************/
110 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
111 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
);
112 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
);
114 static inline void AVIFILE_Reset(IAVIStreamImpl
*This
)
118 This
->dwLastQuality
= ICQUALITY_HIGH
;
119 This
->dwUnusedBytes
= 0;
122 HRESULT
AVIFILE_CreateICMStream(REFIID riid
, LPVOID
*ppv
)
124 IAVIStreamImpl
*pstream
;
127 assert(riid
!= NULL
&& ppv
!= NULL
);
131 pstream
= (IAVIStreamImpl
*)LocalAlloc(LPTR
, sizeof(IAVIStreamImpl
));
133 return AVIERR_MEMORY
;
135 pstream
->lpVtbl
= &iicmst
;
136 AVIFILE_Reset(pstream
);
138 hr
= IAVIStream_QueryInterface((IAVIStream
*)pstream
, riid
, ppv
);
140 LocalFree((HLOCAL
)pstream
);
145 static HRESULT WINAPI
ICMStream_fnQueryInterface(IAVIStream
*iface
,
146 REFIID refiid
, LPVOID
*obj
)
148 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
150 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
152 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
153 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
155 IAVIStream_AddRef(iface
);
160 return OLE_E_ENUM_NOMORE
;
163 static ULONG WINAPI
ICMStream_fnAddRef(IAVIStream
*iface
)
165 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
166 ULONG ref
= InterlockedIncrement(&This
->ref
);
168 TRACE("(%p) -> %ld\n", iface
, ref
);
170 /* also add reference to the nested stream */
171 if (This
->pStream
!= NULL
)
172 IAVIStream_AddRef(This
->pStream
);
177 static ULONG WINAPI
ICMStream_fnRelease(IAVIStream
* iface
)
179 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
180 ULONG ref
= InterlockedDecrement(&This
->ref
);
182 TRACE("(%p) -> %ld\n", iface
, ref
);
186 if (This
->pg
!= NULL
) {
187 AVIStreamGetFrameClose(This
->pg
);
190 if (This
->pStream
!= NULL
) {
191 IAVIStream_Release(This
->pStream
);
192 This
->pStream
= NULL
;
194 if (This
->hic
!= NULL
) {
195 if (This
->lpbiPrev
!= NULL
) {
196 ICDecompressEnd(This
->hic
);
197 GlobalFreePtr(This
->lpbiPrev
);
198 This
->lpbiPrev
= NULL
;
201 ICCompressEnd(This
->hic
);
204 if (This
->lpbiCur
!= NULL
) {
205 GlobalFreePtr(This
->lpbiCur
);
206 This
->lpbiCur
= NULL
;
209 if (This
->lpbiOutput
!= NULL
) {
210 GlobalFreePtr(This
->lpbiOutput
);
211 This
->lpbiOutput
= NULL
;
214 if (This
->lpbiInput
!= NULL
) {
215 GlobalFreePtr(This
->lpbiInput
);
216 This
->lpbiInput
= NULL
;
220 LocalFree((HLOCAL
)This
);
225 /* also release reference to the nested stream */
226 if (This
->pStream
!= NULL
)
227 IAVIStream_Release(This
->pStream
);
232 /* lParam1: PAVISTREAM
233 * lParam2: LPAVICOMPRESSOPTIONS
235 static HRESULT WINAPI
ICMStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
238 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
241 ICCOMPRESSFRAMES icFrames
;
242 LPAVICOMPRESSOPTIONS pco
= (LPAVICOMPRESSOPTIONS
)lParam2
;
244 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
246 /* check parameter */
247 if ((LPVOID
)lParam1
== NULL
)
248 return AVIERR_BADPARAM
;
250 /* get infos from stream */
251 IAVIStream_Info((PAVISTREAM
)lParam1
, &This
->sInfo
, sizeof(This
->sInfo
));
252 if (This
->sInfo
.fccType
!= streamtypeVIDEO
)
253 return AVIERR_ERROR
; /* error in registry or AVIMakeCompressedStream */
255 /* add reference to the stream */
256 This
->pStream
= (PAVISTREAM
)lParam1
;
257 IAVIStream_AddRef(This
->pStream
);
261 if (pco
!= NULL
&& pco
->fccHandler
!= comptypeDIB
) {
262 /* we should compress */
263 This
->sInfo
.fccHandler
= pco
->fccHandler
;
265 This
->hic
= ICOpen(ICTYPE_VIDEO
, pco
->fccHandler
, ICMODE_COMPRESS
);
266 if (This
->hic
== NULL
)
267 return AVIERR_NOCOMPRESSOR
;
269 /* restore saved state of codec */
270 if (pco
->cbParms
> 0 && pco
->lpParms
!= NULL
) {
271 ICSetState(This
->hic
, pco
->lpParms
, pco
->cbParms
);
274 /* set quality -- resolve default quality */
275 This
->sInfo
.dwQuality
= pco
->dwQuality
;
276 if (pco
->dwQuality
== ICQUALITY_DEFAULT
)
277 This
->sInfo
.dwQuality
= ICGetDefaultQuality(This
->hic
);
279 /* get capabilities of codec */
280 ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
281 This
->dwICMFlags
= icinfo
.dwFlags
;
284 if ((pco
->dwFlags
& AVICOMPRESSF_KEYFRAMES
) &&
285 (icinfo
.dwFlags
& (VIDCF_TEMPORAL
|VIDCF_FASTTEMPORALC
))) {
286 This
->lKeyFrameEvery
= pco
->dwKeyFrameEvery
;
288 This
->lKeyFrameEvery
= 1;
291 if ((pco
->dwFlags
& AVICOMPRESSF_DATARATE
)) {
292 /* Do we have a chance to reduce size to desired one? */
293 if ((icinfo
.dwFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0)
294 return AVIERR_NOCOMPRESSOR
;
296 assert(This
->sInfo
.dwRate
!= 0);
298 This
->dwBytesPerFrame
= MulDiv(pco
->dwBytesPerSecond
,
299 This
->sInfo
.dwScale
, This
->sInfo
.dwRate
);
301 pco
->dwBytesPerSecond
= 0;
302 This
->dwBytesPerFrame
= 0;
305 if (icinfo
.dwFlags
& VIDCF_COMPRESSFRAMES
) {
306 memset(&icFrames
, 0, sizeof(icFrames
));
307 icFrames
.lpbiOutput
= This
->lpbiOutput
;
308 icFrames
.lpbiInput
= This
->lpbiInput
;
309 icFrames
.lFrameCount
= This
->sInfo
.dwLength
;
310 icFrames
.lQuality
= This
->sInfo
.dwQuality
;
311 icFrames
.lDataRate
= pco
->dwBytesPerSecond
;
312 icFrames
.lKeyRate
= This
->lKeyFrameEvery
;
313 icFrames
.dwRate
= This
->sInfo
.dwRate
;
314 icFrames
.dwScale
= This
->sInfo
.dwScale
;
315 ICSendMessage(This
->hic
, ICM_COMPRESS_FRAMES_INFO
,
316 (LPARAM
)&icFrames
, (LPARAM
)sizeof(icFrames
));
319 This
->sInfo
.fccHandler
= comptypeDIB
;
324 static HRESULT WINAPI
ICMStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
327 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
329 TRACE("(%p,%p,%ld)\n", iface
, psi
, size
);
332 return AVIERR_BADPARAM
;
334 return AVIERR_BADSIZE
;
336 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
338 if ((DWORD
)size
< sizeof(This
->sInfo
))
339 return AVIERR_BUFFERTOOSMALL
;
343 static LONG WINAPI
ICMStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
346 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
348 TRACE("(%p,%ld,0x%08lX)\n",iface
,pos
,flags
);
350 if (flags
& FIND_FROM_START
) {
351 pos
= This
->sInfo
.dwStart
;
352 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
356 if (flags
& FIND_RET
)
357 WARN(": FIND_RET flags will be ignored!\n");
359 if (flags
& FIND_KEY
) {
360 if (This
->hic
== NULL
)
361 return pos
; /* we decompress so every frame is a keyframe */
363 if (flags
& FIND_PREV
) {
364 /* need to read old or new frames? */
365 if (This
->lLastKey
<= pos
|| pos
< This
->lCurrent
)
366 IAVIStream_Read(iface
, pos
, 1, NULL
, 0, NULL
, NULL
);
368 return This
->lLastKey
;
370 } else if (flags
& FIND_ANY
) {
371 return pos
; /* We really don't know, reread is to expensive, so guess. */
372 } else if (flags
& FIND_FORMAT
) {
373 if (flags
& FIND_PREV
)
380 static HRESULT WINAPI
ICMStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
381 LPVOID format
, LONG
*formatsize
)
383 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
385 LPBITMAPINFOHEADER lpbi
;
388 TRACE("(%p,%ld,%p,%p)\n", iface
, pos
, format
, formatsize
);
390 if (formatsize
== NULL
)
391 return AVIERR_BADPARAM
;
393 if (This
->pg
== NULL
) {
394 hr
= AVIFILE_OpenGetFrame(This
);
400 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, pos
);
402 return AVIERR_MEMORY
;
404 if (This
->hic
== NULL
) {
405 LONG size
= lpbi
->biSize
+ lpbi
->biClrUsed
* sizeof(RGBQUAD
);
408 if (This
->sInfo
.dwSuggestedBufferSize
< lpbi
->biSizeImage
)
409 This
->sInfo
.dwSuggestedBufferSize
= lpbi
->biSizeImage
;
411 This
->cbOutput
= size
;
412 if (format
!= NULL
) {
413 if (This
->lpbiOutput
!= NULL
)
414 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
416 memcpy(format
, lpbi
, min(*formatsize
, size
));
419 } else if (format
!= NULL
)
420 memcpy(format
, This
->lpbiOutput
, min(*formatsize
, This
->cbOutput
));
422 if (*formatsize
< This
->cbOutput
)
423 hr
= AVIERR_BUFFERTOOSMALL
;
427 *formatsize
= This
->cbOutput
;
431 static HRESULT WINAPI
ICMStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
432 LPVOID format
, LONG formatsize
)
434 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
436 TRACE("(%p,%ld,%p,%ld)\n", iface
, pos
, format
, formatsize
);
438 /* check parameters */
439 if (format
== NULL
|| formatsize
<= 0)
440 return AVIERR_BADPARAM
;
442 /* We can only accept RGB data for writing */
443 if (((LPBITMAPINFOHEADER
)format
)->biCompression
!= BI_RGB
) {
444 WARN(": need RGB data as input\n");
445 return AVIERR_UNSUPPORTED
;
448 /* Input format already known?
449 * Changing of palette is supported, but be quiet if it's the same */
450 if (This
->lpbiInput
!= NULL
) {
451 if (This
->cbInput
!= formatsize
)
452 return AVIERR_UNSUPPORTED
;
454 if (memcmp(format
, This
->lpbiInput
, formatsize
) == 0)
458 /* Does the nested stream support writing? */
459 if ((This
->sInfo
.dwCaps
& AVIFILECAPS_CANWRITE
) == 0)
460 return AVIERR_READONLY
;
462 /* check if frame is already written */
463 if (This
->sInfo
.dwLength
+ This
->sInfo
.dwStart
> pos
)
464 return AVIERR_UNSUPPORTED
;
466 /* check if we should compress */
467 if (This
->sInfo
.fccHandler
== 0 ||
468 This
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E'))
469 This
->sInfo
.fccHandler
= comptypeDIB
;
471 /* only pass through? */
472 if (This
->sInfo
.fccHandler
== comptypeDIB
)
473 return IAVIStream_SetFormat(This
->pStream
, pos
, format
, formatsize
);
475 /* initial format setting? */
476 if (This
->lpbiInput
== NULL
) {
479 assert(This
->hic
!= NULL
);
481 /* get memory for input format */
482 This
->lpbiInput
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, formatsize
);
483 if (This
->lpbiInput
== NULL
)
484 return AVIERR_MEMORY
;
485 This
->cbInput
= formatsize
;
486 memcpy(This
->lpbiInput
, format
, formatsize
);
488 /* get output format */
489 size
= ICCompressGetFormatSize(This
->hic
, This
->lpbiInput
);
490 if (size
< sizeof(BITMAPINFOHEADER
))
491 return AVIERR_COMPRESSOR
;
492 This
->lpbiOutput
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
493 if (This
->lpbiOutput
== NULL
)
494 return AVIERR_MEMORY
;
495 This
->cbOutput
= size
;
496 if (ICCompressGetFormat(This
->hic
,This
->lpbiInput
,This
->lpbiOutput
) < S_OK
)
497 return AVIERR_COMPRESSOR
;
499 /* update AVISTREAMINFO structure */
500 This
->sInfo
.rcFrame
.right
=
501 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
502 This
->sInfo
.rcFrame
.bottom
=
503 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
505 /* prepare codec for compression */
506 if (ICCompressBegin(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
) != S_OK
)
507 return AVIERR_COMPRESSOR
;
509 /* allocate memory for compressed frame */
510 size
= ICCompressGetSize(This
->hic
, This
->lpbiInput
, This
->lpbiOutput
);
512 (LPBITMAPINFOHEADER
)GlobalAllocPtr(GMEM_MOVEABLE
, This
->cbOutput
+ size
);
513 if (This
->lpbiCur
== NULL
)
514 return AVIERR_MEMORY
;
515 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
516 This
->lpCur
= DIBPTR(This
->lpbiCur
);
518 /* allocate memory for last frame if needed */
519 if (This
->lKeyFrameEvery
!= 1 &&
520 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
521 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
522 This
->lpbiPrev
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
523 if (This
->lpbiPrev
== NULL
)
524 return AVIERR_MEMORY
;
525 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
526 return AVIERR_COMPRESSOR
;
528 if (This
->lpbiPrev
->biSizeImage
== 0) {
529 This
->lpbiPrev
->biSizeImage
=
530 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
533 /* get memory for format and picture */
534 size
+= This
->lpbiPrev
->biSizeImage
;
536 (LPBITMAPINFOHEADER
)GlobalReAllocPtr(This
->lpbiPrev
,size
,GMEM_MOVEABLE
);
537 if (This
->lpbiPrev
== NULL
)
538 return AVIERR_MEMORY
;
539 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
541 /* prepare codec also for decompression */
542 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
543 return AVIERR_COMPRESSOR
;
546 /* format change -- check that's only the palette */
547 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)format
;
549 if (lpbi
->biSize
!= This
->lpbiInput
->biSize
||
550 lpbi
->biWidth
!= This
->lpbiInput
->biWidth
||
551 lpbi
->biHeight
!= This
->lpbiInput
->biHeight
||
552 lpbi
->biBitCount
!= This
->lpbiInput
->biBitCount
||
553 lpbi
->biPlanes
!= This
->lpbiInput
->biPlanes
||
554 lpbi
->biCompression
!= This
->lpbiInput
->biCompression
||
555 lpbi
->biClrUsed
!= This
->lpbiInput
->biClrUsed
)
556 return AVIERR_UNSUPPORTED
;
558 /* get new output format */
559 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
560 return AVIERR_BADFORMAT
;
562 /* restart compression */
563 ICCompressEnd(This
->hic
);
564 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
565 return AVIERR_COMPRESSOR
;
567 /* check if we need to restart decompresion also */
568 if (This
->lKeyFrameEvery
!= 1 &&
569 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
570 ICDecompressEnd(This
->hic
);
571 if (ICDecompressGetFormat(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) < S_OK
)
572 return AVIERR_COMPRESSOR
;
573 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
574 return AVIERR_COMPRESSOR
;
578 /* tell nested stream the new format */
579 return IAVIStream_SetFormat(This
->pStream
, pos
,
580 This
->lpbiOutput
, This
->cbOutput
);
583 static HRESULT WINAPI
ICMStream_fnRead(IAVIStream
*iface
, LONG start
,
584 LONG samples
, LPVOID buffer
,
585 LONG buffersize
, LPLONG bytesread
,
588 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
590 LPBITMAPINFOHEADER lpbi
;
592 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface
, start
, samples
, buffer
,
593 buffersize
, bytesread
, samplesread
);
595 /* clear return parameters if given */
596 if (bytesread
!= NULL
)
598 if (samplesread
!= NULL
)
604 /* check parameters */
605 if (samples
!= 1 && (bytesread
== NULL
&& samplesread
== NULL
))
606 return AVIERR_BADPARAM
;
607 if (samples
== -1) /* read as much as we could */
610 if (This
->pg
== NULL
) {
611 HRESULT hr
= AVIFILE_OpenGetFrame(This
);
617 /* compress or decompress? */
618 if (This
->hic
== NULL
) {
620 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, start
);
622 return AVIERR_MEMORY
;
624 if (buffer
!= NULL
&& buffersize
> 0) {
625 /* check buffersize */
626 if (buffersize
< lpbi
->biSizeImage
)
627 return AVIERR_BUFFERTOOSMALL
;
629 memcpy(buffer
, DIBPTR(lpbi
), lpbi
->biSizeImage
);
632 /* fill out return parameters if given */
633 if (bytesread
!= NULL
)
634 *bytesread
= lpbi
->biSizeImage
;
637 if (This
->lCurrent
> start
)
640 while (start
> This
->lCurrent
) {
643 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, ++This
->lCurrent
);
646 return AVIERR_MEMORY
;
649 hr
= AVIFILE_EncodeFrame(This
, lpbi
, DIBPTR(lpbi
));
656 if (buffer
!= NULL
&& buffersize
> 0) {
657 /* check buffersize */
658 if (This
->lpbiCur
->biSizeImage
> buffersize
)
659 return AVIERR_BUFFERTOOSMALL
;
661 memcpy(buffer
, This
->lpCur
, This
->lpbiCur
->biSizeImage
);
664 /* fill out return parameters if given */
665 if (bytesread
!= NULL
)
666 *bytesread
= This
->lpbiCur
->biSizeImage
;
669 /* fill out return parameters if given */
670 if (samplesread
!= NULL
)
676 static HRESULT WINAPI
ICMStream_fnWrite(IAVIStream
*iface
, LONG start
,
677 LONG samples
, LPVOID buffer
,
678 LONG buffersize
, DWORD flags
,
682 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
686 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface
, start
, samples
,
687 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
689 /* clear return parameters if given */
690 if (sampwritten
!= NULL
)
692 if (byteswritten
!= NULL
)
695 /* check parameters */
696 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
697 return AVIERR_BADPARAM
;
699 if (This
->sInfo
.fccHandler
== comptypeDIB
) {
700 /* only pass through */
701 flags
|= AVIIF_KEYFRAME
;
703 return IAVIStream_Write(This
->pStream
, start
, samples
, buffer
, buffersize
,
704 flags
, sampwritten
, byteswritten
);
706 /* compress data before writing to pStream */
707 if (samples
!= 1 && (sampwritten
== NULL
&& byteswritten
== NULL
))
708 return AVIERR_UNSUPPORTED
;
710 This
->lCurrent
= start
;
711 hr
= AVIFILE_EncodeFrame(This
, This
->lpbiInput
, buffer
);
715 if (This
->lLastKey
== start
)
716 flags
|= AVIIF_KEYFRAME
;
718 return IAVIStream_Write(This
->pStream
, start
, samples
, This
->lpCur
,
719 This
->lpbiCur
->biSizeImage
, flags
, byteswritten
,
724 static HRESULT WINAPI
ICMStream_fnDelete(IAVIStream
*iface
, LONG start
,
727 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
729 TRACE("(%p,%ld,%ld)\n", iface
, start
, samples
);
731 return IAVIStream_Delete(This
->pStream
, start
, samples
);
734 static HRESULT WINAPI
ICMStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
735 LPVOID lp
, LPLONG lpread
)
737 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
739 TRACE("(%p,0x%08lX,%p,%p)\n", iface
, fcc
, lp
, lpread
);
741 assert(This
->pStream
!= NULL
);
743 return IAVIStream_ReadData(This
->pStream
, fcc
, lp
, lpread
);
746 static HRESULT WINAPI
ICMStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
747 LPVOID lp
, LONG size
)
749 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
751 TRACE("(%p,0x%08lx,%p,%ld)\n", iface
, fcc
, lp
, size
);
753 assert(This
->pStream
!= NULL
);
755 return IAVIStream_WriteData(This
->pStream
, fcc
, lp
, size
);
758 static HRESULT WINAPI
ICMStream_fnSetInfo(IAVIStream
*iface
,
759 LPAVISTREAMINFOW info
, LONG infolen
)
761 FIXME("(%p,%p,%ld): stub\n", iface
, info
, infolen
);
766 /***********************************************************************/
768 static HRESULT
AVIFILE_EncodeFrame(IAVIStreamImpl
*This
,
769 LPBITMAPINFOHEADER lpbi
, LPVOID lpBits
)
771 DWORD dwMinQual
, dwMaxQual
, dwCurQual
;
775 BOOL bDecreasedQual
= FALSE
;
779 /* make lKeyFrameEvery and at start a keyframe */
780 if ((This
->lKeyFrameEvery
!= 0 &&
781 (This
->lCurrent
- This
->lLastKey
) >= This
->lKeyFrameEvery
) ||
782 This
->lCurrent
== This
->sInfo
.dwStart
) {
783 idxFlags
= AVIIF_KEYFRAME
;
784 icmFlags
= ICCOMPRESS_KEYFRAME
;
787 if (This
->lKeyFrameEvery
!= 0) {
788 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
789 if (idxFlags
& AVIIF_KEYFRAME
) {
790 /* for keyframes allow to consume all unused bytes */
791 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
792 This
->dwUnusedBytes
= 0;
794 /* for non-keyframes only allow something of the unused bytes to be consumed */
798 if (This
->dwBytesPerFrame
>= This
->dwUnusedBytes
)
799 tmp1
= This
->dwBytesPerFrame
/ This
->lKeyFrameEvery
;
800 tmp2
= (This
->dwUnusedBytes
+ tmp1
) / This
->lKeyFrameEvery
;
802 dwRequest
= This
->dwBytesPerFrame
- tmp1
+ tmp2
;
803 This
->dwUnusedBytes
-= tmp2
;
806 dwRequest
= MAX_FRAMESIZE
;
808 /* only one keyframe at start desired */
809 if (This
->lCurrent
== This
->sInfo
.dwStart
) {
810 dwRequest
= This
->dwBytesPerFrame
+ This
->dwUnusedBytes
;
811 This
->dwUnusedBytes
= 0;
813 dwRequest
= MAX_FRAMESIZE
;
816 /* must we check for framesize to gain requested
817 * datarate or could we trust codec? */
818 doSizeCheck
= (dwRequest
!= 0 && ((This
->dwICMFlags
& (VIDCF_CRUNCH
|VIDCF_QUALITY
)) == 0));
820 dwMaxQual
= dwCurQual
= This
->sInfo
.dwQuality
;
821 dwMinQual
= ICQUALITY_LOW
;
824 if ((icmFlags
& ICCOMPRESS_KEYFRAME
) == 0 &&
825 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0)
832 hr
= ICCompress(This
->hic
,icmFlags
,This
->lpbiCur
,This
->lpCur
,lpbi
,lpBits
,
833 &idxCkid
, &idxFlags
, This
->lCurrent
, dwRequest
, dwCurQual
,
834 noPrev
? NULL
:This
->lpbiPrev
, noPrev
? NULL
:This
->lpPrev
);
835 if (hr
== ICERR_NEWPALETTE
) {
836 FIXME(": codec has changed palette -- unhandled!\n");
837 } else if (hr
!= ICERR_OK
)
838 return AVIERR_COMPRESSOR
;
840 /* need to check for framesize */
844 if (dwRequest
>= This
->lpbiCur
->biSizeImage
) {
845 /* frame is smaller -- try to maximize quality */
846 if (dwMaxQual
- dwCurQual
> 10) {
847 DWORD tmp
= dwRequest
/ 8;
849 if (tmp
< MAX_FRAMESIZE_DIFF
)
850 tmp
= MAX_FRAMESIZE_DIFF
;
852 if (tmp
< dwRequest
- This
->lpbiCur
->biSizeImage
&& bDecreasedQual
) {
854 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
860 } else if (dwMaxQual
- dwMinQual
<= 1) {
863 dwMaxQual
= dwCurQual
;
865 if (bDecreasedQual
|| dwCurQual
== This
->dwLastQuality
)
866 dwCurQual
= (dwMinQual
+ dwMaxQual
) / 2;
868 FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
869 dwMinQual
, dwCurQual
, dwMaxQual
, This
->dwLastQuality
);
871 bDecreasedQual
= TRUE
;
875 /* remember some values */
876 This
->dwLastQuality
= dwCurQual
;
877 This
->dwUnusedBytes
= dwRequest
- This
->lpbiCur
->biSizeImage
;
878 if (icmFlags
& ICCOMPRESS_KEYFRAME
)
879 This
->lLastKey
= This
->lCurrent
;
881 /* Does we manage previous frame? */
882 if (This
->lpPrev
!= NULL
&& This
->lKeyFrameEvery
!= 1)
883 ICDecompress(This
->hic
, 0, This
->lpbiCur
, This
->lpCur
,
884 This
->lpbiPrev
, This
->lpPrev
);
889 static HRESULT
AVIFILE_OpenGetFrame(IAVIStreamImpl
*This
)
891 LPBITMAPINFOHEADER lpbi
;
895 assert(This
!= NULL
);
896 assert(This
->pStream
!= NULL
);
897 assert(This
->pg
== NULL
);
899 This
->pg
= AVIStreamGetFrameOpen(This
->pStream
, NULL
);
900 if (This
->pg
== NULL
)
903 /* When we only decompress this is enough */
904 if (This
->sInfo
.fccHandler
== comptypeDIB
)
907 assert(This
->hic
!= NULL
);
908 assert(This
->lpbiOutput
== NULL
);
910 /* get input format */
911 lpbi
= (LPBITMAPINFOHEADER
)AVIStreamGetFrame(This
->pg
, This
->sInfo
.dwStart
);
913 return AVIERR_MEMORY
;
915 /* get memory for output format */
916 size
= ICCompressGetFormatSize(This
->hic
, lpbi
);
917 if ((LONG
)size
< (LONG
)sizeof(BITMAPINFOHEADER
))
918 return AVIERR_COMPRESSOR
;
919 This
->lpbiOutput
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
920 if (This
->lpbiOutput
== NULL
)
921 return AVIERR_MEMORY
;
922 This
->cbOutput
= size
;
924 if (ICCompressGetFormat(This
->hic
, lpbi
, This
->lpbiOutput
) < S_OK
)
925 return AVIERR_BADFORMAT
;
927 /* update AVISTREAMINFO structure */
928 This
->sInfo
.rcFrame
.right
=
929 This
->sInfo
.rcFrame
.left
+ This
->lpbiOutput
->biWidth
;
930 This
->sInfo
.rcFrame
.bottom
=
931 This
->sInfo
.rcFrame
.top
+ This
->lpbiOutput
->biHeight
;
932 This
->sInfo
.dwSuggestedBufferSize
=
933 ICCompressGetSize(This
->hic
, lpbi
, This
->lpbiOutput
);
935 /* prepare codec for compression */
936 if (ICCompressBegin(This
->hic
, lpbi
, This
->lpbiOutput
) != S_OK
)
937 return AVIERR_COMPRESSOR
;
939 /* allocate memory for current frame */
940 size
+= This
->sInfo
.dwSuggestedBufferSize
;
941 This
->lpbiCur
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GMEM_MOVEABLE
, size
);
942 if (This
->lpbiCur
== NULL
)
943 return AVIERR_MEMORY
;
944 memcpy(This
->lpbiCur
, This
->lpbiOutput
, This
->cbOutput
);
945 This
->lpCur
= DIBPTR(This
->lpbiCur
);
947 /* allocate memory for last frame if needed */
948 if (This
->lKeyFrameEvery
!= 1 &&
949 (This
->dwICMFlags
& VIDCF_FASTTEMPORALC
) == 0) {
950 size
= ICDecompressGetFormatSize(This
->hic
, This
->lpbiOutput
);
951 This
->lpbiPrev
= (LPBITMAPINFOHEADER
)GlobalAllocPtr(GHND
, size
);
952 if (This
->lpbiPrev
== NULL
)
953 return AVIERR_MEMORY
;
954 if (ICDecompressGetFormat(This
->hic
, This
->lpbiOutput
, This
->lpbiPrev
) < S_OK
)
955 return AVIERR_COMPRESSOR
;
957 if (This
->lpbiPrev
->biSizeImage
== 0) {
958 This
->lpbiPrev
->biSizeImage
=
959 DIBWIDTHBYTES(*This
->lpbiPrev
) * This
->lpbiPrev
->biHeight
;
962 /* get memory for format and picture */
963 size
+= This
->lpbiPrev
->biSizeImage
;
965 (LPBITMAPINFOHEADER
)GlobalReAllocPtr(This
->lpbiPrev
,size
,GMEM_MOVEABLE
);
966 if (This
->lpbiPrev
== NULL
)
967 return AVIERR_MEMORY
;
968 This
->lpPrev
= DIBPTR(This
->lpbiPrev
);
970 /* prepare codec also for decompression */
971 if (ICDecompressBegin(This
->hic
,This
->lpbiOutput
,This
->lpbiPrev
) != S_OK
)
972 return AVIERR_COMPRESSOR
;