- some new logging on device enumeration
[wine/multimedia.git] / dlls / avifil32 / icmstream.c
blob663c50577b090da7a87ebfe4d3da46fb30a3c1ae
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 #define COM_NO_WINDOWS_H
20 #include <assert.h>
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "winerror.h"
29 #include "windowsx.h"
30 #include "mmsystem.h"
31 #include "vfw.h"
32 #include "msacm.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 ICOM_VTABLE(IAVIStream) iicmst = {
61 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
62 ICMStream_fnQueryInterface,
63 ICMStream_fnAddRef,
64 ICMStream_fnRelease,
65 ICMStream_fnCreate,
66 ICMStream_fnInfo,
67 ICMStream_fnFindSample,
68 ICMStream_fnReadFormat,
69 ICMStream_fnSetFormat,
70 ICMStream_fnRead,
71 ICMStream_fnWrite,
72 ICMStream_fnDelete,
73 ICMStream_fnReadData,
74 ICMStream_fnWriteData,
75 ICMStream_fnSetInfo
78 typedef struct _IAVIStreamImpl {
79 /* IUnknown stuff */
80 ICOM_VFIELD(IAVIStream);
81 DWORD ref;
83 /* IAVIStream stuff */
84 PAVISTREAM pStream;
85 AVISTREAMINFOW sInfo;
87 PGETFRAME pg;
88 HIC hic;
89 DWORD dwICMFlags;
91 LONG lCurrent;
92 LONG lLastKey;
93 LONG lKeyFrameEvery;
94 DWORD dwLastQuality;
95 DWORD dwBytesPerFrame;
96 DWORD dwUnusedBytes;
98 LPBITMAPINFOHEADER lpbiCur; /* current frame */
99 LPVOID lpCur;
100 LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
101 LPVOID lpPrev;
103 LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
104 LONG cbOutput;
105 LPBITMAPINFOHEADER lpbiInput; /* input format for codec */
106 LONG cbInput;
107 } IAVIStreamImpl;
109 /***********************************************************************/
111 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
112 LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
113 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
115 inline void AVIFILE_Reset(IAVIStreamImpl *This)
117 This->lCurrent = -1;
118 This->lLastKey = 0;
119 This->dwLastQuality = ICQUALITY_HIGH;
120 This->dwUnusedBytes = 0;
123 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
125 IAVIStreamImpl *pstream;
126 HRESULT hr;
128 assert(riid != NULL && ppv != NULL);
130 *ppv = NULL;
132 pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
133 if (pstream == NULL)
134 return AVIERR_MEMORY;
136 pstream->lpVtbl = &iicmst;
137 AVIFILE_Reset(pstream);
139 hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
140 if (FAILED(hr))
141 LocalFree((HLOCAL)pstream);
143 return hr;
146 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface,
147 REFIID refiid, LPVOID *obj)
149 ICOM_THIS(IAVIStreamImpl,iface);
151 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
153 if (IsEqualGUID(&IID_IUnknown, refiid) ||
154 IsEqualGUID(&IID_IAVIStream, refiid)) {
155 *obj = This;
156 IAVIStream_AddRef(iface);
158 return S_OK;
161 return OLE_E_ENUM_NOMORE;
164 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface)
166 ICOM_THIS(IAVIStreamImpl,iface);
168 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
170 /* also add reference to the nested stream */
171 if (This->pStream != NULL)
172 IAVIStream_AddRef(This->pStream);
174 return ++(This->ref);
177 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
179 ICOM_THIS(IAVIStreamImpl,iface);
181 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
183 if (This->ref == 0) {
184 /* destruct */
185 if (This->pg != NULL) {
186 AVIStreamGetFrameClose(This->pg);
187 This->pg = NULL;
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 GlobalFreePtr(This->lpbiPrev);
197 This->lpbiPrev = NULL;
198 This->lpPrev = NULL;
200 ICCompressEnd(This->hic);
201 This->hic = NULL;
203 if (This->lpbiCur != NULL) {
204 GlobalFreePtr(This->lpbiCur);
205 This->lpbiCur = NULL;
206 This->lpCur = NULL;
208 if (This->lpbiOutput != NULL) {
209 GlobalFreePtr(This->lpbiOutput);
210 This->lpbiOutput = NULL;
211 This->cbOutput = 0;
213 if (This->lpbiInput != NULL) {
214 GlobalFreePtr(This->lpbiInput);
215 This->lpbiInput = NULL;
216 This->cbInput = 0;
219 LocalFree((HLOCAL)This);
221 return 0;
224 /* also release reference to the nested stream */
225 if (This->pStream != NULL)
226 IAVIStream_Release(This->pStream);
228 return --This->ref;
231 /* lParam1: PAVISTREAM
232 * lParam2: LPAVICOMPRESSOPTIONS
234 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
235 LPARAM lParam2)
237 ICOM_THIS(IAVIStreamImpl,iface);
239 ICINFO icinfo;
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);
258 AVIFILE_Reset(This);
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;
282 /* use keyframes? */
283 if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
284 (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
285 This->lKeyFrameEvery = pco->dwKeyFrameEvery;
286 } else
287 This->lKeyFrameEvery = 1;
289 /* use datarate? */
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);
299 } else {
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));
317 } else
318 This->sInfo.fccHandler = comptypeDIB;
320 return AVIERR_OK;
323 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
324 LONG size)
326 ICOM_THIS(IAVIStreamImpl,iface);
328 TRACE("(%p,%p,%ld)\n", iface, psi, size);
330 if (psi == NULL)
331 return AVIERR_BADPARAM;
332 if (size < 0)
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;
339 return AVIERR_OK;
342 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
343 LONG flags)
345 ICOM_THIS(IAVIStreamImpl,iface);
347 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
349 if (flags & FIND_FROM_START) {
350 pos = This->sInfo.dwStart;
351 flags &= ~(FIND_FROM_START|FIND_PREV);
352 flags |= FIND_NEXT;
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)
373 return 0;
376 return -1;
379 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
380 LPVOID format, LONG *formatsize)
382 ICOM_THIS(IAVIStreamImpl,iface);
384 LPBITMAPINFOHEADER lpbi;
385 HRESULT hr;
387 TRACE("(%p,%ld,%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);
395 if (FAILED(hr))
396 return hr;
399 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
400 if (lpbi == NULL)
401 return AVIERR_MEMORY;
403 if (This->hic == NULL) {
404 LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
406 if (size > 0) {
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));
414 else
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;
423 else
424 hr = AVIERR_OK;
426 *formatsize = This->cbOutput;
427 return hr;
430 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos,
431 LPVOID format, LONG formatsize)
433 ICOM_THIS(IAVIStreamImpl,iface);
435 TRACE("(%p,%ld,%p,%ld)\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)
454 return AVIERR_OK;
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) {
476 LONG size;
478 assert(This->hic != NULL);
480 /* get memory for input format */
481 This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, 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 = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, 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 =
511 (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size);
512 if (This->lpbiCur == NULL)
513 return AVIERR_MEMORY;
514 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
515 This->lpCur = DIBPTR(This->lpbiCur);
517 /* allocate memory for last frame if needed */
518 if (This->lKeyFrameEvery != 1 &&
519 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
520 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
521 This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
522 if (This->lpbiPrev == NULL)
523 return AVIERR_MEMORY;
524 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
525 return AVIERR_COMPRESSOR;
527 if (This->lpbiPrev->biSizeImage == 0) {
528 This->lpbiPrev->biSizeImage =
529 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
532 /* get memory for format and picture */
533 size += This->lpbiPrev->biSizeImage;
534 This->lpbiPrev =
535 (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
536 if (This->lpbiPrev == NULL)
537 return AVIERR_MEMORY;
538 This->lpPrev = DIBPTR(This->lpbiPrev);
540 /* prepare codec also for decompression */
541 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
542 return AVIERR_COMPRESSOR;
544 } else {
545 /* format change -- check that's only the palette */
546 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
548 if (lpbi->biSize != This->lpbiInput->biSize ||
549 lpbi->biWidth != This->lpbiInput->biWidth ||
550 lpbi->biHeight != This->lpbiInput->biHeight ||
551 lpbi->biBitCount != This->lpbiInput->biBitCount ||
552 lpbi->biPlanes != This->lpbiInput->biPlanes ||
553 lpbi->biCompression != This->lpbiInput->biCompression ||
554 lpbi->biClrUsed != This->lpbiInput->biClrUsed)
555 return AVIERR_UNSUPPORTED;
557 /* get new output format */
558 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
559 return AVIERR_BADFORMAT;
561 /* restart compression */
562 ICCompressEnd(This->hic);
563 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
564 return AVIERR_COMPRESSOR;
566 /* check if we need to restart decompresion also */
567 if (This->lKeyFrameEvery != 1 &&
568 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
569 ICDecompressEnd(This->hic);
570 if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
571 return AVIERR_COMPRESSOR;
572 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
573 return AVIERR_COMPRESSOR;
577 /* tell nested stream the new format */
578 return IAVIStream_SetFormat(This->pStream, pos,
579 This->lpbiOutput, This->cbOutput);
582 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
583 LONG samples, LPVOID buffer,
584 LONG buffersize, LPLONG bytesread,
585 LPLONG samplesread)
587 ICOM_THIS(IAVIStreamImpl,iface);
589 LPBITMAPINFOHEADER lpbi;
591 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
592 buffersize, bytesread, samplesread);
594 /* clear return parameters if given */
595 if (bytesread != NULL)
596 *bytesread = 0;
597 if (samplesread != NULL)
598 *samplesread = 0;
600 if (samples == 0)
601 return AVIERR_OK;
603 /* check parameters */
604 if (samples != 1 && (bytesread == NULL && samplesread == NULL))
605 return AVIERR_BADPARAM;
606 if (samples == -1) /* read as much as we could */
607 samples = 1;
609 if (This->pg == NULL) {
610 HRESULT hr = AVIFILE_OpenGetFrame(This);
612 if (FAILED(hr))
613 return hr;
616 /* compress or decompress? */
617 if (This->hic == NULL) {
618 /* decompress */
619 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
620 if (lpbi == NULL)
621 return AVIERR_MEMORY;
623 if (buffer != NULL && buffersize > 0) {
624 /* check buffersize */
625 if (buffersize < lpbi->biSizeImage)
626 return AVIERR_BUFFERTOOSMALL;
628 memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
631 /* fill out return parameters if given */
632 if (bytesread != NULL)
633 *bytesread = lpbi->biSizeImage;
634 } else {
635 /* compress */
636 if (This->lCurrent > start)
637 AVIFILE_Reset(This);
639 while (start > This->lCurrent) {
640 HRESULT hr;
642 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
643 if (lpbi == NULL) {
644 AVIFILE_Reset(This);
645 return AVIERR_MEMORY;
648 hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
649 if (FAILED(hr)) {
650 AVIFILE_Reset(This);
651 return hr;
655 if (buffer != NULL && buffersize > 0) {
656 /* check buffersize */
657 if (This->lpbiCur->biSizeImage > buffersize)
658 return AVIERR_BUFFERTOOSMALL;
660 memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
663 /* fill out return parameters if given */
664 if (bytesread != NULL)
665 *bytesread = This->lpbiCur->biSizeImage;
668 /* fill out return parameters if given */
669 if (samplesread != NULL)
670 *samplesread = 1;
672 return AVIERR_OK;
675 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
676 LONG samples, LPVOID buffer,
677 LONG buffersize, DWORD flags,
678 LPLONG sampwritten,
679 LPLONG byteswritten)
681 ICOM_THIS(IAVIStreamImpl,iface);
683 HRESULT hr;
685 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
686 buffer, buffersize, flags, sampwritten, byteswritten);
688 /* clear return parameters if given */
689 if (sampwritten != NULL)
690 *sampwritten = 0;
691 if (byteswritten != NULL)
692 *byteswritten = 0;
694 /* check parameters */
695 if (buffer == NULL && (buffersize > 0 || samples > 0))
696 return AVIERR_BADPARAM;
698 if (This->sInfo.fccHandler == comptypeDIB) {
699 /* only pass through */
700 flags |= AVIIF_KEYFRAME;
702 return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
703 flags, sampwritten, byteswritten);
704 } else {
705 /* compress data before writing to pStream */
706 if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
707 return AVIERR_UNSUPPORTED;
709 This->lCurrent = start;
710 hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
711 if (FAILED(hr))
712 return hr;
714 if (This->lLastKey == start)
715 flags |= AVIIF_KEYFRAME;
717 return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
718 This->lpbiCur->biSizeImage, flags, byteswritten,
719 sampwritten);
723 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
724 LONG samples)
726 ICOM_THIS(IAVIStreamImpl,iface);
728 TRACE("(%p,%ld,%ld)\n", iface, start, samples);
730 return IAVIStream_Delete(This->pStream, start, samples);
733 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
734 LPVOID lp, LPLONG lpread)
736 ICOM_THIS(IAVIStreamImpl,iface);
738 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
740 assert(This->pStream != NULL);
742 return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
745 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
746 LPVOID lp, LONG size)
748 ICOM_THIS(IAVIStreamImpl,iface);
750 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
752 assert(This->pStream != NULL);
754 return IAVIStream_WriteData(This->pStream, fcc, lp, size);
757 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
758 LPAVISTREAMINFOW info, LONG infolen)
760 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
762 return E_FAIL;
765 /***********************************************************************/
767 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
768 LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
770 DWORD dwMinQual, dwMaxQual, dwCurQual;
771 DWORD dwRequest;
772 DWORD icmFlags = 0;
773 DWORD idxFlags = 0;
774 BOOL bDecreasedQual = FALSE;
775 BOOL doSizeCheck;
776 BOOL noPrev;
778 /* make lKeyFrameEvery and at start a keyframe */
779 if ((This->lKeyFrameEvery != 0 &&
780 (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
781 This->lCurrent == This->sInfo.dwStart) {
782 idxFlags = AVIIF_KEYFRAME;
783 icmFlags = ICCOMPRESS_KEYFRAME;
786 if (This->lKeyFrameEvery != 0) {
787 if (This->lCurrent == This->sInfo.dwStart) {
788 if (idxFlags & AVIIF_KEYFRAME) {
789 /* for keyframes allow to consume all unused bytes */
790 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
791 This->dwUnusedBytes = 0;
792 } else {
793 /* for non-keyframes only allow something of the unused bytes to be consumed */
794 DWORD tmp1 = 0;
795 DWORD tmp2;
797 if (This->dwBytesPerFrame >= This->dwUnusedBytes)
798 tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
799 tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
801 dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
802 This->dwUnusedBytes -= tmp2;
804 } else
805 dwRequest = MAX_FRAMESIZE;
806 } else {
807 /* only one keyframe at start desired */
808 if (This->lCurrent == This->sInfo.dwStart) {
809 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
810 This->dwUnusedBytes = 0;
811 } else
812 dwRequest = MAX_FRAMESIZE;
815 /* must we check for framesize to gain requested
816 * datarate or could we trust codec? */
817 doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
819 dwMaxQual = dwCurQual = This->sInfo.dwQuality;
820 dwMinQual = ICQUALITY_LOW;
822 noPrev = TRUE;
823 if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 &&
824 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
825 noPrev = FALSE;
827 do {
828 DWORD idxCkid = 0;
829 HRESULT hr;
831 hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
832 &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
833 noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
834 if (hr == ICERR_NEWPALETTE) {
835 FIXME(": codec has changed palette -- unhandled!\n");
836 } else if (hr != ICERR_OK)
837 return AVIERR_COMPRESSOR;
839 /* need to check for framesize */
840 if (! doSizeCheck)
841 break;
843 if (dwRequest >= This->lpbiCur->biSizeImage) {
844 /* frame is smaller -- try to maximize quality */
845 if (dwMaxQual - dwCurQual > 10) {
846 DWORD tmp = dwRequest / 8;
848 if (tmp < MAX_FRAMESIZE_DIFF)
849 tmp = MAX_FRAMESIZE_DIFF;
851 if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
852 tmp = dwCurQual;
853 dwCurQual = (dwMinQual + dwMaxQual) / 2;
854 dwMinQual = tmp;
855 continue;
857 } else
858 break;
859 } else if (dwMaxQual - dwMinQual <= 1) {
860 break;
861 } else {
862 dwMaxQual = dwCurQual;
864 if (bDecreasedQual || dwCurQual == This->dwLastQuality)
865 dwCurQual = (dwMinQual + dwMaxQual) / 2;
866 else
867 FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
868 dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
870 bDecreasedQual = TRUE;
872 } while (TRUE);
874 /* remember some values */
875 This->dwLastQuality = dwCurQual;
876 This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
877 if (icmFlags & ICCOMPRESS_KEYFRAME)
878 This->lLastKey = This->lCurrent;
880 /* Does we manage previous frame? */
881 if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
882 ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
883 This->lpbiPrev, This->lpPrev);
885 return AVIERR_OK;
888 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
890 LPBITMAPINFOHEADER lpbi;
891 DWORD size;
893 /* pre-conditions */
894 assert(This != NULL);
895 assert(This->pStream != NULL);
896 assert(This->pg == NULL);
898 This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
899 if (This->pg == NULL)
900 return AVIERR_ERROR;
902 /* When we only decompress this is enough */
903 if (This->sInfo.fccHandler == comptypeDIB)
904 return AVIERR_OK;
906 assert(This->hic != NULL);
907 assert(This->lpbiOutput == NULL);
909 /* get input format */
910 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
911 if (lpbi == NULL)
912 return AVIERR_MEMORY;
914 /* get memory for output format */
915 size = ICCompressGetFormatSize(This->hic, lpbi);
916 if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER))
917 return AVIERR_COMPRESSOR;
918 This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
919 if (This->lpbiOutput == NULL)
920 return AVIERR_MEMORY;
921 This->cbOutput = size;
923 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
924 return AVIERR_BADFORMAT;
926 /* update AVISTREAMINFO structure */
927 This->sInfo.rcFrame.right =
928 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
929 This->sInfo.rcFrame.bottom =
930 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
931 This->sInfo.dwSuggestedBufferSize =
932 ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
934 /* prepare codec for compression */
935 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
936 return AVIERR_COMPRESSOR;
938 /* allocate memory for current frame */
939 size += This->sInfo.dwSuggestedBufferSize;
940 This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size);
941 if (This->lpbiCur == NULL)
942 return AVIERR_MEMORY;
943 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
944 This->lpCur = DIBPTR(This->lpbiCur);
946 /* allocate memory for last frame if needed */
947 if (This->lKeyFrameEvery != 1 &&
948 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
949 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
950 This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
951 if (This->lpbiPrev == NULL)
952 return AVIERR_MEMORY;
953 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
954 return AVIERR_COMPRESSOR;
956 if (This->lpbiPrev->biSizeImage == 0) {
957 This->lpbiPrev->biSizeImage =
958 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
961 /* get memory for format and picture */
962 size += This->lpbiPrev->biSizeImage;
963 This->lpbiPrev =
964 (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
965 if (This->lpbiPrev == NULL)
966 return AVIERR_MEMORY;
967 This->lpPrev = DIBPTR(This->lpbiPrev);
969 /* prepare codec also for decompression */
970 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
971 return AVIERR_COMPRESSOR;
974 return AVIERR_OK;