import avifil32 from WINE 0.9.1
[reactos.git] / reactos / lib / avifil32 / icmstream.c
1 /*
2 * Copyright 2002 Michael Günnewig
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #define COM_NO_WINDOWS_H
20 #include <assert.h>
21 #include <stdarg.h>
22
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"
33
34 #include "avifile_private.h"
35
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
39
40 #define MAX_FRAMESIZE (16 * 1024 * 1024)
41 #define MAX_FRAMESIZE_DIFF 512
42
43 /***********************************************************************/
44
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);
59
60 static const 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
75 };
76
77 typedef struct _IAVIStreamImpl {
78 /* IUnknown stuff */
79 const IAVIStreamVtbl *lpVtbl;
80 LONG ref;
81
82 /* IAVIStream stuff */
83 PAVISTREAM pStream;
84 AVISTREAMINFOW sInfo;
85
86 PGETFRAME pg;
87 HIC hic;
88 DWORD dwICMFlags;
89
90 LONG lCurrent;
91 LONG lLastKey;
92 LONG lKeyFrameEvery;
93 DWORD dwLastQuality;
94 DWORD dwBytesPerFrame;
95 DWORD dwUnusedBytes;
96
97 LPBITMAPINFOHEADER lpbiCur; /* current frame */
98 LPVOID lpCur;
99 LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
100 LPVOID lpPrev;
101
102 LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
103 LONG cbOutput;
104 LPBITMAPINFOHEADER lpbiInput; /* input format for codec */
105 LONG cbInput;
106 } IAVIStreamImpl;
107
108 /***********************************************************************/
109
110 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
111 LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
112 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
113
114 static inline void AVIFILE_Reset(IAVIStreamImpl *This)
115 {
116 This->lCurrent = -1;
117 This->lLastKey = 0;
118 This->dwLastQuality = ICQUALITY_HIGH;
119 This->dwUnusedBytes = 0;
120 }
121
122 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
123 {
124 IAVIStreamImpl *pstream;
125 HRESULT hr;
126
127 assert(riid != NULL && ppv != NULL);
128
129 *ppv = NULL;
130
131 pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
132 if (pstream == NULL)
133 return AVIERR_MEMORY;
134
135 pstream->lpVtbl = &iicmst;
136 AVIFILE_Reset(pstream);
137
138 hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv);
139 if (FAILED(hr))
140 LocalFree((HLOCAL)pstream);
141
142 return hr;
143 }
144
145 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface,
146 REFIID refiid, LPVOID *obj)
147 {
148 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
149
150 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
151
152 if (IsEqualGUID(&IID_IUnknown, refiid) ||
153 IsEqualGUID(&IID_IAVIStream, refiid)) {
154 *obj = This;
155 IAVIStream_AddRef(iface);
156
157 return S_OK;
158 }
159
160 return OLE_E_ENUM_NOMORE;
161 }
162
163 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface)
164 {
165 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
166 ULONG ref = InterlockedIncrement(&This->ref);
167
168 TRACE("(%p) -> %ld\n", iface, ref);
169
170 /* also add reference to the nested stream */
171 if (This->pStream != NULL)
172 IAVIStream_AddRef(This->pStream);
173
174 return ref;
175 }
176
177 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
178 {
179 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
180 ULONG ref = InterlockedDecrement(&This->ref);
181
182 TRACE("(%p) -> %ld\n", iface, ref);
183
184 if (ref == 0) {
185 /* destruct */
186 if (This->pg != NULL) {
187 AVIStreamGetFrameClose(This->pg);
188 This->pg = NULL;
189 }
190 if (This->pStream != NULL) {
191 IAVIStream_Release(This->pStream);
192 This->pStream = NULL;
193 }
194 if (This->hic != NULL) {
195 if (This->lpbiPrev != NULL) {
196 ICDecompressEnd(This->hic);
197 GlobalFreePtr(This->lpbiPrev);
198 This->lpbiPrev = NULL;
199 This->lpPrev = NULL;
200 }
201 ICCompressEnd(This->hic);
202 This->hic = NULL;
203 }
204 if (This->lpbiCur != NULL) {
205 GlobalFreePtr(This->lpbiCur);
206 This->lpbiCur = NULL;
207 This->lpCur = NULL;
208 }
209 if (This->lpbiOutput != NULL) {
210 GlobalFreePtr(This->lpbiOutput);
211 This->lpbiOutput = NULL;
212 This->cbOutput = 0;
213 }
214 if (This->lpbiInput != NULL) {
215 GlobalFreePtr(This->lpbiInput);
216 This->lpbiInput = NULL;
217 This->cbInput = 0;
218 }
219
220 LocalFree((HLOCAL)This);
221
222 return 0;
223 }
224
225 /* also release reference to the nested stream */
226 if (This->pStream != NULL)
227 IAVIStream_Release(This->pStream);
228
229 return ref;
230 }
231
232 /* lParam1: PAVISTREAM
233 * lParam2: LPAVICOMPRESSOPTIONS
234 */
235 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
236 LPARAM lParam2)
237 {
238 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
239
240 ICINFO icinfo;
241 ICCOMPRESSFRAMES icFrames;
242 LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2;
243
244 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
245
246 /* check parameter */
247 if ((LPVOID)lParam1 == NULL)
248 return AVIERR_BADPARAM;
249
250 /* get infos from stream */
251 IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
252 if (This->sInfo.fccType != streamtypeVIDEO)
253 return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */
254
255 /* add reference to the stream */
256 This->pStream = (PAVISTREAM)lParam1;
257 IAVIStream_AddRef(This->pStream);
258
259 AVIFILE_Reset(This);
260
261 if (pco != NULL && pco->fccHandler != comptypeDIB) {
262 /* we should compress */
263 This->sInfo.fccHandler = pco->fccHandler;
264
265 This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS);
266 if (This->hic == NULL)
267 return AVIERR_NOCOMPRESSOR;
268
269 /* restore saved state of codec */
270 if (pco->cbParms > 0 && pco->lpParms != NULL) {
271 ICSetState(This->hic, pco->lpParms, pco->cbParms);
272 }
273
274 /* set quality -- resolve default quality */
275 This->sInfo.dwQuality = pco->dwQuality;
276 if (pco->dwQuality == ICQUALITY_DEFAULT)
277 This->sInfo.dwQuality = ICGetDefaultQuality(This->hic);
278
279 /* get capabilities of codec */
280 ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
281 This->dwICMFlags = icinfo.dwFlags;
282
283 /* use keyframes? */
284 if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
285 (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
286 This->lKeyFrameEvery = pco->dwKeyFrameEvery;
287 } else
288 This->lKeyFrameEvery = 1;
289
290 /* use datarate? */
291 if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) {
292 /* Do we have a chance to reduce size to desired one? */
293 if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)
294 return AVIERR_NOCOMPRESSOR;
295
296 assert(This->sInfo.dwRate != 0);
297
298 This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond,
299 This->sInfo.dwScale, This->sInfo.dwRate);
300 } else {
301 pco->dwBytesPerSecond = 0;
302 This->dwBytesPerFrame = 0;
303 }
304
305 if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) {
306 memset(&icFrames, 0, sizeof(icFrames));
307 icFrames.lpbiOutput = This->lpbiOutput;
308 icFrames.lpbiInput = This->lpbiInput;
309 icFrames.lFrameCount = This->sInfo.dwLength;
310 icFrames.lQuality = This->sInfo.dwQuality;
311 icFrames.lDataRate = pco->dwBytesPerSecond;
312 icFrames.lKeyRate = This->lKeyFrameEvery;
313 icFrames.dwRate = This->sInfo.dwRate;
314 icFrames.dwScale = This->sInfo.dwScale;
315 ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO,
316 (LPARAM)&icFrames, (LPARAM)sizeof(icFrames));
317 }
318 } else
319 This->sInfo.fccHandler = comptypeDIB;
320
321 return AVIERR_OK;
322 }
323
324 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
325 LONG size)
326 {
327 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
328
329 TRACE("(%p,%p,%ld)\n", iface, psi, size);
330
331 if (psi == NULL)
332 return AVIERR_BADPARAM;
333 if (size < 0)
334 return AVIERR_BADSIZE;
335
336 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
337
338 if ((DWORD)size < sizeof(This->sInfo))
339 return AVIERR_BUFFERTOOSMALL;
340 return AVIERR_OK;
341 }
342
343 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
344 LONG flags)
345 {
346 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
347
348 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
349
350 if (flags & FIND_FROM_START) {
351 pos = This->sInfo.dwStart;
352 flags &= ~(FIND_FROM_START|FIND_PREV);
353 flags |= FIND_NEXT;
354 }
355
356 if (flags & FIND_RET)
357 WARN(": FIND_RET flags will be ignored!\n");
358
359 if (flags & FIND_KEY) {
360 if (This->hic == NULL)
361 return pos; /* we decompress so every frame is a keyframe */
362
363 if (flags & FIND_PREV) {
364 /* need to read old or new frames? */
365 if (This->lLastKey <= pos || pos < This->lCurrent)
366 IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL);
367
368 return This->lLastKey;
369 }
370 } else if (flags & FIND_ANY) {
371 return pos; /* We really don't know, reread is to expensive, so guess. */
372 } else if (flags & FIND_FORMAT) {
373 if (flags & FIND_PREV)
374 return 0;
375 }
376
377 return -1;
378 }
379
380 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
381 LPVOID format, LONG *formatsize)
382 {
383 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
384
385 LPBITMAPINFOHEADER lpbi;
386 HRESULT hr;
387
388 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
389
390 if (formatsize == NULL)
391 return AVIERR_BADPARAM;
392
393 if (This->pg == NULL) {
394 hr = AVIFILE_OpenGetFrame(This);
395
396 if (FAILED(hr))
397 return hr;
398 }
399
400 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
401 if (lpbi == NULL)
402 return AVIERR_MEMORY;
403
404 if (This->hic == NULL) {
405 LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
406
407 if (size > 0) {
408 if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage)
409 This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage;
410
411 This->cbOutput = size;
412 if (format != NULL) {
413 if (This->lpbiOutput != NULL)
414 memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
415 else
416 memcpy(format, lpbi, min(*formatsize, size));
417 }
418 }
419 } else if (format != NULL)
420 memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
421
422 if (*formatsize < This->cbOutput)
423 hr = AVIERR_BUFFERTOOSMALL;
424 else
425 hr = AVIERR_OK;
426
427 *formatsize = This->cbOutput;
428 return hr;
429 }
430
431 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos,
432 LPVOID format, LONG formatsize)
433 {
434 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
435
436 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
437
438 /* check parameters */
439 if (format == NULL || formatsize <= 0)
440 return AVIERR_BADPARAM;
441
442 /* We can only accept RGB data for writing */
443 if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) {
444 WARN(": need RGB data as input\n");
445 return AVIERR_UNSUPPORTED;
446 }
447
448 /* Input format already known?
449 * Changing of palette is supported, but be quiet if it's the same */
450 if (This->lpbiInput != NULL) {
451 if (This->cbInput != formatsize)
452 return AVIERR_UNSUPPORTED;
453
454 if (memcmp(format, This->lpbiInput, formatsize) == 0)
455 return AVIERR_OK;
456 }
457
458 /* Does the nested stream support writing? */
459 if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
460 return AVIERR_READONLY;
461
462 /* check if frame is already written */
463 if (This->sInfo.dwLength + This->sInfo.dwStart > pos)
464 return AVIERR_UNSUPPORTED;
465
466 /* check if we should compress */
467 if (This->sInfo.fccHandler == 0 ||
468 This->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))
469 This->sInfo.fccHandler = comptypeDIB;
470
471 /* only pass through? */
472 if (This->sInfo.fccHandler == comptypeDIB)
473 return IAVIStream_SetFormat(This->pStream, pos, format, formatsize);
474
475 /* initial format setting? */
476 if (This->lpbiInput == NULL) {
477 ULONG size;
478
479 assert(This->hic != NULL);
480
481 /* get memory for input format */
482 This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, formatsize);
483 if (This->lpbiInput == NULL)
484 return AVIERR_MEMORY;
485 This->cbInput = formatsize;
486 memcpy(This->lpbiInput, format, formatsize);
487
488 /* get output format */
489 size = ICCompressGetFormatSize(This->hic, This->lpbiInput);
490 if (size < sizeof(BITMAPINFOHEADER))
491 return AVIERR_COMPRESSOR;
492 This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
493 if (This->lpbiOutput == NULL)
494 return AVIERR_MEMORY;
495 This->cbOutput = size;
496 if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK)
497 return AVIERR_COMPRESSOR;
498
499 /* update AVISTREAMINFO structure */
500 This->sInfo.rcFrame.right =
501 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
502 This->sInfo.rcFrame.bottom =
503 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
504
505 /* prepare codec for compression */
506 if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK)
507 return AVIERR_COMPRESSOR;
508
509 /* allocate memory for compressed frame */
510 size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput);
511 This->lpbiCur =
512 (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size);
513 if (This->lpbiCur == NULL)
514 return AVIERR_MEMORY;
515 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
516 This->lpCur = DIBPTR(This->lpbiCur);
517
518 /* allocate memory for last frame if needed */
519 if (This->lKeyFrameEvery != 1 &&
520 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
521 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
522 This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
523 if (This->lpbiPrev == NULL)
524 return AVIERR_MEMORY;
525 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
526 return AVIERR_COMPRESSOR;
527
528 if (This->lpbiPrev->biSizeImage == 0) {
529 This->lpbiPrev->biSizeImage =
530 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
531 }
532
533 /* get memory for format and picture */
534 size += This->lpbiPrev->biSizeImage;
535 This->lpbiPrev =
536 (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
537 if (This->lpbiPrev == NULL)
538 return AVIERR_MEMORY;
539 This->lpPrev = DIBPTR(This->lpbiPrev);
540
541 /* prepare codec also for decompression */
542 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
543 return AVIERR_COMPRESSOR;
544 }
545 } else {
546 /* format change -- check that's only the palette */
547 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
548
549 if (lpbi->biSize != This->lpbiInput->biSize ||
550 lpbi->biWidth != This->lpbiInput->biWidth ||
551 lpbi->biHeight != This->lpbiInput->biHeight ||
552 lpbi->biBitCount != This->lpbiInput->biBitCount ||
553 lpbi->biPlanes != This->lpbiInput->biPlanes ||
554 lpbi->biCompression != This->lpbiInput->biCompression ||
555 lpbi->biClrUsed != This->lpbiInput->biClrUsed)
556 return AVIERR_UNSUPPORTED;
557
558 /* get new output format */
559 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
560 return AVIERR_BADFORMAT;
561
562 /* restart compression */
563 ICCompressEnd(This->hic);
564 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
565 return AVIERR_COMPRESSOR;
566
567 /* check if we need to restart decompresion also */
568 if (This->lKeyFrameEvery != 1 &&
569 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
570 ICDecompressEnd(This->hic);
571 if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
572 return AVIERR_COMPRESSOR;
573 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
574 return AVIERR_COMPRESSOR;
575 }
576 }
577
578 /* tell nested stream the new format */
579 return IAVIStream_SetFormat(This->pStream, pos,
580 This->lpbiOutput, This->cbOutput);
581 }
582
583 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
584 LONG samples, LPVOID buffer,
585 LONG buffersize, LPLONG bytesread,
586 LPLONG samplesread)
587 {
588 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
589
590 LPBITMAPINFOHEADER lpbi;
591
592 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
593 buffersize, bytesread, samplesread);
594
595 /* clear return parameters if given */
596 if (bytesread != NULL)
597 *bytesread = 0;
598 if (samplesread != NULL)
599 *samplesread = 0;
600
601 if (samples == 0)
602 return AVIERR_OK;
603
604 /* check parameters */
605 if (samples != 1 && (bytesread == NULL && samplesread == NULL))
606 return AVIERR_BADPARAM;
607 if (samples == -1) /* read as much as we could */
608 samples = 1;
609
610 if (This->pg == NULL) {
611 HRESULT hr = AVIFILE_OpenGetFrame(This);
612
613 if (FAILED(hr))
614 return hr;
615 }
616
617 /* compress or decompress? */
618 if (This->hic == NULL) {
619 /* decompress */
620 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
621 if (lpbi == NULL)
622 return AVIERR_MEMORY;
623
624 if (buffer != NULL && buffersize > 0) {
625 /* check buffersize */
626 if (buffersize < lpbi->biSizeImage)
627 return AVIERR_BUFFERTOOSMALL;
628
629 memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
630 }
631
632 /* fill out return parameters if given */
633 if (bytesread != NULL)
634 *bytesread = lpbi->biSizeImage;
635 } else {
636 /* compress */
637 if (This->lCurrent > start)
638 AVIFILE_Reset(This);
639
640 while (start > This->lCurrent) {
641 HRESULT hr;
642
643 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
644 if (lpbi == NULL) {
645 AVIFILE_Reset(This);
646 return AVIERR_MEMORY;
647 }
648
649 hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
650 if (FAILED(hr)) {
651 AVIFILE_Reset(This);
652 return hr;
653 }
654 }
655
656 if (buffer != NULL && buffersize > 0) {
657 /* check buffersize */
658 if (This->lpbiCur->biSizeImage > buffersize)
659 return AVIERR_BUFFERTOOSMALL;
660
661 memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
662 }
663
664 /* fill out return parameters if given */
665 if (bytesread != NULL)
666 *bytesread = This->lpbiCur->biSizeImage;
667 }
668
669 /* fill out return parameters if given */
670 if (samplesread != NULL)
671 *samplesread = 1;
672
673 return AVIERR_OK;
674 }
675
676 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
677 LONG samples, LPVOID buffer,
678 LONG buffersize, DWORD flags,
679 LPLONG sampwritten,
680 LPLONG byteswritten)
681 {
682 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
683
684 HRESULT hr;
685
686 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
687 buffer, buffersize, flags, sampwritten, byteswritten);
688
689 /* clear return parameters if given */
690 if (sampwritten != NULL)
691 *sampwritten = 0;
692 if (byteswritten != NULL)
693 *byteswritten = 0;
694
695 /* check parameters */
696 if (buffer == NULL && (buffersize > 0 || samples > 0))
697 return AVIERR_BADPARAM;
698
699 if (This->sInfo.fccHandler == comptypeDIB) {
700 /* only pass through */
701 flags |= AVIIF_KEYFRAME;
702
703 return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
704 flags, sampwritten, byteswritten);
705 } else {
706 /* compress data before writing to pStream */
707 if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
708 return AVIERR_UNSUPPORTED;
709
710 This->lCurrent = start;
711 hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
712 if (FAILED(hr))
713 return hr;
714
715 if (This->lLastKey == start)
716 flags |= AVIIF_KEYFRAME;
717
718 return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
719 This->lpbiCur->biSizeImage, flags, byteswritten,
720 sampwritten);
721 }
722 }
723
724 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
725 LONG samples)
726 {
727 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
728
729 TRACE("(%p,%ld,%ld)\n", iface, start, samples);
730
731 return IAVIStream_Delete(This->pStream, start, samples);
732 }
733
734 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
735 LPVOID lp, LPLONG lpread)
736 {
737 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
738
739 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
740
741 assert(This->pStream != NULL);
742
743 return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
744 }
745
746 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
747 LPVOID lp, LONG size)
748 {
749 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
750
751 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
752
753 assert(This->pStream != NULL);
754
755 return IAVIStream_WriteData(This->pStream, fcc, lp, size);
756 }
757
758 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
759 LPAVISTREAMINFOW info, LONG infolen)
760 {
761 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
762
763 return E_FAIL;
764 }
765
766 /***********************************************************************/
767
768 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
769 LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
770 {
771 DWORD dwMinQual, dwMaxQual, dwCurQual;
772 DWORD dwRequest;
773 DWORD icmFlags = 0;
774 DWORD idxFlags = 0;
775 BOOL bDecreasedQual = FALSE;
776 BOOL doSizeCheck;
777 BOOL noPrev;
778
779 /* make lKeyFrameEvery and at start a keyframe */
780 if ((This->lKeyFrameEvery != 0 &&
781 (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
782 This->lCurrent == This->sInfo.dwStart) {
783 idxFlags = AVIIF_KEYFRAME;
784 icmFlags = ICCOMPRESS_KEYFRAME;
785 }
786
787 if (This->lKeyFrameEvery != 0) {
788 if (This->lCurrent == This->sInfo.dwStart) {
789 if (idxFlags & AVIIF_KEYFRAME) {
790 /* for keyframes allow to consume all unused bytes */
791 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
792 This->dwUnusedBytes = 0;
793 } else {
794 /* for non-keyframes only allow something of the unused bytes to be consumed */
795 DWORD tmp1 = 0;
796 DWORD tmp2;
797
798 if (This->dwBytesPerFrame >= This->dwUnusedBytes)
799 tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
800 tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
801
802 dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
803 This->dwUnusedBytes -= tmp2;
804 }
805 } else
806 dwRequest = MAX_FRAMESIZE;
807 } else {
808 /* only one keyframe at start desired */
809 if (This->lCurrent == This->sInfo.dwStart) {
810 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
811 This->dwUnusedBytes = 0;
812 } else
813 dwRequest = MAX_FRAMESIZE;
814 }
815
816 /* must we check for framesize to gain requested
817 * datarate or could we trust codec? */
818 doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
819
820 dwMaxQual = dwCurQual = This->sInfo.dwQuality;
821 dwMinQual = ICQUALITY_LOW;
822
823 noPrev = TRUE;
824 if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 &&
825 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
826 noPrev = FALSE;
827
828 do {
829 DWORD idxCkid = 0;
830 HRESULT hr;
831
832 hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
833 &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
834 noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
835 if (hr == ICERR_NEWPALETTE) {
836 FIXME(": codec has changed palette -- unhandled!\n");
837 } else if (hr != ICERR_OK)
838 return AVIERR_COMPRESSOR;
839
840 /* need to check for framesize */
841 if (! doSizeCheck)
842 break;
843
844 if (dwRequest >= This->lpbiCur->biSizeImage) {
845 /* frame is smaller -- try to maximize quality */
846 if (dwMaxQual - dwCurQual > 10) {
847 DWORD tmp = dwRequest / 8;
848
849 if (tmp < MAX_FRAMESIZE_DIFF)
850 tmp = MAX_FRAMESIZE_DIFF;
851
852 if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
853 tmp = dwCurQual;
854 dwCurQual = (dwMinQual + dwMaxQual) / 2;
855 dwMinQual = tmp;
856 continue;
857 }
858 } else
859 break;
860 } else if (dwMaxQual - dwMinQual <= 1) {
861 break;
862 } else {
863 dwMaxQual = dwCurQual;
864
865 if (bDecreasedQual || dwCurQual == This->dwLastQuality)
866 dwCurQual = (dwMinQual + dwMaxQual) / 2;
867 else
868 FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
869 dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
870
871 bDecreasedQual = TRUE;
872 }
873 } while (TRUE);
874
875 /* remember some values */
876 This->dwLastQuality = dwCurQual;
877 This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
878 if (icmFlags & ICCOMPRESS_KEYFRAME)
879 This->lLastKey = This->lCurrent;
880
881 /* Does we manage previous frame? */
882 if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
883 ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
884 This->lpbiPrev, This->lpPrev);
885
886 return AVIERR_OK;
887 }
888
889 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
890 {
891 LPBITMAPINFOHEADER lpbi;
892 DWORD size;
893
894 /* pre-conditions */
895 assert(This != NULL);
896 assert(This->pStream != NULL);
897 assert(This->pg == NULL);
898
899 This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
900 if (This->pg == NULL)
901 return AVIERR_ERROR;
902
903 /* When we only decompress this is enough */
904 if (This->sInfo.fccHandler == comptypeDIB)
905 return AVIERR_OK;
906
907 assert(This->hic != NULL);
908 assert(This->lpbiOutput == NULL);
909
910 /* get input format */
911 lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
912 if (lpbi == NULL)
913 return AVIERR_MEMORY;
914
915 /* get memory for output format */
916 size = ICCompressGetFormatSize(This->hic, lpbi);
917 if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER))
918 return AVIERR_COMPRESSOR;
919 This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
920 if (This->lpbiOutput == NULL)
921 return AVIERR_MEMORY;
922 This->cbOutput = size;
923
924 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
925 return AVIERR_BADFORMAT;
926
927 /* update AVISTREAMINFO structure */
928 This->sInfo.rcFrame.right =
929 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
930 This->sInfo.rcFrame.bottom =
931 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
932 This->sInfo.dwSuggestedBufferSize =
933 ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
934
935 /* prepare codec for compression */
936 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
937 return AVIERR_COMPRESSOR;
938
939 /* allocate memory for current frame */
940 size += This->sInfo.dwSuggestedBufferSize;
941 This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size);
942 if (This->lpbiCur == NULL)
943 return AVIERR_MEMORY;
944 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
945 This->lpCur = DIBPTR(This->lpbiCur);
946
947 /* allocate memory for last frame if needed */
948 if (This->lKeyFrameEvery != 1 &&
949 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
950 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
951 This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
952 if (This->lpbiPrev == NULL)
953 return AVIERR_MEMORY;
954 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
955 return AVIERR_COMPRESSOR;
956
957 if (This->lpbiPrev->biSizeImage == 0) {
958 This->lpbiPrev->biSizeImage =
959 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
960 }
961
962 /* get memory for format and picture */
963 size += This->lpbiPrev->biSizeImage;
964 This->lpbiPrev =
965 (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
966 if (This->lpbiPrev == NULL)
967 return AVIERR_MEMORY;
968 This->lpPrev = DIBPTR(This->lpbiPrev);
969
970 /* prepare codec also for decompression */
971 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
972 return AVIERR_COMPRESSOR;
973 }
974
975 return AVIERR_OK;
976 }