Use Interlocked* functions in AddRef and Release.
[wine.git] / dlls / avifil32 / icmstream.c
blob23765831272376c18c21abfe9bffe669465a67f7
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 IAVIStreamVtbl iicmst = {
61 ICMStream_fnQueryInterface,
62 ICMStream_fnAddRef,
63 ICMStream_fnRelease,
64 ICMStream_fnCreate,
65 ICMStream_fnInfo,
66 ICMStream_fnFindSample,
67 ICMStream_fnReadFormat,
68 ICMStream_fnSetFormat,
69 ICMStream_fnRead,
70 ICMStream_fnWrite,
71 ICMStream_fnDelete,
72 ICMStream_fnReadData,
73 ICMStream_fnWriteData,
74 ICMStream_fnSetInfo
77 typedef struct _IAVIStreamImpl {
78 /* IUnknown stuff */
79 IAVIStreamVtbl *lpVtbl;
80 DWORD ref;
82 /* IAVIStream stuff */
83 PAVISTREAM pStream;
84 AVISTREAMINFOW sInfo;
86 PGETFRAME pg;
87 HIC hic;
88 DWORD dwICMFlags;
90 LONG lCurrent;
91 LONG lLastKey;
92 LONG lKeyFrameEvery;
93 DWORD dwLastQuality;
94 DWORD dwBytesPerFrame;
95 DWORD dwUnusedBytes;
97 LPBITMAPINFOHEADER lpbiCur; /* current frame */
98 LPVOID lpCur;
99 LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
100 LPVOID lpPrev;
102 LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
103 LONG cbOutput;
104 LPBITMAPINFOHEADER lpbiInput; /* input format for codec */
105 LONG cbInput;
106 } IAVIStreamImpl;
108 /***********************************************************************/
110 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
111 LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
112 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
114 inline void AVIFILE_Reset(IAVIStreamImpl *This)
116 This->lCurrent = -1;
117 This->lLastKey = 0;
118 This->dwLastQuality = ICQUALITY_HIGH;
119 This->dwUnusedBytes = 0;
122 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
124 IAVIStreamImpl *pstream;
125 HRESULT hr;
127 assert(riid != NULL && ppv != NULL);
129 *ppv = NULL;
131 pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
132 if (pstream == NULL)
133 return AVIERR_MEMORY;
135 pstream->lpVtbl = &iicmst;
136 AVIFILE_Reset(pstream);
138 hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
139 if (FAILED(hr))
140 LocalFree((HLOCAL)pstream);
142 return hr;
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)) {
154 *obj = This;
155 IAVIStream_AddRef(iface);
157 return S_OK;
160 return OLE_E_ENUM_NOMORE;
163 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface)
165 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
167 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
169 /* also add reference to the nested stream */
170 if (This->pStream != NULL)
171 IAVIStream_AddRef(This->pStream);
173 return ++(This->ref);
176 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
178 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
180 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
182 if (This->ref == 0) {
183 /* destruct */
184 if (This->pg != NULL) {
185 AVIStreamGetFrameClose(This->pg);
186 This->pg = NULL;
188 if (This->pStream != NULL) {
189 IAVIStream_Release(This->pStream);
190 This->pStream = NULL;
192 if (This->hic != NULL) {
193 if (This->lpbiPrev != NULL) {
194 ICDecompressEnd(This->hic);
195 GlobalFreePtr(This->lpbiPrev);
196 This->lpbiPrev = NULL;
197 This->lpPrev = NULL;
199 ICCompressEnd(This->hic);
200 This->hic = NULL;
202 if (This->lpbiCur != NULL) {
203 GlobalFreePtr(This->lpbiCur);
204 This->lpbiCur = NULL;
205 This->lpCur = NULL;
207 if (This->lpbiOutput != NULL) {
208 GlobalFreePtr(This->lpbiOutput);
209 This->lpbiOutput = NULL;
210 This->cbOutput = 0;
212 if (This->lpbiInput != NULL) {
213 GlobalFreePtr(This->lpbiInput);
214 This->lpbiInput = NULL;
215 This->cbInput = 0;
218 LocalFree((HLOCAL)This);
220 return 0;
223 /* also release reference to the nested stream */
224 if (This->pStream != NULL)
225 IAVIStream_Release(This->pStream);
227 return --This->ref;
230 /* lParam1: PAVISTREAM
231 * lParam2: LPAVICOMPRESSOPTIONS
233 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
234 LPARAM lParam2)
236 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
238 ICINFO icinfo;
239 ICCOMPRESSFRAMES icFrames;
240 LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2;
242 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
244 /* check parameter */
245 if ((LPVOID)lParam1 == NULL)
246 return AVIERR_BADPARAM;
248 /* get infos from stream */
249 IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
250 if (This->sInfo.fccType != streamtypeVIDEO)
251 return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */
253 /* add reference to the stream */
254 This->pStream = (PAVISTREAM)lParam1;
255 IAVIStream_AddRef(This->pStream);
257 AVIFILE_Reset(This);
259 if (pco != NULL && pco->fccHandler != comptypeDIB) {
260 /* we should compress */
261 This->sInfo.fccHandler = pco->fccHandler;
263 This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS);
264 if (This->hic == NULL)
265 return AVIERR_NOCOMPRESSOR;
267 /* restore saved state of codec */
268 if (pco->cbParms > 0 && pco->lpParms != NULL) {
269 ICSetState(This->hic, pco->lpParms, pco->cbParms);
272 /* set quality -- resolve default quality */
273 This->sInfo.dwQuality = pco->dwQuality;
274 if (pco->dwQuality == ICQUALITY_DEFAULT)
275 This->sInfo.dwQuality = ICGetDefaultQuality(This->hic);
277 /* get capabilities of codec */
278 ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
279 This->dwICMFlags = icinfo.dwFlags;
281 /* use keyframes? */
282 if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
283 (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
284 This->lKeyFrameEvery = pco->dwKeyFrameEvery;
285 } else
286 This->lKeyFrameEvery = 1;
288 /* use datarate? */
289 if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) {
290 /* Do we have a chance to reduce size to desired one? */
291 if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)
292 return AVIERR_NOCOMPRESSOR;
294 assert(This->sInfo.dwRate != 0);
296 This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond,
297 This->sInfo.dwScale, This->sInfo.dwRate);
298 } else {
299 pco->dwBytesPerSecond = 0;
300 This->dwBytesPerFrame = 0;
303 if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) {
304 memset(&icFrames, 0, sizeof(icFrames));
305 icFrames.lpbiOutput = This->lpbiOutput;
306 icFrames.lpbiInput = This->lpbiInput;
307 icFrames.lFrameCount = This->sInfo.dwLength;
308 icFrames.lQuality = This->sInfo.dwQuality;
309 icFrames.lDataRate = pco->dwBytesPerSecond;
310 icFrames.lKeyRate = This->lKeyFrameEvery;
311 icFrames.dwRate = This->sInfo.dwRate;
312 icFrames.dwScale = This->sInfo.dwScale;
313 ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO,
314 (LPARAM)&icFrames, (LPARAM)sizeof(icFrames));
316 } else
317 This->sInfo.fccHandler = comptypeDIB;
319 return AVIERR_OK;
322 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
323 LONG size)
325 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
327 TRACE("(%p,%p,%ld)\n", iface, psi, size);
329 if (psi == NULL)
330 return AVIERR_BADPARAM;
331 if (size < 0)
332 return AVIERR_BADSIZE;
334 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
336 if ((DWORD)size < sizeof(This->sInfo))
337 return AVIERR_BUFFERTOOSMALL;
338 return AVIERR_OK;
341 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
342 LONG flags)
344 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
346 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
348 if (flags & FIND_FROM_START) {
349 pos = This->sInfo.dwStart;
350 flags &= ~(FIND_FROM_START|FIND_PREV);
351 flags |= FIND_NEXT;
354 if (flags & FIND_RET)
355 WARN(": FIND_RET flags will be ignored!\n");
357 if (flags & FIND_KEY) {
358 if (This->hic == NULL)
359 return pos; /* we decompress so every frame is a keyframe */
361 if (flags & FIND_PREV) {
362 /* need to read old or new frames? */
363 if (This->lLastKey <= pos || pos < This->lCurrent)
364 IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL);
366 return This->lLastKey;
368 } else if (flags & FIND_ANY) {
369 return pos; /* We really don't know, reread is to expensive, so guess. */
370 } else if (flags & FIND_FORMAT) {
371 if (flags & FIND_PREV)
372 return 0;
375 return -1;
378 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
379 LPVOID format, LONG *formatsize)
381 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
383 LPBITMAPINFOHEADER lpbi;
384 HRESULT hr;
386 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
388 if (formatsize == NULL)
389 return AVIERR_BADPARAM;
391 if (This->pg == NULL) {
392 hr = AVIFILE_OpenGetFrame(This);
394 if (FAILED(hr))
395 return hr;
398 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
399 if (lpbi == NULL)
400 return AVIERR_MEMORY;
402 if (This->hic == NULL) {
403 LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
405 if (size > 0) {
406 if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage)
407 This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage;
409 This->cbOutput = size;
410 if (format != NULL) {
411 if (This->lpbiOutput != NULL)
412 memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
413 else
414 memcpy(format, lpbi, min(*formatsize, size));
417 } else if (format != NULL)
418 memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
420 if (*formatsize < This->cbOutput)
421 hr = AVIERR_BUFFERTOOSMALL;
422 else
423 hr = AVIERR_OK;
425 *formatsize = This->cbOutput;
426 return hr;
429 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos,
430 LPVOID format, LONG formatsize)
432 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
434 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
436 /* check parameters */
437 if (format == NULL || formatsize <= 0)
438 return AVIERR_BADPARAM;
440 /* We can only accept RGB data for writing */
441 if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) {
442 WARN(": need RGB data as input\n");
443 return AVIERR_UNSUPPORTED;
446 /* Input format already known?
447 * Changing of palette is supported, but be quiet if it's the same */
448 if (This->lpbiInput != NULL) {
449 if (This->cbInput != formatsize)
450 return AVIERR_UNSUPPORTED;
452 if (memcmp(format, This->lpbiInput, formatsize) == 0)
453 return AVIERR_OK;
456 /* Does the nested stream support writing? */
457 if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
458 return AVIERR_READONLY;
460 /* check if frame is already written */
461 if (This->sInfo.dwLength + This->sInfo.dwStart > pos)
462 return AVIERR_UNSUPPORTED;
464 /* check if we should compress */
465 if (This->sInfo.fccHandler == 0 ||
466 This->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))
467 This->sInfo.fccHandler = comptypeDIB;
469 /* only pass through? */
470 if (This->sInfo.fccHandler == comptypeDIB)
471 return IAVIStream_SetFormat(This->pStream, pos, format, formatsize);
473 /* initial format setting? */
474 if (This->lpbiInput == NULL) {
475 ULONG size;
477 assert(This->hic != NULL);
479 /* get memory for input format */
480 This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, formatsize);
481 if (This->lpbiInput == NULL)
482 return AVIERR_MEMORY;
483 This->cbInput = formatsize;
484 memcpy(This->lpbiInput, format, formatsize);
486 /* get output format */
487 size = ICCompressGetFormatSize(This->hic, This->lpbiInput);
488 if (size < sizeof(BITMAPINFOHEADER))
489 return AVIERR_COMPRESSOR;
490 This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
491 if (This->lpbiOutput == NULL)
492 return AVIERR_MEMORY;
493 This->cbOutput = size;
494 if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK)
495 return AVIERR_COMPRESSOR;
497 /* update AVISTREAMINFO structure */
498 This->sInfo.rcFrame.right =
499 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
500 This->sInfo.rcFrame.bottom =
501 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
503 /* prepare codec for compression */
504 if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK)
505 return AVIERR_COMPRESSOR;
507 /* allocate memory for compressed frame */
508 size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput);
509 This->lpbiCur =
510 (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size);
511 if (This->lpbiCur == NULL)
512 return AVIERR_MEMORY;
513 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
514 This->lpCur = DIBPTR(This->lpbiCur);
516 /* allocate memory for last frame if needed */
517 if (This->lKeyFrameEvery != 1 &&
518 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
519 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
520 This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
521 if (This->lpbiPrev == NULL)
522 return AVIERR_MEMORY;
523 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
524 return AVIERR_COMPRESSOR;
526 if (This->lpbiPrev->biSizeImage == 0) {
527 This->lpbiPrev->biSizeImage =
528 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
531 /* get memory for format and picture */
532 size += This->lpbiPrev->biSizeImage;
533 This->lpbiPrev =
534 (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
535 if (This->lpbiPrev == NULL)
536 return AVIERR_MEMORY;
537 This->lpPrev = DIBPTR(This->lpbiPrev);
539 /* prepare codec also for decompression */
540 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
541 return AVIERR_COMPRESSOR;
543 } else {
544 /* format change -- check that's only the palette */
545 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
547 if (lpbi->biSize != This->lpbiInput->biSize ||
548 lpbi->biWidth != This->lpbiInput->biWidth ||
549 lpbi->biHeight != This->lpbiInput->biHeight ||
550 lpbi->biBitCount != This->lpbiInput->biBitCount ||
551 lpbi->biPlanes != This->lpbiInput->biPlanes ||
552 lpbi->biCompression != This->lpbiInput->biCompression ||
553 lpbi->biClrUsed != This->lpbiInput->biClrUsed)
554 return AVIERR_UNSUPPORTED;
556 /* get new output format */
557 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
558 return AVIERR_BADFORMAT;
560 /* restart compression */
561 ICCompressEnd(This->hic);
562 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
563 return AVIERR_COMPRESSOR;
565 /* check if we need to restart decompresion also */
566 if (This->lKeyFrameEvery != 1 &&
567 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
568 ICDecompressEnd(This->hic);
569 if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
570 return AVIERR_COMPRESSOR;
571 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
572 return AVIERR_COMPRESSOR;
576 /* tell nested stream the new format */
577 return IAVIStream_SetFormat(This->pStream, pos,
578 This->lpbiOutput, This->cbOutput);
581 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
582 LONG samples, LPVOID buffer,
583 LONG buffersize, LPLONG bytesread,
584 LPLONG samplesread)
586 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
588 LPBITMAPINFOHEADER lpbi;
590 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
591 buffersize, bytesread, samplesread);
593 /* clear return parameters if given */
594 if (bytesread != NULL)
595 *bytesread = 0;
596 if (samplesread != NULL)
597 *samplesread = 0;
599 if (samples == 0)
600 return AVIERR_OK;
602 /* check parameters */
603 if (samples != 1 && (bytesread == NULL && samplesread == NULL))
604 return AVIERR_BADPARAM;
605 if (samples == -1) /* read as much as we could */
606 samples = 1;
608 if (This->pg == NULL) {
609 HRESULT hr = AVIFILE_OpenGetFrame(This);
611 if (FAILED(hr))
612 return hr;
615 /* compress or decompress? */
616 if (This->hic == NULL) {
617 /* decompress */
618 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
619 if (lpbi == NULL)
620 return AVIERR_MEMORY;
622 if (buffer != NULL && buffersize > 0) {
623 /* check buffersize */
624 if (buffersize < lpbi->biSizeImage)
625 return AVIERR_BUFFERTOOSMALL;
627 memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
630 /* fill out return parameters if given */
631 if (bytesread != NULL)
632 *bytesread = lpbi->biSizeImage;
633 } else {
634 /* compress */
635 if (This->lCurrent > start)
636 AVIFILE_Reset(This);
638 while (start > This->lCurrent) {
639 HRESULT hr;
641 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
642 if (lpbi == NULL) {
643 AVIFILE_Reset(This);
644 return AVIERR_MEMORY;
647 hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
648 if (FAILED(hr)) {
649 AVIFILE_Reset(This);
650 return hr;
654 if (buffer != NULL && buffersize > 0) {
655 /* check buffersize */
656 if (This->lpbiCur->biSizeImage > buffersize)
657 return AVIERR_BUFFERTOOSMALL;
659 memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
662 /* fill out return parameters if given */
663 if (bytesread != NULL)
664 *bytesread = This->lpbiCur->biSizeImage;
667 /* fill out return parameters if given */
668 if (samplesread != NULL)
669 *samplesread = 1;
671 return AVIERR_OK;
674 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
675 LONG samples, LPVOID buffer,
676 LONG buffersize, DWORD flags,
677 LPLONG sampwritten,
678 LPLONG byteswritten)
680 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
682 HRESULT hr;
684 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
685 buffer, buffersize, flags, sampwritten, byteswritten);
687 /* clear return parameters if given */
688 if (sampwritten != NULL)
689 *sampwritten = 0;
690 if (byteswritten != NULL)
691 *byteswritten = 0;
693 /* check parameters */
694 if (buffer == NULL && (buffersize > 0 || samples > 0))
695 return AVIERR_BADPARAM;
697 if (This->sInfo.fccHandler == comptypeDIB) {
698 /* only pass through */
699 flags |= AVIIF_KEYFRAME;
701 return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
702 flags, sampwritten, byteswritten);
703 } else {
704 /* compress data before writing to pStream */
705 if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
706 return AVIERR_UNSUPPORTED;
708 This->lCurrent = start;
709 hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
710 if (FAILED(hr))
711 return hr;
713 if (This->lLastKey == start)
714 flags |= AVIIF_KEYFRAME;
716 return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
717 This->lpbiCur->biSizeImage, flags, byteswritten,
718 sampwritten);
722 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
723 LONG samples)
725 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
727 TRACE("(%p,%ld,%ld)\n", iface, start, samples);
729 return IAVIStream_Delete(This->pStream, start, samples);
732 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
733 LPVOID lp, LPLONG lpread)
735 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
737 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
739 assert(This->pStream != NULL);
741 return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
744 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
745 LPVOID lp, LONG size)
747 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
749 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
751 assert(This->pStream != NULL);
753 return IAVIStream_WriteData(This->pStream, fcc, lp, size);
756 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
757 LPAVISTREAMINFOW info, LONG infolen)
759 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
761 return E_FAIL;
764 /***********************************************************************/
766 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
767 LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
769 DWORD dwMinQual, dwMaxQual, dwCurQual;
770 DWORD dwRequest;
771 DWORD icmFlags = 0;
772 DWORD idxFlags = 0;
773 BOOL bDecreasedQual = FALSE;
774 BOOL doSizeCheck;
775 BOOL noPrev;
777 /* make lKeyFrameEvery and at start a keyframe */
778 if ((This->lKeyFrameEvery != 0 &&
779 (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
780 This->lCurrent == This->sInfo.dwStart) {
781 idxFlags = AVIIF_KEYFRAME;
782 icmFlags = ICCOMPRESS_KEYFRAME;
785 if (This->lKeyFrameEvery != 0) {
786 if (This->lCurrent == This->sInfo.dwStart) {
787 if (idxFlags & AVIIF_KEYFRAME) {
788 /* for keyframes allow to consume all unused bytes */
789 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
790 This->dwUnusedBytes = 0;
791 } else {
792 /* for non-keyframes only allow something of the unused bytes to be consumed */
793 DWORD tmp1 = 0;
794 DWORD tmp2;
796 if (This->dwBytesPerFrame >= This->dwUnusedBytes)
797 tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
798 tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
800 dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
801 This->dwUnusedBytes -= tmp2;
803 } else
804 dwRequest = MAX_FRAMESIZE;
805 } else {
806 /* only one keyframe at start desired */
807 if (This->lCurrent == This->sInfo.dwStart) {
808 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
809 This->dwUnusedBytes = 0;
810 } else
811 dwRequest = MAX_FRAMESIZE;
814 /* must we check for framesize to gain requested
815 * datarate or could we trust codec? */
816 doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
818 dwMaxQual = dwCurQual = This->sInfo.dwQuality;
819 dwMinQual = ICQUALITY_LOW;
821 noPrev = TRUE;
822 if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 &&
823 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
824 noPrev = FALSE;
826 do {
827 DWORD idxCkid = 0;
828 HRESULT hr;
830 hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
831 &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
832 noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
833 if (hr == ICERR_NEWPALETTE) {
834 FIXME(": codec has changed palette -- unhandled!\n");
835 } else if (hr != ICERR_OK)
836 return AVIERR_COMPRESSOR;
838 /* need to check for framesize */
839 if (! doSizeCheck)
840 break;
842 if (dwRequest >= This->lpbiCur->biSizeImage) {
843 /* frame is smaller -- try to maximize quality */
844 if (dwMaxQual - dwCurQual > 10) {
845 DWORD tmp = dwRequest / 8;
847 if (tmp < MAX_FRAMESIZE_DIFF)
848 tmp = MAX_FRAMESIZE_DIFF;
850 if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
851 tmp = dwCurQual;
852 dwCurQual = (dwMinQual + dwMaxQual) / 2;
853 dwMinQual = tmp;
854 continue;
856 } else
857 break;
858 } else if (dwMaxQual - dwMinQual <= 1) {
859 break;
860 } else {
861 dwMaxQual = dwCurQual;
863 if (bDecreasedQual || dwCurQual == This->dwLastQuality)
864 dwCurQual = (dwMinQual + dwMaxQual) / 2;
865 else
866 FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
867 dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
869 bDecreasedQual = TRUE;
871 } while (TRUE);
873 /* remember some values */
874 This->dwLastQuality = dwCurQual;
875 This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
876 if (icmFlags & ICCOMPRESS_KEYFRAME)
877 This->lLastKey = This->lCurrent;
879 /* Does we manage previous frame? */
880 if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
881 ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
882 This->lpbiPrev, This->lpPrev);
884 return AVIERR_OK;
887 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
889 LPBITMAPINFOHEADER lpbi;
890 DWORD size;
892 /* pre-conditions */
893 assert(This != NULL);
894 assert(This->pStream != NULL);
895 assert(This->pg == NULL);
897 This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
898 if (This->pg == NULL)
899 return AVIERR_ERROR;
901 /* When we only decompress this is enough */
902 if (This->sInfo.fccHandler == comptypeDIB)
903 return AVIERR_OK;
905 assert(This->hic != NULL);
906 assert(This->lpbiOutput == NULL);
908 /* get input format */
909 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
910 if (lpbi == NULL)
911 return AVIERR_MEMORY;
913 /* get memory for output format */
914 size = ICCompressGetFormatSize(This->hic, lpbi);
915 if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER))
916 return AVIERR_COMPRESSOR;
917 This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
918 if (This->lpbiOutput == NULL)
919 return AVIERR_MEMORY;
920 This->cbOutput = size;
922 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
923 return AVIERR_BADFORMAT;
925 /* update AVISTREAMINFO structure */
926 This->sInfo.rcFrame.right =
927 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
928 This->sInfo.rcFrame.bottom =
929 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
930 This->sInfo.dwSuggestedBufferSize =
931 ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
933 /* prepare codec for compression */
934 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
935 return AVIERR_COMPRESSOR;
937 /* allocate memory for current frame */
938 size += This->sInfo.dwSuggestedBufferSize;
939 This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size);
940 if (This->lpbiCur == NULL)
941 return AVIERR_MEMORY;
942 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
943 This->lpCur = DIBPTR(This->lpbiCur);
945 /* allocate memory for last frame if needed */
946 if (This->lKeyFrameEvery != 1 &&
947 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
948 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
949 This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
950 if (This->lpbiPrev == NULL)
951 return AVIERR_MEMORY;
952 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
953 return AVIERR_COMPRESSOR;
955 if (This->lpbiPrev->biSizeImage == 0) {
956 This->lpbiPrev->biSizeImage =
957 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
960 /* get memory for format and picture */
961 size += This->lpbiPrev->biSizeImage;
962 This->lpbiPrev =
963 (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
964 if (This->lpbiPrev == NULL)
965 return AVIERR_MEMORY;
966 This->lpPrev = DIBPTR(This->lpbiPrev);
968 /* prepare codec also for decompression */
969 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
970 return AVIERR_COMPRESSOR;
973 return AVIERR_OK;