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