f1f65091d3a56c37aa597ce8070255086cef24ce
[reactos.git] / reactos / 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 This->fInfo.dwStreams--;
450 if (nStream < This->fInfo.dwStreams)
451 memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1],
452 (This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0]));
453
454 This->ppStreams[This->fInfo.dwStreams] = NULL;
455 This->fDirty = TRUE;
456
457 /* This->fInfo will be updated further when asked for */
458 return AVIERR_OK;
459 } else
460 return AVIERR_NODATA;
461 }
462
463 static const struct IAVIFileVtbl avif_vt = {
464 IAVIFile_fnQueryInterface,
465 IAVIFile_fnAddRef,
466 IAVIFile_fnRelease,
467 IAVIFile_fnInfo,
468 IAVIFile_fnGetStream,
469 IAVIFile_fnCreateStream,
470 IAVIFile_fnWriteData,
471 IAVIFile_fnReadData,
472 IAVIFile_fnEndRecord,
473 IAVIFile_fnDeleteStream
474 };
475
476
477 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
478 {
479 IAVIFileImpl *This = impl_from_IPersistFile(iface);
480
481 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
482 }
483
484 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
485 {
486 IAVIFileImpl *This = impl_from_IPersistFile(iface);
487
488 return IUnknown_AddRef(This->outer_unk);
489 }
490
491 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
492 {
493 IAVIFileImpl *This = impl_from_IPersistFile(iface);
494
495 return IUnknown_Release(This->outer_unk);
496 }
497
498 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
499 {
500 TRACE("(%p,%p)\n", iface, pClassID);
501
502 if (pClassID == NULL)
503 return AVIERR_BADPARAM;
504
505 *pClassID = CLSID_AVIFile;
506
507 return AVIERR_OK;
508 }
509
510 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
511 {
512 IAVIFileImpl *This = impl_from_IPersistFile(iface);
513
514 TRACE("(%p)\n", iface);
515
516 return (This->fDirty ? S_OK : S_FALSE);
517 }
518
519 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
520 {
521 IAVIFileImpl *This = impl_from_IPersistFile(iface);
522 int len;
523
524 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
525
526 /* check parameter */
527 if (pszFileName == NULL)
528 return AVIERR_BADPARAM;
529
530 if (This->hmmio != NULL)
531 return AVIERR_ERROR; /* No reuse of this object for another file! */
532
533 /* remember mode and name */
534 This->uMode = dwMode;
535
536 len = lstrlenW(pszFileName) + 1;
537 This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
538 if (This->szFileName == NULL)
539 return AVIERR_MEMORY;
540 lstrcpyW(This->szFileName, pszFileName);
541
542 /* try to open the file */
543 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
544 if (This->hmmio == NULL) {
545 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
546 LPSTR szFileName;
547
548 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
549 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
550 if (szFileName == NULL)
551 return AVIERR_MEMORY;
552
553 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
554
555 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
556 HeapFree(GetProcessHeap(), 0, szFileName);
557 if (This->hmmio == NULL)
558 return AVIERR_FILEOPEN;
559 }
560
561 /* should we create a new file? */
562 if (dwMode & OF_CREATE) {
563 memset(& This->fInfo, 0, sizeof(This->fInfo));
564 This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
565
566 return AVIERR_OK;
567 } else
568 return AVIFILE_LoadFile(This);
569 }
570
571 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
572 BOOL fRemember)
573 {
574 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
575
576 /* We write directly to disk, so nothing to do. */
577
578 return AVIERR_OK;
579 }
580
581 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
582 {
583 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
584
585 /* We write directly to disk, so nothing to do. */
586
587 return AVIERR_OK;
588 }
589
590 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
591 {
592 IAVIFileImpl *This = impl_from_IPersistFile(iface);
593
594 TRACE("(%p,%p)\n", iface, ppszFileName);
595
596 if (ppszFileName == NULL)
597 return AVIERR_BADPARAM;
598
599 *ppszFileName = NULL;
600
601 if (This->szFileName != NULL) {
602 int len = lstrlenW(This->szFileName) + 1;
603
604 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
605 if (*ppszFileName == NULL)
606 return AVIERR_MEMORY;
607
608 strcpyW(*ppszFileName, This->szFileName);
609 }
610
611 return AVIERR_OK;
612 }
613
614 static const struct IPersistFileVtbl pf_vt = {
615 IPersistFile_fnQueryInterface,
616 IPersistFile_fnAddRef,
617 IPersistFile_fnRelease,
618 IPersistFile_fnGetClassID,
619 IPersistFile_fnIsDirty,
620 IPersistFile_fnLoad,
621 IPersistFile_fnSave,
622 IPersistFile_fnSaveCompleted,
623 IPersistFile_fnGetCurFile
624 };
625
626 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
627 {
628 IAVIFileImpl *obj;
629 HRESULT hr;
630
631 *ppv = NULL;
632 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
633 if (!obj)
634 return AVIERR_MEMORY;
635
636 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
637 obj->IAVIFile_iface.lpVtbl = &avif_vt;
638 obj->IPersistFile_iface.lpVtbl = &pf_vt;
639 obj->ref = 1;
640 if (pUnkOuter)
641 obj->outer_unk = pUnkOuter;
642 else
643 obj->outer_unk = &obj->IUnknown_inner;
644
645 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
646 IUnknown_Release(&obj->IUnknown_inner);
647
648 return hr;
649 }
650
651
652 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
653 {
654 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
655
656 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
657
658 if (!ppv) {
659 WARN("invalid parameter\n");
660 return E_INVALIDARG;
661 }
662 *ppv = NULL;
663
664 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
665 *ppv = iface;
666 IAVIStream_AddRef(iface);
667
668 return S_OK;
669 }
670 /* FIXME: IAVIStreaming interface */
671
672 return E_NOINTERFACE;
673 }
674
675 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
676 {
677 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
678 ULONG ref = InterlockedIncrement(&This->ref);
679
680 TRACE("(%p) ref=%d\n", This, ref);
681
682 /* also add ref to parent, so that it doesn't kill us */
683 if (This->paf != NULL)
684 IAVIFile_AddRef(&This->paf->IAVIFile_iface);
685
686 return ref;
687 }
688
689 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
690 {
691 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
692 ULONG ref = InterlockedDecrement(&This->ref);
693
694 TRACE("(%p) ref=%d\n", This, ref);
695
696 if (This->paf != NULL)
697 IAVIFile_Release(&This->paf->IAVIFile_iface);
698
699 return ref;
700 }
701
702 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
703 {
704 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
705
706 /* This IAVIStream interface needs an AVIFile */
707 return AVIERR_UNSUPPORTED;
708 }
709
710 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
711 {
712 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
713
714 TRACE("(%p,%p,%d)\n", iface, psi, size);
715
716 if (psi == NULL)
717 return AVIERR_BADPARAM;
718 if (size < 0)
719 return AVIERR_BADSIZE;
720
721 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
722
723 if ((DWORD)size < sizeof(This->sInfo))
724 return AVIERR_BUFFERTOOSMALL;
725 return AVIERR_OK;
726 }
727
728 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
729 {
730 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
731 LONG offset = 0;
732
733 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
734
735 if (flags & FIND_FROM_START) {
736 pos = This->sInfo.dwStart;
737 flags &= ~(FIND_FROM_START|FIND_PREV);
738 flags |= FIND_NEXT;
739 }
740
741 if (This->sInfo.dwSampleSize != 0) {
742 /* convert samples into block number with offset */
743 AVIFILE_SamplesToBlock(This, &pos, &offset);
744 }
745
746 if (flags & FIND_TYPE) {
747 if (flags & FIND_KEY) {
748 while (0 <= pos && pos <= This->lLastFrame) {
749 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
750 goto RETURN_FOUND;
751
752 if (flags & FIND_NEXT)
753 pos++;
754 else
755 pos--;
756 };
757 } else if (flags & FIND_ANY) {
758 while (0 <= pos && pos <= This->lLastFrame) {
759 if (This->idxFrames[pos].dwChunkLength > 0)
760 goto RETURN_FOUND;
761
762 if (flags & FIND_NEXT)
763 pos++;
764 else
765 pos--;
766
767 };
768 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
769 This->sInfo.fccType == streamtypeVIDEO) {
770 if (flags & FIND_NEXT) {
771 ULONG n;
772
773 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
774 if (This->idxFmtChanges[n].ckid >= pos) {
775 pos = This->idxFmtChanges[n].ckid;
776 goto RETURN_FOUND;
777 }
778 } else {
779 LONG n;
780
781 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
782 if (This->idxFmtChanges[n].ckid <= pos) {
783 pos = This->idxFmtChanges[n].ckid;
784 goto RETURN_FOUND;
785 }
786 }
787
788 if (pos > (LONG)This->sInfo.dwStart)
789 return 0; /* format changes always for first frame */
790 }
791 }
792
793 return -1;
794 }
795
796 RETURN_FOUND:
797 if (pos < (LONG)This->sInfo.dwStart)
798 return -1;
799
800 switch (flags & FIND_RET) {
801 case FIND_LENGTH:
802 /* physical size */
803 pos = This->idxFrames[pos].dwChunkLength;
804 break;
805 case FIND_OFFSET:
806 /* physical position */
807 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
808 + offset * This->sInfo.dwSampleSize;
809 break;
810 case FIND_SIZE:
811 /* logical size */
812 if (This->sInfo.dwSampleSize)
813 pos = This->sInfo.dwSampleSize;
814 else
815 pos = 1;
816 break;
817 case FIND_INDEX:
818 FIXME(": FIND_INDEX flag is not supported!\n");
819 /* This is an index in the index-table on disc. */
820 break;
821 }; /* else logical position */
822
823 return pos;
824 }
825
826 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
827 LONG *formatsize)
828 {
829 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
830
831 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
832
833 if (formatsize == NULL)
834 return AVIERR_BADPARAM;
835
836 /* only interested in needed buffersize? */
837 if (format == NULL || *formatsize <= 0) {
838 *formatsize = This->cbFormat;
839
840 return AVIERR_OK;
841 }
842
843 /* copy initial format (only as much as will fit) */
844 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
845 if (*(DWORD*)formatsize < This->cbFormat) {
846 *formatsize = This->cbFormat;
847 return AVIERR_BUFFERTOOSMALL;
848 }
849
850 /* Could format change? When yes will it change? */
851 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
852 pos > This->sInfo.dwStart) {
853 LONG lLastFmt;
854
855 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
856 if (lLastFmt > 0) {
857 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
858 }
859 }
860
861 *formatsize = This->cbFormat;
862 return AVIERR_OK;
863 }
864
865 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
866 LONG formatsize)
867 {
868 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
869 BITMAPINFOHEADER *lpbiNew = format;
870
871 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
872
873 /* check parameters */
874 if (format == NULL || formatsize <= 0)
875 return AVIERR_BADPARAM;
876
877 /* Do we have write permission? */
878 if ((This->paf->uMode & MMIO_RWMODE) == 0)
879 return AVIERR_READONLY;
880
881 /* can only set format before frame is written! */
882 if (This->lLastFrame > pos)
883 return AVIERR_UNSUPPORTED;
884
885 /* initial format or a formatchange? */
886 if (This->lpFormat == NULL) {
887 /* initial format */
888 if (This->paf->dwMoviChunkPos != 0)
889 return AVIERR_ERROR; /* user has used API in wrong sequence! */
890
891 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
892 if (This->lpFormat == NULL)
893 return AVIERR_MEMORY;
894 This->cbFormat = formatsize;
895
896 memcpy(This->lpFormat, format, formatsize);
897
898 /* update some infos about stream */
899 if (This->sInfo.fccType == streamtypeVIDEO) {
900 LONG lDim;
901
902 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
903 if (lDim < lpbiNew->biWidth)
904 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
905 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
906 if (lDim < lpbiNew->biHeight)
907 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
908 } else if (This->sInfo.fccType == streamtypeAUDIO)
909 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
910
911 return AVIERR_OK;
912 } else {
913 MMCKINFO ck;
914 LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
915 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
916 AVIPALCHANGE *lppc = NULL;
917 UINT n;
918
919 /* perhaps format change, check it ... */
920 if (This->cbFormat != formatsize)
921 return AVIERR_UNSUPPORTED;
922
923 /* no format change, only the initial one */
924 if (memcmp(This->lpFormat, format, formatsize) == 0)
925 return AVIERR_OK;
926
927 /* check that's only the palette, which changes */
928 if (lpbiOld->biSize != lpbiNew->biSize ||
929 lpbiOld->biWidth != lpbiNew->biWidth ||
930 lpbiOld->biHeight != lpbiNew->biHeight ||
931 lpbiOld->biPlanes != lpbiNew->biPlanes ||
932 lpbiOld->biBitCount != lpbiNew->biBitCount ||
933 lpbiOld->biCompression != lpbiNew->biCompression ||
934 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
935 return AVIERR_UNSUPPORTED;
936
937 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
938
939 /* simply say all colors have changed */
940 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
941 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
942 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
943 if (lppc == NULL)
944 return AVIERR_MEMORY;
945
946 lppc->bFirstEntry = 0;
947 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
948 lppc->wFlags = 0;
949 for (n = 0; n < lpbiOld->biClrUsed; n++) {
950 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
951 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
952 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
953 lppc->peNew[n].peFlags = 0;
954 }
955
956 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
957 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
958 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
959 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
960 {
961 HeapFree(GetProcessHeap(), 0, lppc);
962 return AVIERR_FILEWRITE;
963 }
964
965 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
966
967 HeapFree(GetProcessHeap(), 0, lppc);
968
969 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
970 }
971 }
972
973 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
974 LONG buffersize, LONG *bytesread, LONG *samplesread)
975 {
976 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
977 DWORD size;
978 HRESULT hr;
979
980 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
981 buffersize, bytesread, samplesread);
982
983 /* clear return parameters if given */
984 if (bytesread != NULL)
985 *bytesread = 0;
986 if (samplesread != NULL)
987 *samplesread = 0;
988
989 /* check parameters */
990 if ((LONG)This->sInfo.dwStart > start)
991 return AVIERR_NODATA; /* couldn't read before start of stream */
992 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
993 return AVIERR_NODATA; /* start is past end of stream */
994
995 /* should we read as much as possible? */
996 if (samples == -1) {
997 /* User should know how much we have read */
998 if (bytesread == NULL && samplesread == NULL)
999 return AVIERR_BADPARAM;
1000
1001 if (This->sInfo.dwSampleSize != 0)
1002 samples = buffersize / This->sInfo.dwSampleSize;
1003 else
1004 samples = 1;
1005 }
1006
1007 /* limit to end of stream */
1008 if ((LONG)This->sInfo.dwLength < samples)
1009 samples = This->sInfo.dwLength;
1010 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1011 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1012
1013 /* nothing to read? Then leave ... */
1014 if (samples == 0)
1015 return AVIERR_OK;
1016
1017 if (This->sInfo.dwSampleSize != 0) {
1018 /* fixed samplesize -- we can read over frame/block boundaries */
1019 LONG block = start;
1020 LONG offset = 0;
1021
1022 if (!buffer)
1023 {
1024 if (bytesread)
1025 *bytesread = samples*This->sInfo.dwSampleSize;
1026 if (samplesread)
1027 *samplesread = samples;
1028 return AVIERR_OK;
1029 }
1030
1031 /* convert start sample to block,offset pair */
1032 AVIFILE_SamplesToBlock(This, &block, &offset);
1033
1034 /* convert samples to bytes */
1035 samples *= This->sInfo.dwSampleSize;
1036
1037 while (samples > 0 && buffersize > 0) {
1038 LONG blocksize;
1039 if (block != This->dwCurrentFrame) {
1040 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1041 if (FAILED(hr))
1042 return hr;
1043 }
1044
1045 size = min((DWORD)samples, (DWORD)buffersize);
1046 blocksize = This->lpBuffer[1];
1047 TRACE("blocksize = %u\n",blocksize);
1048 size = min(size, blocksize - offset);
1049 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1050
1051 block++;
1052 offset = 0;
1053 buffer = ((LPBYTE)buffer)+size;
1054 samples -= size;
1055 buffersize -= size;
1056
1057 /* fill out return parameters if given */
1058 if (bytesread != NULL)
1059 *bytesread += size;
1060 if (samplesread != NULL)
1061 *samplesread += size / This->sInfo.dwSampleSize;
1062 }
1063
1064 if (samples == 0)
1065 return AVIERR_OK;
1066 else
1067 return AVIERR_BUFFERTOOSMALL;
1068 } else {
1069 /* variable samplesize -- we can only read one full frame/block */
1070 if (samples > 1)
1071 samples = 1;
1072
1073 assert(start <= This->lLastFrame);
1074 size = This->idxFrames[start].dwChunkLength;
1075 if (buffer != NULL && buffersize >= size) {
1076 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1077 if (FAILED(hr))
1078 return hr;
1079 } else if (buffer != NULL)
1080 return AVIERR_BUFFERTOOSMALL;
1081
1082 /* fill out return parameters if given */
1083 if (bytesread != NULL)
1084 *bytesread = size;
1085 if (samplesread != NULL)
1086 *samplesread = samples;
1087
1088 return AVIERR_OK;
1089 }
1090 }
1091
1092 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
1093 LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1094 {
1095 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1096 FOURCC ckid;
1097 HRESULT hr;
1098
1099 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1100 buffer, buffersize, flags, sampwritten, byteswritten);
1101
1102 /* clear return parameters if given */
1103 if (sampwritten != NULL)
1104 *sampwritten = 0;
1105 if (byteswritten != NULL)
1106 *byteswritten = 0;
1107
1108 /* check parameters */
1109 if (buffer == NULL && (buffersize > 0 || samples > 0))
1110 return AVIERR_BADPARAM;
1111
1112 /* Have we write permission? */
1113 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1114 return AVIERR_READONLY;
1115
1116 switch (This->sInfo.fccType) {
1117 case streamtypeAUDIO:
1118 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1119 break;
1120 default:
1121 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1122 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1123 else
1124 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1125 break;
1126 };
1127
1128 /* append to end of stream? */
1129 if (start == -1) {
1130 if (This->lLastFrame == -1)
1131 start = This->sInfo.dwStart;
1132 else
1133 start = This->sInfo.dwLength;
1134 } else if (This->lLastFrame == -1)
1135 This->sInfo.dwStart = start;
1136
1137 if (This->sInfo.dwSampleSize != 0) {
1138 /* fixed sample size -- audio like */
1139 if (samples * This->sInfo.dwSampleSize != buffersize)
1140 return AVIERR_BADPARAM;
1141
1142 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1143 if (This->sInfo.dwLength != start)
1144 return AVIERR_UNSUPPORTED;
1145
1146 /* Convert position to frame/block */
1147 start = This->lLastFrame + 1;
1148
1149 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1150 FIXME(": not interleaved, could collect audio data!\n");
1151 }
1152 } else {
1153 /* variable sample size -- video like */
1154 if (samples > 1)
1155 return AVIERR_UNSUPPORTED;
1156
1157 /* must we fill up with empty frames? */
1158 if (This->lLastFrame != -1) {
1159 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1160
1161 while (start > This->lLastFrame + 1) {
1162 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1163 if (FAILED(hr))
1164 return hr;
1165 }
1166 }
1167 }
1168
1169 /* write the block now */
1170 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1171 if (SUCCEEDED(hr)) {
1172 /* fill out return parameters if given */
1173 if (sampwritten != NULL)
1174 *sampwritten = samples;
1175 if (byteswritten != NULL)
1176 *byteswritten = buffersize;
1177 }
1178
1179 return hr;
1180 }
1181
1182 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1183 {
1184 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1185
1186 FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1187
1188 /* check parameters */
1189 if (start < 0 || samples < 0)
1190 return AVIERR_BADPARAM;
1191
1192 /* Delete before start of stream? */
1193 if (start + samples < This->sInfo.dwStart)
1194 return AVIERR_OK;
1195
1196 /* Delete after end of stream? */
1197 if (start > This->sInfo.dwLength)
1198 return AVIERR_OK;
1199
1200 /* For the rest we need write permissions */
1201 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1202 return AVIERR_READONLY;
1203
1204 /* 1. overwrite the data with JUNK
1205 *
1206 * if ISINTERLEAVED {
1207 * 2. concat all neighboured JUNK-blocks in this record to one
1208 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1209 * to start of this record, repeat this.
1210 * } else {
1211 * 2. concat all neighboured JUNK-blocks.
1212 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1213 * start of this block.
1214 * }
1215 */
1216
1217 return AVIERR_UNSUPPORTED;
1218 }
1219
1220 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1221 {
1222 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1223
1224 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1225
1226 if (fcc == ckidSTREAMHANDLERDATA) {
1227 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1228 if (lp == NULL || *lpread <= 0) {
1229 *lpread = This->cbHandlerData;
1230 return AVIERR_OK;
1231 }
1232
1233 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1234 if (*lpread < This->cbHandlerData)
1235 return AVIERR_BUFFERTOOSMALL;
1236 return AVIERR_OK;
1237 } else
1238 return AVIERR_NODATA;
1239 } else
1240 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1241 }
1242
1243 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1244 {
1245 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1246
1247 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1248
1249 /* check parameters */
1250 if (lp == NULL)
1251 return AVIERR_BADPARAM;
1252 if (size <= 0)
1253 return AVIERR_BADSIZE;
1254
1255 /* need write permission */
1256 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1257 return AVIERR_READONLY;
1258
1259 /* already written something to this file? */
1260 if (This->paf->dwMoviChunkPos != 0) {
1261 /* the data will be inserted before the 'movi' chunk, so check for
1262 * enough space */
1263 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1264
1265 /* ckid,size => 2 * sizeof(DWORD) */
1266 dwPos += 2 * sizeof(DWORD) + size;
1267 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1268 return AVIERR_UNSUPPORTED; /* not enough space left */
1269 }
1270
1271 This->paf->fDirty = TRUE;
1272
1273 if (fcc == ckidSTREAMHANDLERDATA) {
1274 if (This->lpHandlerData != NULL) {
1275 FIXME(": handler data already set -- overwrite?\n");
1276 return AVIERR_UNSUPPORTED;
1277 }
1278
1279 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1280 if (This->lpHandlerData == NULL)
1281 return AVIERR_MEMORY;
1282 This->cbHandlerData = size;
1283 memcpy(This->lpHandlerData, lp, size);
1284
1285 return AVIERR_OK;
1286 } else
1287 return WriteExtraChunk(&This->extra, fcc, lp, size);
1288 }
1289
1290 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1291 {
1292 FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1293
1294 return E_FAIL;
1295 }
1296
1297 static const struct IAVIStreamVtbl avist_vt = {
1298 IAVIStream_fnQueryInterface,
1299 IAVIStream_fnAddRef,
1300 IAVIStream_fnRelease,
1301 IAVIStream_fnCreate,
1302 IAVIStream_fnInfo,
1303 IAVIStream_fnFindSample,
1304 IAVIStream_fnReadFormat,
1305 IAVIStream_fnSetFormat,
1306 IAVIStream_fnRead,
1307 IAVIStream_fnWrite,
1308 IAVIStream_fnDelete,
1309 IAVIStream_fnReadData,
1310 IAVIStream_fnWriteData,
1311 IAVIStream_fnSetInfo
1312 };
1313
1314
1315 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1316 {
1317 UINT n;
1318
1319 /* pre-conditions */
1320 assert(This != NULL);
1321
1322 switch (TWOCCFromFOURCC(ckid)) {
1323 case cktypeDIBbits:
1324 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1325 flags |= AVIIF_KEYFRAME;
1326 break;
1327 case cktypeDIBcompressed:
1328 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1329 flags &= ~AVIIF_KEYFRAME;
1330 break;
1331 case cktypePALchange:
1332 if (This->sInfo.fccType != streamtypeVIDEO) {
1333 ERR(": found palette change in non-video stream!\n");
1334 return AVIERR_BADFORMAT;
1335 }
1336
1337 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1338 DWORD new_count = This->nIdxFmtChanges + 16;
1339 void *new_buffer;
1340
1341 if (This->idxFmtChanges == NULL) {
1342 This->idxFmtChanges =
1343 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
1344 if (!This->idxFmtChanges) return AVIERR_MEMORY;
1345 } else {
1346 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
1347 new_count * sizeof(AVIINDEXENTRY));
1348 if (!new_buffer) return AVIERR_MEMORY;
1349 This->idxFmtChanges = new_buffer;
1350 }
1351 This->nIdxFmtChanges = new_count;
1352 }
1353
1354 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1355 n = ++This->sInfo.dwFormatChangeCount;
1356 This->idxFmtChanges[n].ckid = This->lLastFrame;
1357 This->idxFmtChanges[n].dwFlags = 0;
1358 This->idxFmtChanges[n].dwChunkOffset = offset;
1359 This->idxFmtChanges[n].dwChunkLength = size;
1360
1361 return AVIERR_OK;
1362 case cktypeWAVEbytes:
1363 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1364 flags |= AVIIF_KEYFRAME;
1365 break;
1366 default:
1367 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1368 break;
1369 };
1370
1371 /* first frame is always a keyframe */
1372 if (This->lLastFrame == -1)
1373 flags |= AVIIF_KEYFRAME;
1374
1375 if (This->sInfo.dwSuggestedBufferSize < size)
1376 This->sInfo.dwSuggestedBufferSize = size;
1377
1378 /* get memory for index */
1379 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1380 This->nIdxFrames += 512;
1381 if (This->idxFrames == NULL)
1382 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1383 else
1384 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1385 This->nIdxFrames * sizeof(AVIINDEXENTRY));
1386 if (This->idxFrames == NULL)
1387 return AVIERR_MEMORY;
1388 }
1389
1390 This->lLastFrame++;
1391 This->idxFrames[This->lLastFrame].ckid = ckid;
1392 This->idxFrames[This->lLastFrame].dwFlags = flags;
1393 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1394 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1395
1396 /* update AVISTREAMINFO structure if necessary */
1397 if (This->sInfo.dwLength <= This->lLastFrame)
1398 This->sInfo.dwLength = This->lLastFrame + 1;
1399
1400 return AVIERR_OK;
1401 }
1402
1403 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1404 {
1405 /* pre-conditions */
1406 assert(This != NULL && This->ppStreams[0] != NULL);
1407
1408 if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) {
1409 DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY);
1410 void *mem;
1411 if (!This->idxRecords)
1412 mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count);
1413 else
1414 mem = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxRecords, new_count);
1415 if (mem) {
1416 This->cbIdxRecords = new_count;
1417 This->idxRecords = mem;
1418 } else {
1419 HeapFree(GetProcessHeap(), 0, This->idxRecords);
1420 This->idxRecords = NULL;
1421 return AVIERR_MEMORY;
1422 }
1423 }
1424
1425 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1426
1427 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1428 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1429 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1430 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1431 This->idxRecords[This->nIdxRecords].dwChunkLength =
1432 This->ckLastRecord.cksize;
1433 This->nIdxRecords++;
1434
1435 return AVIERR_OK;
1436 }
1437
1438 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1439 {
1440 DWORD dwPos;
1441 DWORD nStream;
1442
1443 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1444 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1445
1446 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1447 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1448
1449 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1450 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1451 dwPos += ((pStream->cbFormat + 1) & ~1U);
1452 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1453 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1454 if (pStream->sInfo.szName[0])
1455 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1456 }
1457
1458 if (This->dwMoviChunkPos == 0) {
1459 This->dwNextFramePos = dwPos;
1460
1461 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1462 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1463 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1464
1465 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1466 }
1467
1468 return dwPos;
1469 }
1470
1471 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1472 {
1473 IAVIStreamImpl *pstream;
1474
1475 /* pre-conditions */
1476 assert(paf != NULL);
1477 assert(nr < MAX_AVISTREAMS);
1478 assert(paf->ppStreams[nr] != NULL);
1479
1480 pstream = paf->ppStreams[nr];
1481
1482 pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1483 pstream->ref = 0;
1484 pstream->paf = paf;
1485 pstream->nStream = nr;
1486 pstream->dwCurrentFrame = (DWORD)-1;
1487 pstream->lLastFrame = -1;
1488
1489 if (asi != NULL) {
1490 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1491
1492 if (asi->dwLength > 0) {
1493 /* pre-allocate mem for frame-index structure */
1494 pstream->idxFrames =
1495 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1496 if (pstream->idxFrames != NULL)
1497 pstream->nIdxFrames = asi->dwLength;
1498 }
1499 if (asi->dwFormatChangeCount > 0) {
1500 /* pre-allocate mem for formatchange-index structure */
1501 pstream->idxFmtChanges =
1502 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1503 if (pstream->idxFmtChanges != NULL)
1504 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1505 }
1506
1507 /* These values will be computed */
1508 pstream->sInfo.dwLength = 0;
1509 pstream->sInfo.dwSuggestedBufferSize = 0;
1510 pstream->sInfo.dwFormatChangeCount = 0;
1511 pstream->sInfo.dwEditCount = 1;
1512 if (pstream->sInfo.dwSampleSize > 0)
1513 SetRectEmpty(&pstream->sInfo.rcFrame);
1514 }
1515
1516 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1517 }
1518
1519 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1520 {
1521 /* pre-conditions */
1522 assert(This != NULL);
1523
1524 This->dwCurrentFrame = (DWORD)-1;
1525 This->lLastFrame = -1;
1526 This->paf = NULL;
1527 if (This->idxFrames != NULL) {
1528 HeapFree(GetProcessHeap(), 0, This->idxFrames);
1529 This->idxFrames = NULL;
1530 This->nIdxFrames = 0;
1531 }
1532 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
1533 This->idxFmtChanges = NULL;
1534 if (This->lpBuffer != NULL) {
1535 HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1536 This->lpBuffer = NULL;
1537 This->cbBuffer = 0;
1538 }
1539 if (This->lpHandlerData != NULL) {
1540 HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1541 This->lpHandlerData = NULL;
1542 This->cbHandlerData = 0;
1543 }
1544 if (This->extra.lp != NULL) {
1545 HeapFree(GetProcessHeap(), 0, This->extra.lp);
1546 This->extra.lp = NULL;
1547 This->extra.cb = 0;
1548 }
1549 if (This->lpFormat != NULL) {
1550 HeapFree(GetProcessHeap(), 0, This->lpFormat);
1551 This->lpFormat = NULL;
1552 This->cbFormat = 0;
1553 }
1554 }
1555
1556 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1557 {
1558 MainAVIHeader MainAVIHdr;
1559 MMCKINFO ckRIFF;
1560 MMCKINFO ckLIST1;
1561 MMCKINFO ckLIST2;
1562 MMCKINFO ck;
1563 IAVIStreamImpl *pStream;
1564 DWORD nStream;
1565 HRESULT hr;
1566
1567 if (This->hmmio == NULL)
1568 return AVIERR_FILEOPEN;
1569
1570 /* initialize stream ptr's */
1571 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1572
1573 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1574 ckRIFF.fccType = formtypeAVI;
1575 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1576 ERR(": not an AVI!\n");
1577 return AVIERR_FILEREAD;
1578 }
1579
1580 /* get "LIST" "hdrl" */
1581 ckLIST1.fccType = listtypeAVIHEADER;
1582 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1583 if (FAILED(hr))
1584 return hr;
1585
1586 /* get "avih" chunk */
1587 ck.ckid = ckidAVIMAINHDR;
1588 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1589 if (FAILED(hr))
1590 return hr;
1591
1592 if (ck.cksize != sizeof(MainAVIHdr)) {
1593 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1594 return AVIERR_BADFORMAT;
1595 }
1596 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1597 return AVIERR_FILEREAD;
1598
1599 /* check for MAX_AVISTREAMS limit */
1600 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1601 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1602 return AVIERR_UNSUPPORTED;
1603 }
1604
1605 /* adjust permissions if copyrighted material in file */
1606 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1607 This->uMode &= ~MMIO_RWMODE;
1608 This->uMode |= MMIO_READ;
1609 }
1610
1611 /* convert MainAVIHeader into AVIFILINFOW */
1612 memset(&This->fInfo, 0, sizeof(This->fInfo));
1613 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1614 This->fInfo.dwScale = 1000000;
1615 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1616 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1617 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1618 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1619 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1620 This->fInfo.dwSuggestedBufferSize = 0;
1621 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1622 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1623 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1624 sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
1625
1626 /* go back to into header list */
1627 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1628 return AVIERR_FILEREAD;
1629
1630 /* foreach stream exists a "LIST","strl" chunk */
1631 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1632 /* get next nested chunk in this "LIST","strl" */
1633 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1634 return AVIERR_FILEREAD;
1635
1636 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1637 if (ckLIST2.ckid == FOURCC_LIST &&
1638 ckLIST2.fccType == listtypeSTREAMHEADER) {
1639 pStream = This->ppStreams[nStream] =
1640 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1641 if (pStream == NULL)
1642 return AVIERR_MEMORY;
1643 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1644
1645 ck.ckid = 0;
1646 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1647 switch (ck.ckid) {
1648 case ckidSTREAMHANDLERDATA:
1649 if (pStream->lpHandlerData != NULL)
1650 return AVIERR_BADFORMAT;
1651 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1652 if (pStream->lpHandlerData == NULL)
1653 return AVIERR_MEMORY;
1654 pStream->cbHandlerData = ck.cksize;
1655
1656 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1657 return AVIERR_FILEREAD;
1658 break;
1659 case ckidSTREAMFORMAT:
1660 if (pStream->lpFormat != NULL)
1661 return AVIERR_BADFORMAT;
1662 if (ck.cksize == 0)
1663 break;
1664
1665 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1666 if (pStream->lpFormat == NULL)
1667 return AVIERR_MEMORY;
1668 pStream->cbFormat = ck.cksize;
1669
1670 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1671 return AVIERR_FILEREAD;
1672
1673 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1674 LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1675
1676 /* some corrections to the video format */
1677 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1678 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1679 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1680 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1681 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1682 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1683 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1684 lpbi->biCompression = BI_RLE8;
1685 }
1686 if (lpbi->biCompression == BI_RGB &&
1687 (pStream->sInfo.fccHandler == 0 ||
1688 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1689 pStream->sInfo.fccHandler = comptypeDIB;
1690
1691 /* init rcFrame if it's empty */
1692 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1693 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1694 }
1695 break;
1696 case ckidSTREAMHEADER:
1697 {
1698 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1699 static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
1700
1701 AVIStreamHeader streamHdr;
1702 WCHAR szType[25];
1703 UINT count;
1704 LONG n = ck.cksize;
1705
1706 if (ck.cksize > sizeof(streamHdr))
1707 n = sizeof(streamHdr);
1708
1709 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1710 return AVIERR_FILEREAD;
1711
1712 pStream->sInfo.fccType = streamHdr.fccType;
1713 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1714 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1715 pStream->sInfo.wPriority = streamHdr.wPriority;
1716 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1717 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1718 pStream->sInfo.dwScale = streamHdr.dwScale;
1719 pStream->sInfo.dwRate = streamHdr.dwRate;
1720 pStream->sInfo.dwStart = streamHdr.dwStart;
1721 pStream->sInfo.dwLength = streamHdr.dwLength;
1722 pStream->sInfo.dwSuggestedBufferSize = 0;
1723 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1724 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1725 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1726 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1727 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1728 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1729 pStream->sInfo.dwEditCount = 0;
1730 pStream->sInfo.dwFormatChangeCount = 0;
1731
1732 /* generate description for stream like "filename.avi Type #n" */
1733 if (streamHdr.fccType == streamtypeVIDEO)
1734 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
1735 else if (streamHdr.fccType == streamtypeAUDIO)
1736 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
1737 else
1738 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1739
1740 /* get count of this streamtype up to this stream */
1741 count = 0;
1742 for (n = nStream; 0 <= n; n--) {
1743 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1744 count++;
1745 }
1746
1747 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1748
1749 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1750 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1751 AVIFILE_BasenameW(This->szFileName), szType, count);
1752 }
1753 break;
1754 case ckidSTREAMNAME:
1755 { /* streamname will be saved as ASCII string */
1756 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1757 if (str == NULL)
1758 return AVIERR_MEMORY;
1759
1760 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1761 {
1762 HeapFree(GetProcessHeap(), 0, str);
1763 return AVIERR_FILEREAD;
1764 }
1765
1766 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1767 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1768
1769 HeapFree(GetProcessHeap(), 0, str);
1770 }
1771 break;
1772 case ckidAVIPADDING:
1773 case mmioFOURCC('p','a','d','d'):
1774 break;
1775 default:
1776 WARN(": found extra chunk 0x%08X\n", ck.ckid);
1777 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1778 if (FAILED(hr))
1779 return hr;
1780 };
1781 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1782 {
1783 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1784 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1785 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1786 pStream->sInfo.dwScale = 1;
1787 pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1788 }
1789 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1790 return AVIERR_FILEREAD;
1791 }
1792 } else {
1793 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1794 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1795 if (FAILED(hr))
1796 return hr;
1797 }
1798 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1799 return AVIERR_FILEREAD;
1800 }
1801
1802 /* read any extra headers in "LIST","hdrl" */
1803 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1804 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1805 return AVIERR_FILEREAD;
1806
1807 /* search "LIST","movi" chunk in "RIFF","AVI " */
1808 ckLIST1.fccType = listtypeAVIMOVIE;
1809 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1810 MMIO_FINDLIST);
1811 if (FAILED(hr))
1812 return hr;
1813
1814 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1815 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1816 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1817 return AVIERR_FILEREAD;
1818
1819 /* try to find an index */
1820 ck.ckid = ckidAVINEWINDEX;
1821 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1822 &ck, &ckRIFF, MMIO_FINDCHUNK);
1823 if (SUCCEEDED(hr) && ck.cksize > 0) {
1824 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1825 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1826 }
1827
1828 /* when we haven't found an index or it's bad, then build one
1829 * by parsing 'movi' chunk */
1830 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1831 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1832 This->ppStreams[nStream]->lLastFrame = -1;
1833
1834 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1835 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1836 return AVIERR_FILEREAD;
1837 }
1838
1839 /* seek through the 'movi' list until end */
1840 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1841 if (ck.ckid != FOURCC_LIST) {
1842 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1843 nStream = StreamFromFOURCC(ck.ckid);
1844
1845 if (nStream > This->fInfo.dwStreams)
1846 return AVIERR_BADFORMAT;
1847
1848 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1849 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1850 } else {
1851 nStream = StreamFromFOURCC(ck.ckid);
1852 WARN(": file seems to be truncated!\n");
1853 if (nStream <= This->fInfo.dwStreams &&
1854 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1855 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1856 if (ck.cksize != -1) {
1857 ck.cksize -= ck.dwDataOffset;
1858 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1859
1860 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1861 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1862 }
1863 }
1864 }
1865 }
1866 }
1867 }
1868
1869 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1870 {
1871 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1872 if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1873 This->fInfo.dwSuggestedBufferSize = sugbuf;
1874 }
1875
1876 /* find other chunks */
1877 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1878
1879 return AVIERR_OK;
1880 }
1881
1882 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1883 {
1884 AVIINDEXENTRY *lp;
1885 DWORD pos, n;
1886 HRESULT hr = AVIERR_OK;
1887 BOOL bAbsolute = TRUE;
1888
1889 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1890 if (lp == NULL)
1891 return AVIERR_MEMORY;
1892
1893 /* adjust limits for index tables, so that inserting will be faster */
1894 for (n = 0; n < This->fInfo.dwStreams; n++) {
1895 IAVIStreamImpl *pStream = This->ppStreams[n];
1896
1897 pStream->lLastFrame = -1;
1898
1899 if (pStream->idxFrames != NULL) {
1900 HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1901 pStream->idxFrames = NULL;
1902 pStream->nIdxFrames = 0;
1903 }
1904
1905 if (pStream->sInfo.dwSampleSize != 0) {
1906 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1907 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1908 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1909 pStream->nIdxFrames =
1910 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1911 }
1912 } else
1913 pStream->nIdxFrames = pStream->sInfo.dwLength;
1914
1915 pStream->idxFrames =
1916 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1917 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1918 pStream->nIdxFrames = 0;
1919 HeapFree(GetProcessHeap(), 0, lp);
1920 return AVIERR_MEMORY;
1921 }
1922 }
1923
1924 pos = (DWORD)-1;
1925 while (size != 0) {
1926 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1927
1928 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1929 hr = AVIERR_FILEREAD;
1930 break;
1931 }
1932 size -= read;
1933
1934 if (pos == (DWORD)-1)
1935 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1936
1937 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1938 pos, &bAbsolute);
1939 }
1940
1941 HeapFree(GetProcessHeap(), 0, lp);
1942
1943 /* checking ... */
1944 for (n = 0; n < This->fInfo.dwStreams; n++) {
1945 IAVIStreamImpl *pStream = This->ppStreams[n];
1946
1947 if (pStream->sInfo.dwSampleSize == 0 &&
1948 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1949 ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1950 n, pStream->sInfo.dwLength, pStream->lLastFrame);
1951 }
1952
1953 return hr;
1954 }
1955
1956 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1957 LONG count, DWORD pos, BOOL *bAbsolute)
1958 {
1959 if (lp == NULL)
1960 return AVIERR_BADPARAM;
1961
1962 for (; count > 0; count--, lp++) {
1963 WORD nStream = StreamFromFOURCC(lp->ckid);
1964
1965 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1966 continue; /* skip these */
1967
1968 if (nStream > This->fInfo.dwStreams)
1969 return AVIERR_BADFORMAT;
1970
1971 /* Video frames can be either indexed in a relative position to the
1972 * "movi" chunk or in a absolute position in the file. If the index
1973 * is relative the frame offset will always be so small that it will
1974 * virtually never reach the "movi" offset so we can detect if the
1975 * video is relative very fast.
1976 */
1977 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1978 *bAbsolute = FALSE;
1979
1980 if (!*bAbsolute)
1981 lp->dwChunkOffset += pos; /* make the offset absolute */
1982
1983 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1984 return AVIERR_MEMORY;
1985 }
1986
1987 return AVIERR_OK;
1988 }
1989
1990 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1991 LPVOID buffer, DWORD size)
1992 {
1993 /* pre-conditions */
1994 assert(This != NULL);
1995 assert(This->paf != NULL);
1996 assert(This->paf->hmmio != NULL);
1997 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
1998 assert(pos <= This->lLastFrame);
1999
2000 /* should we read as much as block gives us? */
2001 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2002 size = This->idxFrames[pos].dwChunkLength;
2003
2004 /* read into out own buffer or given one? */
2005 if (buffer == NULL) {
2006 /* we also read the chunk */
2007 size += 2 * sizeof(DWORD);
2008
2009 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2010 if (This->lpBuffer == NULL || This->cbBuffer < size) {
2011 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2012
2013 if (This->lpBuffer == NULL) {
2014 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2015 if (!This->lpBuffer) return AVIERR_MEMORY;
2016 } else {
2017 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
2018 if (!new_buffer) return AVIERR_MEMORY;
2019 This->lpBuffer = new_buffer;
2020 }
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 %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
2034 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2035 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2036 This->idxFrames[pos].dwChunkLength);
2037 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\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 = HeapAlloc(GetProcessHeap(), 0, 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 HeapFree(GetProcessHeap(), 0, str);
2236 return AVIERR_FILEWRITE;
2237 }
2238
2239 HeapFree(GetProcessHeap(), 0, 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 }