Proper logging of DDSCAPS / DDSCAPS2 structures.
[wine/multimedia.git] / dlls / avifil32 / icmstream.c
blobb109dabac7f02485fb46ca663aa32e5cb8060dbe
1 /*
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 #include <assert.h>
21 #include "winbase.h"
22 #include "winuser.h"
23 #include "winnls.h"
24 #include "winerror.h"
25 #include "windowsx.h"
26 #include "mmsystem.h"
27 #include "vfw.h"
28 #include "msacm.h"
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,
59 ICMStream_fnAddRef,
60 ICMStream_fnRelease,
61 ICMStream_fnCreate,
62 ICMStream_fnInfo,
63 ICMStream_fnFindSample,
64 ICMStream_fnReadFormat,
65 ICMStream_fnSetFormat,
66 ICMStream_fnRead,
67 ICMStream_fnWrite,
68 ICMStream_fnDelete,
69 ICMStream_fnReadData,
70 ICMStream_fnWriteData,
71 ICMStream_fnSetInfo
74 typedef struct _IAVIStreamImpl {
75 /* IUnknown stuff */
76 ICOM_VFIELD(IAVIStream);
77 DWORD ref;
79 /* IAVIStream stuff */
80 PAVISTREAM pStream;
81 AVISTREAMINFOW sInfo;
83 PGETFRAME pg;
84 HIC hic;
85 DWORD dwICMFlags;
87 LONG lCurrent;
88 LONG lLastKey;
89 LONG lKeyFrameEvery;
90 DWORD dwLastQuality;
91 DWORD dwBytesPerFrame;
92 DWORD dwUnusedBytes;
94 LPBITMAPINFOHEADER lpbiCur; /* current frame */
95 LPVOID lpCur;
96 LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
97 LPVOID lpPrev;
99 LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
100 LONG cbOutput;
101 LPBITMAPINFOHEADER lpbiInput; /* input format for codec */
102 LONG cbInput;
103 } IAVIStreamImpl;
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)
113 This->lCurrent = -1;
114 This->lLastKey = 0;
115 This->dwLastQuality = ICQUALITY_HIGH;
116 This->dwUnusedBytes = 0;
119 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
121 IAVIStreamImpl *pstream;
122 HRESULT hr;
124 assert(riid != NULL && ppv != NULL);
126 *ppv = NULL;
128 pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
129 if (pstream == NULL)
130 return AVIERR_MEMORY;
132 ICOM_VTBL(pstream) = &iicmst;
133 AVIFILE_Reset(pstream);
135 hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
136 if (FAILED(hr))
137 LocalFree((HLOCAL)pstream);
139 return hr;
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)) {
151 *obj = This;
152 IAVIStream_AddRef(iface);
154 return S_OK;
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) {
180 /* destruct */
181 if (This->pg != NULL) {
182 AVIStreamGetFrameClose(This->pg);
183 This->pg = NULL;
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;
194 This->lpPrev = NULL;
196 ICCompressEnd(This->hic);
197 This->hic = (HIC)NULL;
199 if (This->lpbiCur != NULL) {
200 GlobalFreePtr(This->lpbiCur);
201 This->lpbiCur = NULL;
202 This->lpCur = NULL;
204 if (This->lpbiOutput != NULL) {
205 GlobalFreePtr(This->lpbiOutput);
206 This->lpbiOutput = NULL;
207 This->cbOutput = 0;
209 if (This->lpbiInput != NULL) {
210 GlobalFreePtr(This->lpbiInput);
211 This->lpbiInput = NULL;
212 This->cbInput = 0;
215 LocalFree((HLOCAL)This);
217 return 0;
220 /* also release reference to the nested stream */
221 if (This->pStream != NULL)
222 IAVIStream_Release(This->pStream);
224 return --This->ref;
227 /* lParam1: PAVISTREAM
228 * lParam2: LPAVICOMPRESSOPTIONS
230 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
231 LPARAM lParam2)
233 ICOM_THIS(IAVIStreamImpl,iface);
235 ICINFO icinfo;
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);
254 AVIFILE_Reset(This);
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;
278 /* use keyframes? */
279 if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
280 (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
281 This->lKeyFrameEvery = pco->dwKeyFrameEvery;
282 } else
283 This->lKeyFrameEvery = 1;
285 /* use datarate? */
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);
295 } else {
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));
313 } else
314 This->sInfo.fccHandler = comptypeDIB;
316 return AVIERR_OK;
319 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
320 LONG size)
322 ICOM_THIS(IAVIStreamImpl,iface);
324 TRACE("(%p,%p,%ld)\n", iface, psi, size);
326 if (psi == NULL)
327 return AVIERR_BADPARAM;
328 if (size < 0)
329 return AVIERR_BADSIZE;
331 memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo)));
333 if (size < sizeof(This->sInfo))
334 return AVIERR_BUFFERTOOSMALL;
335 return AVIERR_OK;
338 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
339 LONG flags)
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);
348 flags |= FIND_NEXT;
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)
369 return 0;
372 return -1;
375 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
376 LPVOID format, LONG *formatsize)
378 ICOM_THIS(IAVIStreamImpl,iface);
380 LPBITMAPINFOHEADER lpbi;
381 HRESULT hr;
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);
391 if (FAILED(hr))
392 return hr;
395 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
396 if (lpbi == NULL)
397 return AVIERR_MEMORY;
399 if (This->hic == (HIC)NULL) {
400 LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
402 if (size > 0) {
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));
410 else
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;
419 else
420 hr = AVIERR_OK;
422 *formatsize = This->cbOutput;
423 return hr;
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)
450 return AVIERR_OK;
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) {
472 LONG size;
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);
506 This->lpbiCur =
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;
530 This->lpbiPrev =
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;
540 } else {
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,
581 LPLONG samplesread)
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)
592 *bytesread = 0;
593 if (samplesread != NULL)
594 *samplesread = 0;
596 if (samples == 0)
597 return AVIERR_OK;
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 */
603 samples = 1;
605 if (This->pg == NULL) {
606 HRESULT hr = AVIFILE_OpenGetFrame(This);
608 if (FAILED(hr))
609 return hr;
612 /* compress or decompress? */
613 if (This->hic == (HIC)NULL) {
614 /* decompress */
615 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
616 if (lpbi == NULL)
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;
630 } else {
631 /* compress */
632 if (This->lCurrent > start)
633 AVIFILE_Reset(This);
635 while (start > This->lCurrent) {
636 HRESULT hr;
638 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
639 if (lpbi == NULL) {
640 AVIFILE_Reset(This);
641 return AVIERR_MEMORY;
644 hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
645 if (FAILED(hr)) {
646 AVIFILE_Reset(This);
647 return hr;
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)
666 *samplesread = 1;
668 return AVIERR_OK;
671 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
672 LONG samples, LPVOID buffer,
673 LONG buffersize, DWORD flags,
674 LPLONG sampwritten,
675 LPLONG byteswritten)
677 ICOM_THIS(IAVIStreamImpl,iface);
679 HRESULT hr;
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)
686 *sampwritten = 0;
687 if (byteswritten != NULL)
688 *byteswritten = 0;
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);
700 } else {
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);
707 if (FAILED(hr))
708 return hr;
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,
715 sampwritten);
719 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
720 LONG samples)
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);
758 return E_FAIL;
761 /***********************************************************************/
763 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
764 LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
766 DWORD dwMinQual, dwMaxQual, dwCurQual;
767 DWORD dwRequest;
768 DWORD icmFlags = 0;
769 DWORD idxFlags = 0;
770 BOOL bDecreasedQual = FALSE;
771 BOOL doSizeCheck;
772 BOOL noPrev;
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;
788 } else {
789 /* for non-keyframes only allow something of the unused bytes to be consumed */
790 DWORD tmp1 = 0;
791 DWORD tmp2;
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;
800 } else
801 dwRequest = MAX_FRAMESIZE;
802 } else {
803 /* only one keyframe at start desired */
804 if (This->lCurrent == This->sInfo.dwStart) {
805 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
806 This->dwUnusedBytes = 0;
807 } else
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;
818 noPrev = TRUE;
819 if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 &&
820 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
821 noPrev = FALSE;
823 do {
824 DWORD idxCkid = 0;
825 HRESULT hr;
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 */
836 if (! doSizeCheck)
837 break;
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) {
848 tmp = dwCurQual;
849 dwCurQual = (dwMinQual + dwMaxQual) / 2;
850 dwMinQual = tmp;
851 continue;
853 } else
854 break;
855 } else if (dwMaxQual - dwMinQual <= 1) {
856 break;
857 } else {
858 dwMaxQual = dwCurQual;
860 if (bDecreasedQual || dwCurQual == This->dwLastQuality)
861 dwCurQual = (dwMinQual + dwMaxQual) / 2;
862 else
863 FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
864 dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
866 bDecreasedQual = TRUE;
868 } while (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);
881 return AVIERR_OK;
884 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
886 LPBITMAPINFOHEADER lpbi;
887 LONG size;
889 /* pre-conditions */
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)
896 return AVIERR_ERROR;
898 /* When we only decompress this is enough */
899 if (This->sInfo.fccHandler == comptypeDIB)
900 return AVIERR_OK;
902 assert(This->hic != (HIC)NULL);
903 assert(This->lpbiOutput == NULL);
905 /* get input format */
906 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
907 if (lpbi == NULL)
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;
959 This->lpbiPrev =
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;
970 return AVIERR_OK;