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