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