Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/avifil32/avifile.c
4389 views
1
/*
2
* Copyright 1999 Marcus Meissner
3
* Copyright 2002-2003 Michael Günnewig
4
*
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
9
*
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
14
*
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18
*/
19
20
/* TODO:
21
* - IAVIStreaming interface is missing for the IAVIStreamImpl
22
* - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
23
* - IAVIStream_fnReadFormat: formatchanges aren't read in.
24
* - IAVIStream_fnDelete: a stub.
25
* - IAVIStream_fnSetInfo: a stub.
26
* - make thread safe
27
*
28
* KNOWN Bugs:
29
* - native version can hangup when reading a file generated with this DLL.
30
* When index is missing it works, but index seems to be okay.
31
*/
32
33
#define COBJMACROS
34
#include <assert.h>
35
#include <stdarg.h>
36
37
#include "windef.h"
38
#include "winbase.h"
39
#include "wingdi.h"
40
#include "winuser.h"
41
#include "winnls.h"
42
#include "winerror.h"
43
#include "mmsystem.h"
44
#include "vfw.h"
45
46
#include "avifile_private.h"
47
#include "extrachunk.h"
48
49
#include "wine/debug.h"
50
51
WINE_DEFAULT_DEBUG_CHANNEL(avifile);
52
53
#ifndef IDX_PER_BLOCK
54
#define IDX_PER_BLOCK 2730
55
#endif
56
57
typedef struct _IAVIFileImpl IAVIFileImpl;
58
59
typedef struct _IAVIStreamImpl {
60
IAVIStream IAVIStream_iface;
61
LONG ref;
62
63
IAVIFileImpl *paf;
64
DWORD nStream; /* the n-th stream in file */
65
AVISTREAMINFOW sInfo;
66
67
LPVOID lpFormat;
68
DWORD cbFormat;
69
70
LPVOID lpHandlerData;
71
DWORD cbHandlerData;
72
73
EXTRACHUNKS extra;
74
75
LPDWORD lpBuffer;
76
DWORD cbBuffer; /* size of lpBuffer */
77
DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
78
79
LONG lLastFrame; /* last correct index in idxFrames */
80
AVIINDEXENTRY *idxFrames;
81
DWORD nIdxFrames; /* upper index limit of idxFrames */
82
AVIINDEXENTRY *idxFmtChanges;
83
DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
84
} IAVIStreamImpl;
85
86
static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
87
{
88
return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
89
}
90
91
struct _IAVIFileImpl {
92
IUnknown IUnknown_inner;
93
IAVIFile IAVIFile_iface;
94
IPersistFile IPersistFile_iface;
95
IUnknown *outer_unk;
96
LONG ref;
97
98
AVIFILEINFOW fInfo;
99
IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
100
101
EXTRACHUNKS fileextra;
102
103
DWORD dwMoviChunkPos; /* some stuff for saving ... */
104
DWORD dwIdxChunkPos;
105
DWORD dwNextFramePos;
106
DWORD dwInitialFrames;
107
108
MMCKINFO ckLastRecord;
109
AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
110
DWORD nIdxRecords; /* current fill level */
111
DWORD cbIdxRecords; /* size of idxRecords */
112
113
/* IPersistFile stuff ... */
114
HMMIO hmmio;
115
LPWSTR szFileName;
116
UINT uMode;
117
BOOL fDirty;
118
};
119
120
static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
121
{
122
return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
123
}
124
125
static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
126
{
127
return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
128
}
129
130
static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
131
{
132
return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
133
}
134
135
/***********************************************************************/
136
137
static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
138
DWORD offset, DWORD flags);
139
static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
140
static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
141
static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
142
const AVISTREAMINFOW *asi);
143
static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
144
static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
145
static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
146
static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
147
LONG count, DWORD pos, BOOL *bAbsolute);
148
static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
149
LPVOID buffer, DWORD size);
150
static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
151
LPLONG offset);
152
static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
153
static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
154
static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
155
LONG lSkip);
156
static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
157
static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
158
FOURCC ckid, DWORD flags, LPCVOID buffer,
159
LONG size);
160
161
static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
162
{
163
IAVIFileImpl *This = impl_from_IUnknown(iface);
164
165
TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
166
167
if (!ppv) {
168
WARN("invalid parameter\n");
169
return E_INVALIDARG;
170
}
171
*ppv = NULL;
172
173
if (IsEqualIID(riid, &IID_IUnknown))
174
*ppv = &This->IUnknown_inner;
175
else if (IsEqualIID(riid, &IID_IAVIFile))
176
*ppv = &This->IAVIFile_iface;
177
else if (IsEqualGUID(riid, &IID_IPersistFile))
178
*ppv = &This->IPersistFile_iface;
179
else {
180
WARN("unknown IID %s\n", debugstr_guid(riid));
181
return E_NOINTERFACE;
182
}
183
184
/* Violation of the COM aggregation ref counting rule */
185
IUnknown_AddRef(&This->IUnknown_inner);
186
return S_OK;
187
}
188
189
static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
190
{
191
IAVIFileImpl *This = impl_from_IUnknown(iface);
192
ULONG ref = InterlockedIncrement(&This->ref);
193
194
TRACE("(%p) ref=%ld\n", This, ref);
195
196
return ref;
197
}
198
199
static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
200
{
201
IAVIFileImpl *This = impl_from_IUnknown(iface);
202
ULONG ref = InterlockedDecrement(&This->ref);
203
UINT i;
204
205
TRACE("(%p) ref=%ld\n", This, ref);
206
207
if (!ref) {
208
if (This->fDirty)
209
AVIFILE_SaveFile(This);
210
211
for (i = 0; i < This->fInfo.dwStreams; i++) {
212
if (This->ppStreams[i] != NULL) {
213
if (This->ppStreams[i]->ref != 0)
214
ERR(": someone has still %lu reference to stream %u (%p)!\n",
215
This->ppStreams[i]->ref, i, This->ppStreams[i]);
216
AVIFILE_DestructAVIStream(This->ppStreams[i]);
217
free(This->ppStreams[i]);
218
This->ppStreams[i] = NULL;
219
}
220
}
221
222
if (This->idxRecords != NULL) {
223
free(This->idxRecords);
224
This->idxRecords = NULL;
225
This->nIdxRecords = 0;
226
}
227
228
if (This->fileextra.lp != NULL) {
229
free(This->fileextra.lp);
230
This->fileextra.lp = NULL;
231
This->fileextra.cb = 0;
232
}
233
234
free(This->szFileName);
235
This->szFileName = NULL;
236
237
if (This->hmmio != NULL) {
238
mmioClose(This->hmmio, 0);
239
This->hmmio = NULL;
240
}
241
242
free(This);
243
}
244
return ref;
245
}
246
247
static const IUnknownVtbl unk_vtbl =
248
{
249
IUnknown_fnQueryInterface,
250
IUnknown_fnAddRef,
251
IUnknown_fnRelease
252
};
253
254
static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
255
{
256
IAVIFileImpl *This = impl_from_IAVIFile(iface);
257
258
return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
259
}
260
261
static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
262
{
263
IAVIFileImpl *This = impl_from_IAVIFile(iface);
264
265
return IUnknown_AddRef(This->outer_unk);
266
}
267
268
static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
269
{
270
IAVIFileImpl *This = impl_from_IAVIFile(iface);
271
272
return IUnknown_Release(This->outer_unk);
273
}
274
275
static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
276
{
277
IAVIFileImpl *This = impl_from_IAVIFile(iface);
278
279
TRACE("(%p,%p,%ld)\n",iface,afi,size);
280
281
if (afi == NULL)
282
return AVIERR_BADPARAM;
283
if (size < 0)
284
return AVIERR_BADSIZE;
285
286
AVIFILE_UpdateInfo(This);
287
288
memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
289
290
if ((DWORD)size < sizeof(This->fInfo))
291
return AVIERR_BUFFERTOOSMALL;
292
return AVIERR_OK;
293
}
294
295
static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
296
LONG lParam)
297
{
298
IAVIFileImpl *This = impl_from_IAVIFile(iface);
299
ULONG nStream;
300
301
TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam);
302
303
if (avis == NULL || lParam < 0)
304
return AVIERR_BADPARAM;
305
306
nStream = AVIFILE_SearchStream(This, fccType, lParam);
307
308
/* Does the requested stream exist? */
309
if (nStream < This->fInfo.dwStreams &&
310
This->ppStreams[nStream] != NULL) {
311
*avis = &This->ppStreams[nStream]->IAVIStream_iface;
312
IAVIStream_AddRef(*avis);
313
314
return AVIERR_OK;
315
}
316
317
/* Sorry, but the specified stream doesn't exist */
318
*avis = NULL;
319
return AVIERR_NODATA;
320
}
321
322
static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
323
AVISTREAMINFOW *asi)
324
{
325
IAVIFileImpl *This = impl_from_IAVIFile(iface);
326
DWORD n;
327
328
TRACE("(%p,%p,%p)\n", iface, avis, asi);
329
330
/* check parameters */
331
if (avis == NULL || asi == NULL)
332
return AVIERR_BADPARAM;
333
334
*avis = NULL;
335
336
/* Does the user have write permission? */
337
if ((This->uMode & MMIO_RWMODE) == 0)
338
return AVIERR_READONLY;
339
340
/* Can we add another stream? */
341
n = This->fInfo.dwStreams;
342
if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
343
/* already reached max nr of streams
344
* or have already written frames to disk */
345
return AVIERR_UNSUPPORTED;
346
}
347
348
/* check AVISTREAMINFO for some really needed things */
349
if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
350
return AVIERR_BADFORMAT;
351
352
/* now it seems to be save to add the stream */
353
assert(This->ppStreams[n] == NULL);
354
This->ppStreams[n] = calloc(1, sizeof(IAVIStreamImpl));
355
if (This->ppStreams[n] == NULL)
356
return AVIERR_MEMORY;
357
358
/* initialize the new allocated stream */
359
AVIFILE_ConstructAVIStream(This, n, asi);
360
361
This->fInfo.dwStreams++;
362
This->fDirty = TRUE;
363
364
/* update our AVIFILEINFO structure */
365
AVIFILE_UpdateInfo(This);
366
367
/* return it */
368
*avis = &This->ppStreams[n]->IAVIStream_iface;
369
IAVIStream_AddRef(*avis);
370
371
return AVIERR_OK;
372
}
373
374
static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
375
{
376
IAVIFileImpl *This = impl_from_IAVIFile(iface);
377
378
TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
379
380
/* check parameters */
381
if (lpData == NULL)
382
return AVIERR_BADPARAM;
383
if (size < 0)
384
return AVIERR_BADSIZE;
385
386
/* Do we have write permission? */
387
if ((This->uMode & MMIO_RWMODE) == 0)
388
return AVIERR_READONLY;
389
390
This->fDirty = TRUE;
391
392
return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
393
}
394
395
static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
396
{
397
IAVIFileImpl *This = impl_from_IAVIFile(iface);
398
399
TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
400
401
return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
402
}
403
404
static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
405
{
406
IAVIFileImpl *This = impl_from_IAVIFile(iface);
407
408
TRACE("(%p)\n",iface);
409
410
if ((This->uMode & MMIO_RWMODE) == 0)
411
return AVIERR_READONLY;
412
413
This->fDirty = TRUE;
414
415
/* no frames written to any stream? -- compute start of 'movi'-chunk */
416
if (This->dwMoviChunkPos == 0)
417
AVIFILE_ComputeMoviStart(This);
418
419
This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
420
421
/* already written frames to any stream, ... */
422
if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
423
/* close last record */
424
if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
425
return AVIERR_FILEWRITE;
426
427
AVIFILE_AddRecord(This);
428
429
if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
430
This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
431
}
432
433
/* write out a new record into file, but don't close it */
434
This->ckLastRecord.cksize = 0;
435
This->ckLastRecord.fccType = listtypeAVIRECORD;
436
if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
437
return AVIERR_FILEWRITE;
438
if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
439
return AVIERR_FILEWRITE;
440
This->dwNextFramePos += 3 * sizeof(DWORD);
441
442
return AVIERR_OK;
443
}
444
445
static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
446
{
447
IAVIFileImpl *This = impl_from_IAVIFile(iface);
448
ULONG nStream;
449
450
TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
451
452
/* check parameter */
453
if (lParam < 0)
454
return AVIERR_BADPARAM;
455
456
/* Have user write permissions? */
457
if ((This->uMode & MMIO_RWMODE) == 0)
458
return AVIERR_READONLY;
459
460
nStream = AVIFILE_SearchStream(This, fccType, lParam);
461
462
/* Does the requested stream exist? */
463
if (nStream < This->fInfo.dwStreams &&
464
This->ppStreams[nStream] != NULL) {
465
/* ... so delete it now */
466
free(This->ppStreams[nStream]);
467
This->fInfo.dwStreams--;
468
if (nStream < This->fInfo.dwStreams)
469
memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1],
470
(This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0]));
471
472
This->ppStreams[This->fInfo.dwStreams] = NULL;
473
This->fDirty = TRUE;
474
475
/* This->fInfo will be updated further when asked for */
476
return AVIERR_OK;
477
} else
478
return AVIERR_NODATA;
479
}
480
481
static const struct IAVIFileVtbl avif_vt = {
482
IAVIFile_fnQueryInterface,
483
IAVIFile_fnAddRef,
484
IAVIFile_fnRelease,
485
IAVIFile_fnInfo,
486
IAVIFile_fnGetStream,
487
IAVIFile_fnCreateStream,
488
IAVIFile_fnWriteData,
489
IAVIFile_fnReadData,
490
IAVIFile_fnEndRecord,
491
IAVIFile_fnDeleteStream
492
};
493
494
495
static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
496
{
497
IAVIFileImpl *This = impl_from_IPersistFile(iface);
498
499
return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
500
}
501
502
static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
503
{
504
IAVIFileImpl *This = impl_from_IPersistFile(iface);
505
506
return IUnknown_AddRef(This->outer_unk);
507
}
508
509
static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
510
{
511
IAVIFileImpl *This = impl_from_IPersistFile(iface);
512
513
return IUnknown_Release(This->outer_unk);
514
}
515
516
static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
517
{
518
TRACE("(%p,%p)\n", iface, pClassID);
519
520
if (pClassID == NULL)
521
return AVIERR_BADPARAM;
522
523
*pClassID = CLSID_AVIFile;
524
525
return AVIERR_OK;
526
}
527
528
static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
529
{
530
IAVIFileImpl *This = impl_from_IPersistFile(iface);
531
532
TRACE("(%p)\n", iface);
533
534
return (This->fDirty ? S_OK : S_FALSE);
535
}
536
537
static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
538
{
539
IAVIFileImpl *This = impl_from_IPersistFile(iface);
540
int len;
541
542
TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
543
544
/* check parameter */
545
if (pszFileName == NULL)
546
return AVIERR_BADPARAM;
547
548
if (This->hmmio != NULL)
549
return AVIERR_ERROR; /* No reuse of this object for another file! */
550
551
/* remember mode and name */
552
This->uMode = dwMode;
553
554
len = lstrlenW(pszFileName) + 1;
555
This->szFileName = malloc(len * sizeof(WCHAR));
556
if (This->szFileName == NULL)
557
return AVIERR_MEMORY;
558
lstrcpyW(This->szFileName, pszFileName);
559
560
/* try to open the file */
561
This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
562
if (This->hmmio == NULL) {
563
/* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
564
LPSTR szFileName;
565
566
len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
567
szFileName = malloc(len * sizeof(CHAR));
568
if (szFileName == NULL)
569
return AVIERR_MEMORY;
570
571
WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
572
573
This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
574
free(szFileName);
575
if (This->hmmio == NULL)
576
return AVIERR_FILEOPEN;
577
}
578
579
/* should we create a new file? */
580
if (dwMode & OF_CREATE) {
581
memset(& This->fInfo, 0, sizeof(This->fInfo));
582
This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
583
584
return AVIERR_OK;
585
} else
586
return AVIFILE_LoadFile(This);
587
}
588
589
static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
590
BOOL fRemember)
591
{
592
TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
593
594
/* We write directly to disk, so nothing to do. */
595
596
return AVIERR_OK;
597
}
598
599
static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
600
{
601
TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
602
603
/* We write directly to disk, so nothing to do. */
604
605
return AVIERR_OK;
606
}
607
608
static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
609
{
610
IAVIFileImpl *This = impl_from_IPersistFile(iface);
611
612
TRACE("(%p,%p)\n", iface, ppszFileName);
613
614
if (ppszFileName == NULL)
615
return AVIERR_BADPARAM;
616
617
*ppszFileName = NULL;
618
619
if (This->szFileName != NULL) {
620
int len = lstrlenW(This->szFileName) + 1;
621
622
*ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
623
if (*ppszFileName == NULL)
624
return AVIERR_MEMORY;
625
626
lstrcpyW(*ppszFileName, This->szFileName);
627
}
628
629
return AVIERR_OK;
630
}
631
632
static const struct IPersistFileVtbl pf_vt = {
633
IPersistFile_fnQueryInterface,
634
IPersistFile_fnAddRef,
635
IPersistFile_fnRelease,
636
IPersistFile_fnGetClassID,
637
IPersistFile_fnIsDirty,
638
IPersistFile_fnLoad,
639
IPersistFile_fnSave,
640
IPersistFile_fnSaveCompleted,
641
IPersistFile_fnGetCurFile
642
};
643
644
HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
645
{
646
IAVIFileImpl *obj;
647
HRESULT hr;
648
649
*ppv = NULL;
650
obj = calloc(1, sizeof(IAVIFileImpl));
651
if (!obj)
652
return AVIERR_MEMORY;
653
654
obj->IUnknown_inner.lpVtbl = &unk_vtbl;
655
obj->IAVIFile_iface.lpVtbl = &avif_vt;
656
obj->IPersistFile_iface.lpVtbl = &pf_vt;
657
obj->ref = 1;
658
if (pUnkOuter)
659
obj->outer_unk = pUnkOuter;
660
else
661
obj->outer_unk = &obj->IUnknown_inner;
662
663
hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
664
IUnknown_Release(&obj->IUnknown_inner);
665
666
return hr;
667
}
668
669
670
static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
671
{
672
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
673
674
TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
675
676
if (!ppv) {
677
WARN("invalid parameter\n");
678
return E_INVALIDARG;
679
}
680
*ppv = NULL;
681
682
if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
683
*ppv = iface;
684
IAVIStream_AddRef(iface);
685
686
return S_OK;
687
}
688
/* FIXME: IAVIStreaming interface */
689
690
return E_NOINTERFACE;
691
}
692
693
static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
694
{
695
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
696
ULONG ref = InterlockedIncrement(&This->ref);
697
698
TRACE("(%p) ref=%ld\n", This, ref);
699
700
/* also add ref to parent, so that it doesn't kill us */
701
if (This->paf != NULL && ref == 1)
702
IAVIFile_AddRef(&This->paf->IAVIFile_iface);
703
704
return ref;
705
}
706
707
static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
708
{
709
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
710
ULONG ref = InterlockedDecrement(&This->ref);
711
712
TRACE("(%p) ref=%ld\n", This, ref);
713
714
if (This->paf != NULL && ref == 0)
715
IAVIFile_Release(&This->paf->IAVIFile_iface);
716
717
return ref;
718
}
719
720
static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
721
{
722
TRACE("(%p,0x%08IX,0x%08IX)\n", iface, lParam1, lParam2);
723
724
/* This IAVIStream interface needs an AVIFile */
725
return AVIERR_UNSUPPORTED;
726
}
727
728
static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
729
{
730
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
731
732
TRACE("(%p,%p,%ld)\n", iface, psi, size);
733
734
if (psi == NULL)
735
return AVIERR_BADPARAM;
736
if (size < 0)
737
return AVIERR_BADSIZE;
738
739
memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
740
741
if ((DWORD)size < sizeof(This->sInfo))
742
return AVIERR_BUFFERTOOSMALL;
743
return AVIERR_OK;
744
}
745
746
static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
747
{
748
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
749
LONG offset = 0;
750
751
TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
752
753
if (flags & FIND_FROM_START) {
754
pos = This->sInfo.dwStart;
755
flags &= ~(FIND_FROM_START|FIND_PREV);
756
flags |= FIND_NEXT;
757
}
758
759
if (This->sInfo.dwSampleSize != 0) {
760
/* convert samples into block number with offset */
761
AVIFILE_SamplesToBlock(This, &pos, &offset);
762
}
763
764
if (flags & FIND_TYPE) {
765
if (flags & FIND_KEY) {
766
while (0 <= pos && pos <= This->lLastFrame) {
767
if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
768
goto RETURN_FOUND;
769
770
if (flags & FIND_NEXT)
771
pos++;
772
else
773
pos--;
774
};
775
} else if (flags & FIND_ANY) {
776
while (0 <= pos && pos <= This->lLastFrame) {
777
if (This->idxFrames[pos].dwChunkLength > 0)
778
goto RETURN_FOUND;
779
780
if (flags & FIND_NEXT)
781
pos++;
782
else
783
pos--;
784
785
};
786
} else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
787
This->sInfo.fccType == streamtypeVIDEO) {
788
if (flags & FIND_NEXT) {
789
ULONG n;
790
791
for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
792
if (This->idxFmtChanges[n].ckid >= pos) {
793
pos = This->idxFmtChanges[n].ckid;
794
goto RETURN_FOUND;
795
}
796
} else {
797
LONG n;
798
799
for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
800
if (This->idxFmtChanges[n].ckid <= pos) {
801
pos = This->idxFmtChanges[n].ckid;
802
goto RETURN_FOUND;
803
}
804
}
805
806
if (pos > (LONG)This->sInfo.dwStart)
807
return 0; /* format changes always for first frame */
808
}
809
}
810
811
return -1;
812
}
813
814
RETURN_FOUND:
815
if (pos < (LONG)This->sInfo.dwStart)
816
return -1;
817
818
switch (flags & FIND_RET) {
819
case FIND_LENGTH:
820
/* physical size */
821
pos = This->idxFrames[pos].dwChunkLength;
822
break;
823
case FIND_OFFSET:
824
/* physical position */
825
pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
826
+ offset * This->sInfo.dwSampleSize;
827
break;
828
case FIND_SIZE:
829
/* logical size */
830
if (This->sInfo.dwSampleSize)
831
pos = This->sInfo.dwSampleSize;
832
else
833
pos = 1;
834
break;
835
case FIND_INDEX:
836
FIXME(": FIND_INDEX flag is not supported!\n");
837
/* This is an index in the index-table on disc. */
838
break;
839
}; /* else logical position */
840
841
return pos;
842
}
843
844
static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
845
LONG *formatsize)
846
{
847
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
848
849
TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
850
851
if (formatsize == NULL)
852
return AVIERR_BADPARAM;
853
854
/* only interested in needed buffersize? */
855
if (format == NULL || *formatsize <= 0) {
856
*formatsize = This->cbFormat;
857
858
return AVIERR_OK;
859
}
860
861
/* copy initial format (only as much as will fit) */
862
memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
863
if (*(DWORD*)formatsize < This->cbFormat) {
864
*formatsize = This->cbFormat;
865
return AVIERR_BUFFERTOOSMALL;
866
}
867
868
/* Could format change? When yes will it change? */
869
if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
870
pos > This->sInfo.dwStart) {
871
LONG lLastFmt;
872
873
lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
874
if (lLastFmt > 0) {
875
FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt);
876
}
877
}
878
879
*formatsize = This->cbFormat;
880
return AVIERR_OK;
881
}
882
883
static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
884
LONG formatsize)
885
{
886
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
887
BITMAPINFOHEADER *lpbiNew = format;
888
889
TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
890
891
/* check parameters */
892
if (format == NULL || formatsize <= 0)
893
return AVIERR_BADPARAM;
894
895
/* Do we have write permission? */
896
if ((This->paf->uMode & MMIO_RWMODE) == 0)
897
return AVIERR_READONLY;
898
899
/* can only set format before frame is written! */
900
if (This->lLastFrame > pos)
901
return AVIERR_UNSUPPORTED;
902
903
/* initial format or a formatchange? */
904
if (This->lpFormat == NULL) {
905
/* initial format */
906
if (This->paf->dwMoviChunkPos != 0)
907
return AVIERR_ERROR; /* user has used API in wrong sequence! */
908
909
This->lpFormat = malloc(formatsize);
910
if (This->lpFormat == NULL)
911
return AVIERR_MEMORY;
912
This->cbFormat = formatsize;
913
914
memcpy(This->lpFormat, format, formatsize);
915
916
/* update some infos about stream */
917
if (This->sInfo.fccType == streamtypeVIDEO) {
918
LONG lDim;
919
920
lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
921
if (lDim < lpbiNew->biWidth)
922
This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
923
lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
924
if (lDim < lpbiNew->biHeight)
925
This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
926
} else if (This->sInfo.fccType == streamtypeAUDIO)
927
This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
928
929
return AVIERR_OK;
930
} else {
931
MMCKINFO ck;
932
LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
933
RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
934
AVIPALCHANGE *lppc = NULL;
935
UINT n;
936
937
/* perhaps format change, check it ... */
938
if (This->cbFormat != formatsize)
939
return AVIERR_UNSUPPORTED;
940
941
/* no format change, only the initial one */
942
if (memcmp(This->lpFormat, format, formatsize) == 0)
943
return AVIERR_OK;
944
945
/* check that's only the palette, which changes */
946
if (lpbiOld->biSize != lpbiNew->biSize ||
947
lpbiOld->biWidth != lpbiNew->biWidth ||
948
lpbiOld->biHeight != lpbiNew->biHeight ||
949
lpbiOld->biPlanes != lpbiNew->biPlanes ||
950
lpbiOld->biBitCount != lpbiNew->biBitCount ||
951
lpbiOld->biCompression != lpbiNew->biCompression ||
952
lpbiOld->biClrUsed != lpbiNew->biClrUsed)
953
return AVIERR_UNSUPPORTED;
954
955
This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
956
957
/* simply say all colors have changed */
958
ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
959
ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
960
lppc = malloc(ck.cksize);
961
if (lppc == NULL)
962
return AVIERR_MEMORY;
963
964
lppc->bFirstEntry = 0;
965
lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
966
lppc->wFlags = 0;
967
for (n = 0; n < lpbiOld->biClrUsed; n++) {
968
lppc->peNew[n].peRed = rgbNew[n].rgbRed;
969
lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
970
lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
971
lppc->peNew[n].peFlags = 0;
972
}
973
974
if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
975
mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
976
mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
977
mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
978
{
979
free(lppc);
980
return AVIERR_FILEWRITE;
981
}
982
983
This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
984
985
free(lppc);
986
987
return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
988
}
989
}
990
991
static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
992
LONG buffersize, LONG *bytesread, LONG *samplesread)
993
{
994
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
995
DWORD size;
996
HRESULT hr;
997
998
TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
999
buffersize, bytesread, samplesread);
1000
1001
/* clear return parameters if given */
1002
if (bytesread != NULL)
1003
*bytesread = 0;
1004
if (samplesread != NULL)
1005
*samplesread = 0;
1006
1007
/* check parameters */
1008
if ((LONG)This->sInfo.dwStart > start)
1009
return AVIERR_NODATA; /* couldn't read before start of stream */
1010
if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1011
return AVIERR_NODATA; /* start is past end of stream */
1012
1013
/* should we read as much as possible? */
1014
if (samples == -1) {
1015
/* User should know how much we have read */
1016
if (bytesread == NULL && samplesread == NULL)
1017
return AVIERR_BADPARAM;
1018
1019
if (This->sInfo.dwSampleSize != 0)
1020
samples = buffersize / This->sInfo.dwSampleSize;
1021
else
1022
samples = 1;
1023
}
1024
1025
/* limit to end of stream */
1026
if ((LONG)This->sInfo.dwLength < samples)
1027
samples = This->sInfo.dwLength;
1028
if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1029
samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1030
1031
/* nothing to read? Then leave ... */
1032
if (samples == 0)
1033
return AVIERR_OK;
1034
1035
if (This->sInfo.dwSampleSize != 0) {
1036
/* fixed samplesize -- we can read over frame/block boundaries */
1037
LONG block = start;
1038
LONG offset = 0;
1039
1040
if (!buffer)
1041
{
1042
if (bytesread)
1043
*bytesread = samples*This->sInfo.dwSampleSize;
1044
if (samplesread)
1045
*samplesread = samples;
1046
return AVIERR_OK;
1047
}
1048
1049
/* convert start sample to block,offset pair */
1050
AVIFILE_SamplesToBlock(This, &block, &offset);
1051
1052
/* convert samples to bytes */
1053
samples *= This->sInfo.dwSampleSize;
1054
1055
while (samples > 0 && buffersize > 0) {
1056
LONG blocksize;
1057
if (block != This->dwCurrentFrame) {
1058
hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1059
if (FAILED(hr))
1060
return hr;
1061
}
1062
1063
size = min((DWORD)samples, (DWORD)buffersize);
1064
blocksize = This->lpBuffer[1];
1065
TRACE("blocksize = %lu\n",blocksize);
1066
size = min(size, blocksize - offset);
1067
memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1068
1069
block++;
1070
offset = 0;
1071
buffer = ((LPBYTE)buffer)+size;
1072
samples -= size;
1073
buffersize -= size;
1074
1075
/* fill out return parameters if given */
1076
if (bytesread != NULL)
1077
*bytesread += size;
1078
if (samplesread != NULL)
1079
*samplesread += size / This->sInfo.dwSampleSize;
1080
}
1081
1082
if (samples == 0)
1083
return AVIERR_OK;
1084
else
1085
return AVIERR_BUFFERTOOSMALL;
1086
} else {
1087
/* variable samplesize -- we can only read one full frame/block */
1088
if (samples > 1)
1089
samples = 1;
1090
1091
assert(start <= This->lLastFrame);
1092
size = This->idxFrames[start].dwChunkLength;
1093
if (buffer != NULL && buffersize >= size) {
1094
hr = AVIFILE_ReadBlock(This, start, buffer, size);
1095
if (FAILED(hr))
1096
return hr;
1097
} else if (buffer != NULL)
1098
return AVIERR_BUFFERTOOSMALL;
1099
1100
/* fill out return parameters if given */
1101
if (bytesread != NULL)
1102
*bytesread = size;
1103
if (samplesread != NULL)
1104
*samplesread = samples;
1105
1106
return AVIERR_OK;
1107
}
1108
}
1109
1110
static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
1111
LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1112
{
1113
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1114
FOURCC ckid;
1115
HRESULT hr;
1116
1117
TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
1118
buffer, buffersize, flags, sampwritten, byteswritten);
1119
1120
/* clear return parameters if given */
1121
if (sampwritten != NULL)
1122
*sampwritten = 0;
1123
if (byteswritten != NULL)
1124
*byteswritten = 0;
1125
1126
/* check parameters */
1127
if (buffer == NULL && (buffersize > 0 || samples > 0))
1128
return AVIERR_BADPARAM;
1129
1130
/* Have we write permission? */
1131
if ((This->paf->uMode & MMIO_RWMODE) == 0)
1132
return AVIERR_READONLY;
1133
1134
switch (This->sInfo.fccType) {
1135
case streamtypeAUDIO:
1136
ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1137
break;
1138
default:
1139
if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1140
ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1141
else
1142
ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1143
break;
1144
};
1145
1146
/* append to end of stream? */
1147
if (start == -1) {
1148
if (This->lLastFrame == -1)
1149
start = This->sInfo.dwStart;
1150
else
1151
start = This->sInfo.dwLength;
1152
} else if (This->lLastFrame == -1)
1153
This->sInfo.dwStart = start;
1154
1155
if (This->sInfo.dwSampleSize != 0) {
1156
/* fixed sample size -- audio like */
1157
if (samples * This->sInfo.dwSampleSize != buffersize)
1158
return AVIERR_BADPARAM;
1159
1160
/* Couldn't skip audio-like data -- User must supply appropriate silence */
1161
if (This->sInfo.dwLength != start)
1162
return AVIERR_UNSUPPORTED;
1163
1164
/* Convert position to frame/block */
1165
start = This->lLastFrame + 1;
1166
1167
if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1168
FIXME(": not interleaved, could collect audio data!\n");
1169
}
1170
} else {
1171
/* variable sample size -- video like */
1172
if (samples > 1)
1173
return AVIERR_UNSUPPORTED;
1174
1175
/* must we fill up with empty frames? */
1176
if (This->lLastFrame != -1) {
1177
FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1178
1179
while (start > This->lLastFrame + 1) {
1180
hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1181
if (FAILED(hr))
1182
return hr;
1183
}
1184
}
1185
}
1186
1187
/* write the block now */
1188
hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1189
if (SUCCEEDED(hr)) {
1190
/* fill out return parameters if given */
1191
if (sampwritten != NULL)
1192
*sampwritten = samples;
1193
if (byteswritten != NULL)
1194
*byteswritten = buffersize;
1195
}
1196
1197
return hr;
1198
}
1199
1200
static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1201
{
1202
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1203
1204
FIXME("(%p,%ld,%ld): stub\n", iface, start, samples);
1205
1206
/* check parameters */
1207
if (start < 0 || samples < 0)
1208
return AVIERR_BADPARAM;
1209
1210
/* Delete before start of stream? */
1211
if (start + samples < This->sInfo.dwStart)
1212
return AVIERR_OK;
1213
1214
/* Delete after end of stream? */
1215
if (start > This->sInfo.dwLength)
1216
return AVIERR_OK;
1217
1218
/* For the rest we need write permissions */
1219
if ((This->paf->uMode & MMIO_RWMODE) == 0)
1220
return AVIERR_READONLY;
1221
1222
/* 1. overwrite the data with JUNK
1223
*
1224
* if ISINTERLEAVED {
1225
* 2. concat all neighboured JUNK-blocks in this record to one
1226
* 3. if this record only contains JUNK and is at end set dwNextFramePos
1227
* to start of this record, repeat this.
1228
* } else {
1229
* 2. concat all neighboured JUNK-blocks.
1230
* 3. if the JUNK block is at the end, then set dwNextFramePos to
1231
* start of this block.
1232
* }
1233
*/
1234
1235
return AVIERR_UNSUPPORTED;
1236
}
1237
1238
static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1239
{
1240
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1241
1242
TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
1243
1244
if (fcc == ckidSTREAMHANDLERDATA) {
1245
if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1246
if (lp == NULL || *lpread <= 0) {
1247
*lpread = This->cbHandlerData;
1248
return AVIERR_OK;
1249
}
1250
1251
memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1252
if (*lpread < This->cbHandlerData)
1253
return AVIERR_BUFFERTOOSMALL;
1254
return AVIERR_OK;
1255
} else
1256
return AVIERR_NODATA;
1257
} else
1258
return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1259
}
1260
1261
static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1262
{
1263
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1264
1265
TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
1266
1267
/* check parameters */
1268
if (lp == NULL)
1269
return AVIERR_BADPARAM;
1270
if (size <= 0)
1271
return AVIERR_BADSIZE;
1272
1273
/* need write permission */
1274
if ((This->paf->uMode & MMIO_RWMODE) == 0)
1275
return AVIERR_READONLY;
1276
1277
/* already written something to this file? */
1278
if (This->paf->dwMoviChunkPos != 0) {
1279
/* the data will be inserted before the 'movi' chunk, so check for
1280
* enough space */
1281
DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1282
1283
/* ckid,size => 2 * sizeof(DWORD) */
1284
dwPos += 2 * sizeof(DWORD) + size;
1285
if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1286
return AVIERR_UNSUPPORTED; /* not enough space left */
1287
}
1288
1289
This->paf->fDirty = TRUE;
1290
1291
if (fcc == ckidSTREAMHANDLERDATA) {
1292
if (This->lpHandlerData != NULL) {
1293
FIXME(": handler data already set -- overwrite?\n");
1294
return AVIERR_UNSUPPORTED;
1295
}
1296
1297
This->lpHandlerData = malloc(size);
1298
if (This->lpHandlerData == NULL)
1299
return AVIERR_MEMORY;
1300
This->cbHandlerData = size;
1301
memcpy(This->lpHandlerData, lp, size);
1302
1303
return AVIERR_OK;
1304
} else
1305
return WriteExtraChunk(&This->extra, fcc, lp, size);
1306
}
1307
1308
static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1309
{
1310
FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
1311
1312
return E_FAIL;
1313
}
1314
1315
static const struct IAVIStreamVtbl avist_vt = {
1316
IAVIStream_fnQueryInterface,
1317
IAVIStream_fnAddRef,
1318
IAVIStream_fnRelease,
1319
IAVIStream_fnCreate,
1320
IAVIStream_fnInfo,
1321
IAVIStream_fnFindSample,
1322
IAVIStream_fnReadFormat,
1323
IAVIStream_fnSetFormat,
1324
IAVIStream_fnRead,
1325
IAVIStream_fnWrite,
1326
IAVIStream_fnDelete,
1327
IAVIStream_fnReadData,
1328
IAVIStream_fnWriteData,
1329
IAVIStream_fnSetInfo
1330
};
1331
1332
1333
static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1334
{
1335
UINT n;
1336
1337
/* pre-conditions */
1338
assert(This != NULL);
1339
1340
switch (TWOCCFromFOURCC(ckid)) {
1341
case cktypeDIBbits:
1342
if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1343
flags |= AVIIF_KEYFRAME;
1344
break;
1345
case cktypeDIBcompressed:
1346
if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1347
flags &= ~AVIIF_KEYFRAME;
1348
break;
1349
case cktypePALchange:
1350
if (This->sInfo.fccType != streamtypeVIDEO) {
1351
ERR(": found palette change in non-video stream!\n");
1352
return AVIERR_BADFORMAT;
1353
}
1354
1355
if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1356
DWORD new_count = This->nIdxFmtChanges + 16;
1357
void *new_buffer = _recalloc(This->idxFmtChanges, new_count, sizeof(AVIINDEXENTRY));
1358
if (!new_buffer) return AVIERR_MEMORY;
1359
This->idxFmtChanges = new_buffer;
1360
This->nIdxFmtChanges = new_count;
1361
}
1362
1363
This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1364
n = ++This->sInfo.dwFormatChangeCount;
1365
This->idxFmtChanges[n].ckid = This->lLastFrame;
1366
This->idxFmtChanges[n].dwFlags = 0;
1367
This->idxFmtChanges[n].dwChunkOffset = offset;
1368
This->idxFmtChanges[n].dwChunkLength = size;
1369
1370
return AVIERR_OK;
1371
case cktypeWAVEbytes:
1372
if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1373
flags |= AVIIF_KEYFRAME;
1374
break;
1375
default:
1376
WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1377
break;
1378
};
1379
1380
/* first frame is always a keyframe */
1381
if (This->lLastFrame == -1)
1382
flags |= AVIIF_KEYFRAME;
1383
1384
if (This->sInfo.dwSuggestedBufferSize < size)
1385
This->sInfo.dwSuggestedBufferSize = size;
1386
1387
/* get memory for index */
1388
if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1389
This->nIdxFrames += 512;
1390
This->idxFrames = _recalloc(This->idxFrames, This->nIdxFrames, sizeof(AVIINDEXENTRY));
1391
if (This->idxFrames == NULL)
1392
return AVIERR_MEMORY;
1393
}
1394
1395
This->lLastFrame++;
1396
This->idxFrames[This->lLastFrame].ckid = ckid;
1397
This->idxFrames[This->lLastFrame].dwFlags = flags;
1398
This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1399
This->idxFrames[This->lLastFrame].dwChunkLength = size;
1400
1401
/* update AVISTREAMINFO structure if necessary */
1402
if (This->sInfo.dwSampleSize)
1403
{
1404
unsigned int block;
1405
1406
size = 0;
1407
for (block = 0; block <= This->lLastFrame; block++)
1408
size += This->idxFrames[block].dwChunkLength;
1409
This->sInfo.dwLength = max(This->sInfo.dwLength, size / This->sInfo.dwSampleSize);
1410
}
1411
else
1412
{
1413
This->sInfo.dwLength = max(This->sInfo.dwLength, This->lLastFrame + 1);
1414
}
1415
return AVIERR_OK;
1416
}
1417
1418
static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1419
{
1420
/* pre-conditions */
1421
assert(This != NULL && This->ppStreams[0] != NULL);
1422
1423
if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) {
1424
DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY);
1425
void *mem;
1426
mem = _recalloc(This->idxRecords, 1, new_count);
1427
if (mem) {
1428
This->cbIdxRecords = new_count;
1429
This->idxRecords = mem;
1430
} else {
1431
free(This->idxRecords);
1432
This->idxRecords = NULL;
1433
return AVIERR_MEMORY;
1434
}
1435
}
1436
1437
assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1438
1439
This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1440
This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1441
This->idxRecords[This->nIdxRecords].dwChunkOffset =
1442
This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1443
This->idxRecords[This->nIdxRecords].dwChunkLength =
1444
This->ckLastRecord.cksize;
1445
This->nIdxRecords++;
1446
1447
return AVIERR_OK;
1448
}
1449
1450
static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1451
{
1452
DWORD dwPos;
1453
DWORD nStream;
1454
1455
/* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1456
dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1457
1458
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1459
IAVIStreamImpl *pStream = This->ppStreams[nStream];
1460
1461
/* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1462
dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1463
dwPos += ((pStream->cbFormat + 1) & ~1U);
1464
if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1465
dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1466
if (pStream->sInfo.szName[0])
1467
dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1468
}
1469
1470
if (This->dwMoviChunkPos == 0) {
1471
This->dwNextFramePos = dwPos;
1472
1473
/* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1474
if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1475
This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1476
1477
This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1478
}
1479
1480
return dwPos;
1481
}
1482
1483
static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1484
{
1485
IAVIStreamImpl *pstream;
1486
1487
/* pre-conditions */
1488
assert(paf != NULL);
1489
assert(nr < MAX_AVISTREAMS);
1490
assert(paf->ppStreams[nr] != NULL);
1491
1492
pstream = paf->ppStreams[nr];
1493
1494
pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1495
pstream->ref = 0;
1496
pstream->paf = paf;
1497
pstream->nStream = nr;
1498
pstream->dwCurrentFrame = (DWORD)-1;
1499
pstream->lLastFrame = -1;
1500
1501
if (asi != NULL) {
1502
memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1503
1504
if (asi->dwLength > 0) {
1505
/* pre-allocate mem for frame-index structure */
1506
pstream->idxFrames = calloc(asi->dwLength, sizeof(AVIINDEXENTRY));
1507
if (pstream->idxFrames != NULL)
1508
pstream->nIdxFrames = asi->dwLength;
1509
}
1510
if (asi->dwFormatChangeCount > 0) {
1511
/* pre-allocate mem for formatchange-index structure */
1512
pstream->idxFmtChanges = calloc(asi->dwFormatChangeCount, sizeof(AVIINDEXENTRY));
1513
if (pstream->idxFmtChanges != NULL)
1514
pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1515
}
1516
1517
/* These values will be computed */
1518
pstream->sInfo.dwLength = 0;
1519
pstream->sInfo.dwSuggestedBufferSize = 0;
1520
pstream->sInfo.dwFormatChangeCount = 0;
1521
pstream->sInfo.dwEditCount = 1;
1522
if (pstream->sInfo.dwSampleSize > 0)
1523
SetRectEmpty(&pstream->sInfo.rcFrame);
1524
}
1525
1526
pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1527
}
1528
1529
static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1530
{
1531
/* pre-conditions */
1532
assert(This != NULL);
1533
1534
This->dwCurrentFrame = (DWORD)-1;
1535
This->lLastFrame = -1;
1536
This->paf = NULL;
1537
if (This->idxFrames != NULL) {
1538
free(This->idxFrames);
1539
This->idxFrames = NULL;
1540
This->nIdxFrames = 0;
1541
}
1542
free(This->idxFmtChanges);
1543
This->idxFmtChanges = NULL;
1544
if (This->lpBuffer != NULL) {
1545
free(This->lpBuffer);
1546
This->lpBuffer = NULL;
1547
This->cbBuffer = 0;
1548
}
1549
if (This->lpHandlerData != NULL) {
1550
free(This->lpHandlerData);
1551
This->lpHandlerData = NULL;
1552
This->cbHandlerData = 0;
1553
}
1554
if (This->extra.lp != NULL) {
1555
free(This->extra.lp);
1556
This->extra.lp = NULL;
1557
This->extra.cb = 0;
1558
}
1559
if (This->lpFormat != NULL) {
1560
free(This->lpFormat);
1561
This->lpFormat = NULL;
1562
This->cbFormat = 0;
1563
}
1564
}
1565
1566
static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1567
{
1568
MainAVIHeader MainAVIHdr;
1569
MMCKINFO ckRIFF;
1570
MMCKINFO ckLIST1;
1571
MMCKINFO ckLIST2;
1572
MMCKINFO ck;
1573
IAVIStreamImpl *pStream;
1574
DWORD nStream;
1575
HRESULT hr;
1576
1577
if (This->hmmio == NULL)
1578
return AVIERR_FILEOPEN;
1579
1580
/* initialize stream ptr's */
1581
memset(This->ppStreams, 0, sizeof(This->ppStreams));
1582
1583
/* try to get "RIFF" chunk -- must not be at beginning of file! */
1584
ckRIFF.fccType = formtypeAVI;
1585
if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1586
ERR(": not an AVI!\n");
1587
return AVIERR_FILEREAD;
1588
}
1589
1590
/* get "LIST" "hdrl" */
1591
ckLIST1.fccType = listtypeAVIHEADER;
1592
hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1593
if (FAILED(hr))
1594
return hr;
1595
1596
/* get "avih" chunk */
1597
ck.ckid = ckidAVIMAINHDR;
1598
hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1599
if (FAILED(hr))
1600
return hr;
1601
1602
if (ck.cksize != sizeof(MainAVIHdr)) {
1603
ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize);
1604
return AVIERR_BADFORMAT;
1605
}
1606
if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1607
return AVIERR_FILEREAD;
1608
1609
/* check for MAX_AVISTREAMS limit */
1610
if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1611
WARN("file contains %lu streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1612
return AVIERR_UNSUPPORTED;
1613
}
1614
1615
/* adjust permissions if copyrighted material in file */
1616
if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1617
This->uMode &= ~MMIO_RWMODE;
1618
This->uMode |= MMIO_READ;
1619
}
1620
1621
/* convert MainAVIHeader into AVIFILINFOW */
1622
memset(&This->fInfo, 0, sizeof(This->fInfo));
1623
This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1624
This->fInfo.dwScale = 1000000;
1625
This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1626
This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1627
This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1628
This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1629
This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1630
This->fInfo.dwSuggestedBufferSize = 0;
1631
This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1632
This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1633
LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1634
ARRAY_SIZE(This->fInfo.szFileType));
1635
1636
/* go back to into header list */
1637
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1638
return AVIERR_FILEREAD;
1639
1640
/* foreach stream exists a "LIST","strl" chunk */
1641
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1642
/* get next nested chunk in this "LIST","strl" */
1643
if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1644
return AVIERR_FILEREAD;
1645
1646
/* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1647
if (ckLIST2.ckid == FOURCC_LIST &&
1648
ckLIST2.fccType == listtypeSTREAMHEADER) {
1649
pStream = This->ppStreams[nStream] = calloc(1, sizeof(IAVIStreamImpl));
1650
if (pStream == NULL)
1651
return AVIERR_MEMORY;
1652
AVIFILE_ConstructAVIStream(This, nStream, NULL);
1653
1654
ck.ckid = 0;
1655
while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1656
switch (ck.ckid) {
1657
case ckidSTREAMHANDLERDATA:
1658
if (pStream->lpHandlerData != NULL)
1659
return AVIERR_BADFORMAT;
1660
pStream->lpHandlerData = malloc(ck.cksize);
1661
if (pStream->lpHandlerData == NULL)
1662
return AVIERR_MEMORY;
1663
pStream->cbHandlerData = ck.cksize;
1664
1665
if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1666
return AVIERR_FILEREAD;
1667
break;
1668
case ckidSTREAMFORMAT:
1669
if (pStream->lpFormat != NULL)
1670
return AVIERR_BADFORMAT;
1671
if (ck.cksize == 0)
1672
break;
1673
1674
pStream->lpFormat = malloc(ck.cksize);
1675
if (pStream->lpFormat == NULL)
1676
return AVIERR_MEMORY;
1677
pStream->cbFormat = ck.cksize;
1678
1679
if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1680
return AVIERR_FILEREAD;
1681
1682
if (pStream->sInfo.fccType == streamtypeVIDEO) {
1683
LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1684
1685
/* some corrections to the video format */
1686
if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1687
lpbi->biClrUsed = 1u << lpbi->biBitCount;
1688
if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1689
lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1690
if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1691
if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1692
pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1693
lpbi->biCompression = BI_RLE8;
1694
}
1695
if (lpbi->biCompression == BI_RGB &&
1696
(pStream->sInfo.fccHandler == 0 ||
1697
pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1698
pStream->sInfo.fccHandler = comptypeDIB;
1699
1700
/* init rcFrame if it's empty */
1701
if (IsRectEmpty(&pStream->sInfo.rcFrame))
1702
SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1703
}
1704
break;
1705
case ckidSTREAMHEADER:
1706
{
1707
AVIStreamHeader streamHdr;
1708
WCHAR szType[25];
1709
UINT count;
1710
LONG n = ck.cksize;
1711
1712
if (ck.cksize > sizeof(streamHdr))
1713
n = sizeof(streamHdr);
1714
1715
if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1716
return AVIERR_FILEREAD;
1717
1718
pStream->sInfo.fccType = streamHdr.fccType;
1719
pStream->sInfo.fccHandler = streamHdr.fccHandler;
1720
pStream->sInfo.dwFlags = streamHdr.dwFlags;
1721
pStream->sInfo.wPriority = streamHdr.wPriority;
1722
pStream->sInfo.wLanguage = streamHdr.wLanguage;
1723
pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1724
pStream->sInfo.dwScale = streamHdr.dwScale;
1725
pStream->sInfo.dwRate = streamHdr.dwRate;
1726
pStream->sInfo.dwStart = streamHdr.dwStart;
1727
pStream->sInfo.dwLength = streamHdr.dwLength;
1728
pStream->sInfo.dwSuggestedBufferSize = 0;
1729
pStream->sInfo.dwQuality = streamHdr.dwQuality;
1730
pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1731
pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1732
pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1733
pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1734
pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1735
pStream->sInfo.dwEditCount = 0;
1736
pStream->sInfo.dwFormatChangeCount = 0;
1737
1738
/* generate description for stream like "filename.avi Type #n" */
1739
if (streamHdr.fccType == streamtypeVIDEO)
1740
LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, ARRAY_SIZE(szType));
1741
else if (streamHdr.fccType == streamtypeAUDIO)
1742
LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, ARRAY_SIZE(szType));
1743
else
1744
wsprintfW(szType, L"%4.4hs", (char*)&streamHdr.fccType);
1745
1746
/* get count of this streamtype up to this stream */
1747
count = 0;
1748
for (n = nStream; 0 <= n; n--) {
1749
if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1750
count++;
1751
}
1752
1753
memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1754
1755
/* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1756
wsprintfW(pStream->sInfo.szName, L"%s %s #%d",
1757
AVIFILE_BasenameW(This->szFileName), szType, count);
1758
}
1759
break;
1760
case ckidSTREAMNAME:
1761
{ /* streamname will be saved as ASCII string */
1762
char *str = malloc(ck.cksize);
1763
if (str == NULL)
1764
return AVIERR_MEMORY;
1765
1766
if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1767
{
1768
free(str);
1769
return AVIERR_FILEREAD;
1770
}
1771
1772
MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1773
ARRAY_SIZE(pStream->sInfo.szName));
1774
1775
free(str);
1776
}
1777
break;
1778
case ckidAVIPADDING:
1779
case mmioFOURCC('p','a','d','d'):
1780
break;
1781
default:
1782
WARN(": found extra chunk 0x%08lX\n", ck.ckid);
1783
hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1784
if (FAILED(hr))
1785
return hr;
1786
};
1787
if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1788
{
1789
WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1790
pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1791
TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1792
pStream->sInfo.dwScale = 1;
1793
pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1794
}
1795
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1796
return AVIERR_FILEREAD;
1797
}
1798
} else {
1799
/* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1800
hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1801
if (FAILED(hr))
1802
return hr;
1803
}
1804
if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1805
return AVIERR_FILEREAD;
1806
}
1807
1808
/* read any extra headers in "LIST","hdrl" */
1809
FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1810
if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1811
return AVIERR_FILEREAD;
1812
1813
/* search "LIST","movi" chunk in "RIFF","AVI " */
1814
ckLIST1.fccType = listtypeAVIMOVIE;
1815
hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1816
MMIO_FINDLIST);
1817
if (FAILED(hr))
1818
return hr;
1819
1820
This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1821
This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1822
if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1823
return AVIERR_FILEREAD;
1824
1825
/* try to find an index */
1826
ck.ckid = ckidAVINEWINDEX;
1827
hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1828
&ck, &ckRIFF, MMIO_FINDCHUNK);
1829
if (SUCCEEDED(hr) && ck.cksize > 0) {
1830
if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1831
This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1832
}
1833
1834
/* when we haven't found an index or it's bad, then build one
1835
* by parsing 'movi' chunk */
1836
if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1837
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1838
This->ppStreams[nStream]->lLastFrame = -1;
1839
1840
if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1841
ERR(": Oops, can't seek back to 'movi' chunk!\n");
1842
return AVIERR_FILEREAD;
1843
}
1844
1845
/* seek through the 'movi' list until end */
1846
while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1847
if (ck.ckid != FOURCC_LIST) {
1848
if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1849
nStream = StreamFromFOURCC(ck.ckid);
1850
1851
if (nStream > This->fInfo.dwStreams)
1852
return AVIERR_BADFORMAT;
1853
1854
AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1855
ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1856
} else {
1857
nStream = StreamFromFOURCC(ck.ckid);
1858
WARN(": file seems to be truncated!\n");
1859
if (nStream <= This->fInfo.dwStreams &&
1860
This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1861
ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1862
if (ck.cksize != -1) {
1863
ck.cksize -= ck.dwDataOffset;
1864
ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1865
1866
AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1867
ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1868
}
1869
}
1870
}
1871
}
1872
}
1873
}
1874
1875
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1876
{
1877
DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1878
if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1879
This->fInfo.dwSuggestedBufferSize = sugbuf;
1880
}
1881
1882
/* find other chunks */
1883
FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1884
1885
return AVIERR_OK;
1886
}
1887
1888
static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1889
{
1890
AVIINDEXENTRY *lp;
1891
DWORD pos, n;
1892
HRESULT hr = AVIERR_OK;
1893
BOOL bAbsolute = TRUE;
1894
1895
lp = malloc(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1896
if (lp == NULL)
1897
return AVIERR_MEMORY;
1898
1899
/* adjust limits for index tables, so that inserting will be faster */
1900
for (n = 0; n < This->fInfo.dwStreams; n++) {
1901
IAVIStreamImpl *pStream = This->ppStreams[n];
1902
1903
pStream->lLastFrame = -1;
1904
1905
if (pStream->idxFrames != NULL) {
1906
free(pStream->idxFrames);
1907
pStream->idxFrames = NULL;
1908
pStream->nIdxFrames = 0;
1909
}
1910
1911
if (pStream->sInfo.dwSampleSize != 0) {
1912
if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1913
pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1914
} else if (pStream->sInfo.dwSuggestedBufferSize) {
1915
pStream->nIdxFrames =
1916
pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1917
}
1918
} else
1919
pStream->nIdxFrames = pStream->sInfo.dwLength;
1920
1921
pStream->idxFrames = calloc(pStream->nIdxFrames, sizeof(AVIINDEXENTRY));
1922
if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1923
pStream->nIdxFrames = 0;
1924
free(lp);
1925
return AVIERR_MEMORY;
1926
}
1927
}
1928
1929
pos = (DWORD)-1;
1930
while (size != 0) {
1931
LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1932
1933
if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1934
hr = AVIERR_FILEREAD;
1935
break;
1936
}
1937
size -= read;
1938
1939
if (pos == (DWORD)-1)
1940
pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1941
1942
AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1943
pos, &bAbsolute);
1944
}
1945
1946
free(lp);
1947
1948
/* checking ... */
1949
for (n = 0; n < This->fInfo.dwStreams; n++) {
1950
IAVIStreamImpl *pStream = This->ppStreams[n];
1951
1952
if (pStream->sInfo.dwSampleSize == 0 &&
1953
pStream->sInfo.dwLength != pStream->lLastFrame+1)
1954
ERR("stream %lu length mismatch: dwLength=%lu found=%ld\n",
1955
n, pStream->sInfo.dwLength, pStream->lLastFrame);
1956
}
1957
1958
return hr;
1959
}
1960
1961
static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1962
LONG count, DWORD pos, BOOL *bAbsolute)
1963
{
1964
if (lp == NULL)
1965
return AVIERR_BADPARAM;
1966
1967
for (; count > 0; count--, lp++) {
1968
WORD nStream = StreamFromFOURCC(lp->ckid);
1969
1970
if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1971
continue; /* skip these */
1972
1973
if (nStream > This->fInfo.dwStreams)
1974
return AVIERR_BADFORMAT;
1975
1976
/* Video frames can be either indexed in a relative position to the
1977
* "movi" chunk or in a absolute position in the file. If the index
1978
* is relative the frame offset will always be so small that it will
1979
* virtually never reach the "movi" offset so we can detect if the
1980
* video is relative very fast.
1981
*/
1982
if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1983
*bAbsolute = FALSE;
1984
1985
if (!*bAbsolute)
1986
lp->dwChunkOffset += pos; /* make the offset absolute */
1987
1988
if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1989
return AVIERR_MEMORY;
1990
}
1991
1992
return AVIERR_OK;
1993
}
1994
1995
static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1996
LPVOID buffer, DWORD size)
1997
{
1998
/* pre-conditions */
1999
assert(This != NULL);
2000
assert(This->paf != NULL);
2001
assert(This->paf->hmmio != NULL);
2002
if (!This->sInfo.dwSampleSize)
2003
assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2004
assert(pos <= This->lLastFrame);
2005
2006
/* should we read as much as block gives us? */
2007
if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2008
size = This->idxFrames[pos].dwChunkLength;
2009
2010
/* read into out own buffer or given one? */
2011
if (buffer == NULL) {
2012
/* we also read the chunk */
2013
size += 2 * sizeof(DWORD);
2014
2015
/* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2016
if (This->lpBuffer == NULL || This->cbBuffer < size) {
2017
DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2018
void *new_buffer = realloc(This->lpBuffer, maxSize);
2019
if (!new_buffer) return AVIERR_MEMORY;
2020
This->lpBuffer = new_buffer;
2021
This->cbBuffer = maxSize;
2022
}
2023
2024
/* now read the complete chunk into our buffer */
2025
if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2026
return AVIERR_FILEREAD;
2027
if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2028
return AVIERR_FILEREAD;
2029
2030
/* check if it was the correct block which we have read */
2031
if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2032
This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2033
ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset);
2034
ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2035
(char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2036
This->idxFrames[pos].dwChunkLength);
2037
ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2038
(char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2039
return AVIERR_FILEREAD;
2040
}
2041
} else {
2042
if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2043
return AVIERR_FILEREAD;
2044
if (mmioRead(This->paf->hmmio, buffer, size) != size)
2045
return AVIERR_FILEREAD;
2046
}
2047
2048
return AVIERR_OK;
2049
}
2050
2051
static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2052
{
2053
LONG block;
2054
2055
/* pre-conditions */
2056
assert(This != NULL);
2057
assert(pos != NULL);
2058
assert(offset != NULL);
2059
assert(This->sInfo.dwSampleSize != 0);
2060
assert(*pos >= This->sInfo.dwStart);
2061
2062
/* convert start sample to start bytes */
2063
(*offset) = (*pos) - This->sInfo.dwStart;
2064
(*offset) *= This->sInfo.dwSampleSize;
2065
2066
/* convert bytes to block number */
2067
for (block = 0; block <= This->lLastFrame; block++) {
2068
if (This->idxFrames[block].dwChunkLength <= *offset)
2069
(*offset) -= This->idxFrames[block].dwChunkLength;
2070
else
2071
break;
2072
}
2073
2074
*pos = block;
2075
}
2076
2077
static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2078
{
2079
MainAVIHeader MainAVIHdr;
2080
IAVIStreamImpl* pStream;
2081
MMCKINFO ckRIFF;
2082
MMCKINFO ckLIST1;
2083
MMCKINFO ckLIST2;
2084
MMCKINFO ck;
2085
DWORD nStream;
2086
DWORD dwPos;
2087
HRESULT hr;
2088
2089
/* initialize some things */
2090
if (This->dwMoviChunkPos == 0)
2091
AVIFILE_ComputeMoviStart(This);
2092
2093
/* written one record too much? */
2094
if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2095
This->dwNextFramePos -= 3 * sizeof(DWORD);
2096
if (This->nIdxRecords > 0)
2097
This->nIdxRecords--;
2098
}
2099
2100
AVIFILE_UpdateInfo(This);
2101
2102
assert(This->fInfo.dwScale != 0);
2103
2104
memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2105
MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2106
This->fInfo.dwScale);
2107
MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2108
MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2109
MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2110
MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2111
MainAVIHdr.dwInitialFrames = 0;
2112
MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2113
MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2114
MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2115
MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2116
MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2117
2118
/* now begin writing ... */
2119
mmioSeek(This->hmmio, 0, SEEK_SET);
2120
2121
/* RIFF chunk */
2122
ckRIFF.cksize = 0;
2123
ckRIFF.fccType = formtypeAVI;
2124
if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2125
return AVIERR_FILEWRITE;
2126
2127
/* AVI headerlist */
2128
ckLIST1.cksize = 0;
2129
ckLIST1.fccType = listtypeAVIHEADER;
2130
if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2131
return AVIERR_FILEWRITE;
2132
2133
/* MainAVIHeader */
2134
ck.ckid = ckidAVIMAINHDR;
2135
ck.cksize = sizeof(MainAVIHdr);
2136
ck.fccType = 0;
2137
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2138
return AVIERR_FILEWRITE;
2139
if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2140
return AVIERR_FILEWRITE;
2141
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2142
return AVIERR_FILEWRITE;
2143
2144
/* write the headers of each stream into a separate streamheader list */
2145
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2146
AVIStreamHeader strHdr;
2147
2148
pStream = This->ppStreams[nStream];
2149
2150
/* begin the new streamheader list */
2151
ckLIST2.cksize = 0;
2152
ckLIST2.fccType = listtypeSTREAMHEADER;
2153
if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2154
return AVIERR_FILEWRITE;
2155
2156
/* create an AVIStreamHeader from the AVSTREAMINFO */
2157
strHdr.fccType = pStream->sInfo.fccType;
2158
strHdr.fccHandler = pStream->sInfo.fccHandler;
2159
strHdr.dwFlags = pStream->sInfo.dwFlags;
2160
strHdr.wPriority = pStream->sInfo.wPriority;
2161
strHdr.wLanguage = pStream->sInfo.wLanguage;
2162
strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2163
strHdr.dwScale = pStream->sInfo.dwScale;
2164
strHdr.dwRate = pStream->sInfo.dwRate;
2165
strHdr.dwStart = pStream->sInfo.dwStart;
2166
strHdr.dwLength = pStream->sInfo.dwLength;
2167
strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2168
strHdr.dwQuality = pStream->sInfo.dwQuality;
2169
strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2170
strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2171
strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2172
strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2173
strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2174
2175
/* now write the AVIStreamHeader */
2176
ck.ckid = ckidSTREAMHEADER;
2177
ck.cksize = sizeof(strHdr);
2178
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2179
return AVIERR_FILEWRITE;
2180
if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2181
return AVIERR_FILEWRITE;
2182
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2183
return AVIERR_FILEWRITE;
2184
2185
/* ... the hopefully ever present streamformat ... */
2186
ck.ckid = ckidSTREAMFORMAT;
2187
ck.cksize = pStream->cbFormat;
2188
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2189
return AVIERR_FILEWRITE;
2190
if (pStream->lpFormat != NULL && ck.cksize > 0) {
2191
if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2192
return AVIERR_FILEWRITE;
2193
}
2194
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2195
return AVIERR_FILEWRITE;
2196
2197
/* ... some optional existing handler data ... */
2198
if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2199
ck.ckid = ckidSTREAMHANDLERDATA;
2200
ck.cksize = pStream->cbHandlerData;
2201
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2202
return AVIERR_FILEWRITE;
2203
if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2204
return AVIERR_FILEWRITE;
2205
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2206
return AVIERR_FILEWRITE;
2207
}
2208
2209
/* ... some optional additional extra chunk for this stream ... */
2210
if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2211
/* the chunk header(s) are already in the structure */
2212
if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2213
return AVIERR_FILEWRITE;
2214
}
2215
2216
/* ... an optional name for this stream ... */
2217
if (pStream->sInfo.szName[0]) {
2218
LPSTR str;
2219
2220
ck.ckid = ckidSTREAMNAME;
2221
ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2222
if (ck.cksize & 1) /* align */
2223
ck.cksize++;
2224
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2225
return AVIERR_FILEWRITE;
2226
2227
/* the streamname must be saved in ASCII not Unicode */
2228
str = malloc(ck.cksize);
2229
if (str == NULL)
2230
return AVIERR_MEMORY;
2231
WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2232
ck.cksize, NULL, NULL);
2233
2234
if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2235
free(str);
2236
return AVIERR_FILEWRITE;
2237
}
2238
2239
free(str);
2240
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2241
return AVIERR_FILEWRITE;
2242
}
2243
2244
/* close streamheader list for this stream */
2245
if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2246
return AVIERR_FILEWRITE;
2247
} /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2248
2249
/* close the aviheader list */
2250
if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2251
return AVIERR_FILEWRITE;
2252
2253
/* check for padding to pre-guessed 'movi'-chunk position */
2254
dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2255
if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2256
ck.ckid = ckidAVIPADDING;
2257
ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2258
assert((LONG)ck.cksize >= 0);
2259
2260
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2261
return AVIERR_FILEWRITE;
2262
if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2263
return AVIERR_FILEWRITE;
2264
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2265
return AVIERR_FILEWRITE;
2266
}
2267
2268
/* now write the 'movi' chunk */
2269
mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2270
ckLIST1.cksize = 0;
2271
ckLIST1.fccType = listtypeAVIMOVIE;
2272
if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2273
return AVIERR_FILEWRITE;
2274
if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2275
return AVIERR_FILEWRITE;
2276
if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2277
return AVIERR_FILEWRITE;
2278
2279
/* write 'idx1' chunk */
2280
hr = AVIFILE_SaveIndex(This);
2281
if (FAILED(hr))
2282
return hr;
2283
2284
/* write optional extra file chunks */
2285
if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2286
/* as for the streams, are the chunk header(s) in the structure */
2287
if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2288
return AVIERR_FILEWRITE;
2289
}
2290
2291
/* close RIFF chunk */
2292
if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2293
return AVIERR_FILEWRITE;
2294
2295
/* add some JUNK at end for bad parsers */
2296
memset(&ckRIFF, 0, sizeof(ckRIFF));
2297
mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2298
mmioFlush(This->hmmio, 0);
2299
2300
return AVIERR_OK;
2301
}
2302
2303
static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2304
{
2305
IAVIStreamImpl *pStream;
2306
AVIINDEXENTRY idx;
2307
MMCKINFO ck;
2308
DWORD nStream;
2309
LONG n;
2310
2311
ck.ckid = ckidAVINEWINDEX;
2312
ck.cksize = 0;
2313
if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2314
return AVIERR_FILEWRITE;
2315
2316
if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2317
/* is interleaved -- write block of corresponding frames */
2318
LONG lInitialFrames = 0;
2319
LONG stepsize;
2320
LONG i;
2321
2322
if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2323
stepsize = 1;
2324
else
2325
stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000);
2326
2327
assert(stepsize > 0);
2328
2329
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2330
if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2331
lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2332
}
2333
2334
for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2335
i += stepsize) {
2336
DWORD nFrame = lInitialFrames + i;
2337
2338
assert(nFrame < This->nIdxRecords);
2339
2340
idx.ckid = listtypeAVIRECORD;
2341
idx.dwFlags = AVIIF_LIST;
2342
idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2343
idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2344
- This->dwMoviChunkPos;
2345
if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2346
return AVIERR_FILEWRITE;
2347
2348
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2349
pStream = This->ppStreams[nStream];
2350
2351
/* heave we reached start of this stream? */
2352
if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2353
continue;
2354
2355
if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2356
nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2357
2358
/* reached end of this stream? */
2359
if (pStream->lLastFrame <= nFrame)
2360
continue;
2361
2362
if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2363
pStream->sInfo.dwFormatChangeCount != 0 &&
2364
pStream->idxFmtChanges != NULL) {
2365
DWORD pos;
2366
2367
for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2368
if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2369
idx.dwFlags = AVIIF_NOTIME;
2370
idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2371
idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2372
idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2373
- This->dwMoviChunkPos;
2374
2375
if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2376
return AVIERR_FILEWRITE;
2377
break;
2378
}
2379
}
2380
} /* if have formatchanges */
2381
2382
idx.ckid = pStream->idxFrames[nFrame].ckid;
2383
idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2384
idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2385
idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2386
- This->dwMoviChunkPos;
2387
if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2388
return AVIERR_FILEWRITE;
2389
}
2390
}
2391
} else {
2392
/* not interleaved -- write index for each stream at once */
2393
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2394
pStream = This->ppStreams[nStream];
2395
2396
for (n = 0; n <= pStream->lLastFrame; n++) {
2397
if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2398
(pStream->sInfo.dwFormatChangeCount != 0)) {
2399
DWORD pos;
2400
2401
for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2402
if (pStream->idxFmtChanges[pos].ckid == n) {
2403
idx.dwFlags = AVIIF_NOTIME;
2404
idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2405
idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2406
idx.dwChunkOffset =
2407
pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2408
if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2409
return AVIERR_FILEWRITE;
2410
break;
2411
}
2412
}
2413
} /* if have formatchanges */
2414
2415
idx.ckid = pStream->idxFrames[n].ckid;
2416
idx.dwFlags = pStream->idxFrames[n].dwFlags;
2417
idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2418
idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2419
- This->dwMoviChunkPos;
2420
2421
if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2422
return AVIERR_FILEWRITE;
2423
}
2424
}
2425
} /* if not interleaved */
2426
2427
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2428
return AVIERR_FILEWRITE;
2429
2430
return AVIERR_OK;
2431
}
2432
2433
static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2434
{
2435
UINT i;
2436
UINT nStream;
2437
2438
/* pre-condition */
2439
assert(lSkip >= 0);
2440
2441
if (fcc != 0) {
2442
/* search the number of the specified stream */
2443
nStream = (ULONG)-1;
2444
for (i = 0; i < This->fInfo.dwStreams; i++) {
2445
assert(This->ppStreams[i] != NULL);
2446
2447
if (This->ppStreams[i]->sInfo.fccType == fcc) {
2448
if (lSkip == 0) {
2449
nStream = i;
2450
break;
2451
} else
2452
lSkip--;
2453
}
2454
}
2455
} else
2456
nStream = lSkip;
2457
2458
return nStream;
2459
}
2460
2461
static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2462
{
2463
UINT i;
2464
2465
/* pre-conditions */
2466
assert(This != NULL);
2467
2468
This->fInfo.dwMaxBytesPerSec = 0;
2469
This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2470
This->fInfo.dwSuggestedBufferSize = 0;
2471
This->fInfo.dwWidth = 0;
2472
This->fInfo.dwHeight = 0;
2473
This->fInfo.dwScale = 0;
2474
This->fInfo.dwRate = 0;
2475
This->fInfo.dwLength = 0;
2476
This->dwInitialFrames = 0;
2477
2478
for (i = 0; i < This->fInfo.dwStreams; i++) {
2479
AVISTREAMINFOW *psi;
2480
DWORD n;
2481
2482
/* pre-conditions */
2483
assert(This->ppStreams[i] != NULL);
2484
2485
psi = &This->ppStreams[i]->sInfo;
2486
assert(psi->dwScale != 0);
2487
assert(psi->dwRate != 0);
2488
2489
if (i == 0) {
2490
/* use first stream timings as base */
2491
This->fInfo.dwScale = psi->dwScale;
2492
This->fInfo.dwRate = psi->dwRate;
2493
This->fInfo.dwLength = psi->dwLength;
2494
} else {
2495
n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface,
2496
&This->ppStreams[i]->IAVIStream_iface, psi->dwLength);
2497
if (This->fInfo.dwLength < n)
2498
This->fInfo.dwLength = n;
2499
}
2500
2501
if (This->dwInitialFrames < psi->dwInitialFrames)
2502
This->dwInitialFrames = psi->dwInitialFrames;
2503
2504
if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2505
This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2506
2507
if (psi->dwSampleSize != 0) {
2508
/* fixed sample size -- exact computation */
2509
This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2510
psi->dwScale);
2511
} else {
2512
/* variable sample size -- only upper limit */
2513
This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2514
psi->dwRate, psi->dwScale);
2515
2516
/* update dimensions */
2517
n = psi->rcFrame.right - psi->rcFrame.left;
2518
if (This->fInfo.dwWidth < n)
2519
This->fInfo.dwWidth = n;
2520
n = psi->rcFrame.bottom - psi->rcFrame.top;
2521
if (This->fInfo.dwHeight < n)
2522
This->fInfo.dwHeight = n;
2523
}
2524
}
2525
}
2526
2527
static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2528
FOURCC ckid, DWORD flags, LPCVOID buffer,
2529
LONG size)
2530
{
2531
MMCKINFO ck;
2532
2533
ck.ckid = ckid;
2534
ck.cksize = size;
2535
ck.fccType = 0;
2536
2537
/* if no frame/block is already written, we must compute start of movi chunk */
2538
if (This->paf->dwMoviChunkPos == 0)
2539
AVIFILE_ComputeMoviStart(This->paf);
2540
2541
if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2542
return AVIERR_FILEWRITE;
2543
2544
if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2545
return AVIERR_FILEWRITE;
2546
if (buffer != NULL && size > 0) {
2547
if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2548
return AVIERR_FILEWRITE;
2549
}
2550
if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2551
return AVIERR_FILEWRITE;
2552
2553
This->paf->fDirty = TRUE;
2554
This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2555
2556
return AVIFILE_AddFrame(This, ckid, size,
2557
ck.dwDataOffset - 2 * sizeof(DWORD), flags);
2558
}
2559
2560