dmime/tests: Test tempo track Play and DMUS_PMSGT_TEMPO messages.
[wine.git] / dlls / avifil32 / avifile.c
blob719d96edf86d76417af60575e8f1d2c6996ce479
1 /*
2 * Copyright 1999 Marcus Meissner
3 * Copyright 2002-2003 Michael Günnewig
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.
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.
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
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
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.
33 #define COBJMACROS
34 #include <assert.h>
35 #include <stdarg.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winnls.h"
42 #include "winerror.h"
43 #include "mmsystem.h"
44 #include "vfw.h"
46 #include "avifile_private.h"
47 #include "extrachunk.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
53 #ifndef IDX_PER_BLOCK
54 #define IDX_PER_BLOCK 2730
55 #endif
57 typedef struct _IAVIFileImpl IAVIFileImpl;
59 typedef struct _IAVIStreamImpl {
60 IAVIStream IAVIStream_iface;
61 LONG ref;
63 IAVIFileImpl *paf;
64 DWORD nStream; /* the n-th stream in file */
65 AVISTREAMINFOW sInfo;
67 LPVOID lpFormat;
68 DWORD cbFormat;
70 LPVOID lpHandlerData;
71 DWORD cbHandlerData;
73 EXTRACHUNKS extra;
75 LPDWORD lpBuffer;
76 DWORD cbBuffer; /* size of lpBuffer */
77 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
79 LONG lLastFrame; /* last correct index in idxFrames */
80 AVIINDEXENTRY *idxFrames;
81 DWORD nIdxFrames; /* upper index limit of idxFrames */
82 AVIINDEXENTRY *idxFmtChanges;
83 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
84 } IAVIStreamImpl;
86 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
88 return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
91 struct _IAVIFileImpl {
92 IUnknown IUnknown_inner;
93 IAVIFile IAVIFile_iface;
94 IPersistFile IPersistFile_iface;
95 IUnknown *outer_unk;
96 LONG ref;
98 AVIFILEINFOW fInfo;
99 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
101 EXTRACHUNKS fileextra;
103 DWORD dwMoviChunkPos; /* some stuff for saving ... */
104 DWORD dwIdxChunkPos;
105 DWORD dwNextFramePos;
106 DWORD dwInitialFrames;
108 MMCKINFO ckLastRecord;
109 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
110 DWORD nIdxRecords; /* current fill level */
111 DWORD cbIdxRecords; /* size of idxRecords */
113 /* IPersistFile stuff ... */
114 HMMIO hmmio;
115 LPWSTR szFileName;
116 UINT uMode;
117 BOOL fDirty;
120 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
122 return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
125 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
127 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
130 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
132 return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
135 /***********************************************************************/
137 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
138 DWORD offset, DWORD flags);
139 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
140 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
141 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
142 const AVISTREAMINFOW *asi);
143 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
144 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
145 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
146 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
147 LONG count, DWORD pos, BOOL *bAbsolute);
148 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
149 LPVOID buffer, DWORD size);
150 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
151 LPLONG offset);
152 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
153 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
154 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
155 LONG lSkip);
156 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
157 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
158 FOURCC ckid, DWORD flags, LPCVOID buffer,
159 LONG size);
161 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
163 IAVIFileImpl *This = impl_from_IUnknown(iface);
165 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
167 if (!ppv) {
168 WARN("invalid parameter\n");
169 return E_INVALIDARG;
171 *ppv = NULL;
173 if (IsEqualIID(riid, &IID_IUnknown))
174 *ppv = &This->IUnknown_inner;
175 else if (IsEqualIID(riid, &IID_IAVIFile))
176 *ppv = &This->IAVIFile_iface;
177 else if (IsEqualGUID(riid, &IID_IPersistFile))
178 *ppv = &This->IPersistFile_iface;
179 else {
180 WARN("unknown IID %s\n", debugstr_guid(riid));
181 return E_NOINTERFACE;
184 /* Violation of the COM aggregation ref counting rule */
185 IUnknown_AddRef(&This->IUnknown_inner);
186 return S_OK;
189 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
191 IAVIFileImpl *This = impl_from_IUnknown(iface);
192 ULONG ref = InterlockedIncrement(&This->ref);
194 TRACE("(%p) ref=%ld\n", This, ref);
196 return ref;
199 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
201 IAVIFileImpl *This = impl_from_IUnknown(iface);
202 ULONG ref = InterlockedDecrement(&This->ref);
203 UINT i;
205 TRACE("(%p) ref=%ld\n", This, ref);
207 if (!ref) {
208 if (This->fDirty)
209 AVIFILE_SaveFile(This);
211 for (i = 0; i < This->fInfo.dwStreams; i++) {
212 if (This->ppStreams[i] != NULL) {
213 if (This->ppStreams[i]->ref != 0)
214 ERR(": someone has still %lu reference to stream %u (%p)!\n",
215 This->ppStreams[i]->ref, i, This->ppStreams[i]);
216 AVIFILE_DestructAVIStream(This->ppStreams[i]);
217 free(This->ppStreams[i]);
218 This->ppStreams[i] = NULL;
222 if (This->idxRecords != NULL) {
223 free(This->idxRecords);
224 This->idxRecords = NULL;
225 This->nIdxRecords = 0;
228 if (This->fileextra.lp != NULL) {
229 free(This->fileextra.lp);
230 This->fileextra.lp = NULL;
231 This->fileextra.cb = 0;
234 free(This->szFileName);
235 This->szFileName = NULL;
237 if (This->hmmio != NULL) {
238 mmioClose(This->hmmio, 0);
239 This->hmmio = NULL;
242 free(This);
244 return ref;
247 static const IUnknownVtbl unk_vtbl =
249 IUnknown_fnQueryInterface,
250 IUnknown_fnAddRef,
251 IUnknown_fnRelease
254 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
256 IAVIFileImpl *This = impl_from_IAVIFile(iface);
258 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
261 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
263 IAVIFileImpl *This = impl_from_IAVIFile(iface);
265 return IUnknown_AddRef(This->outer_unk);
268 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
270 IAVIFileImpl *This = impl_from_IAVIFile(iface);
272 return IUnknown_Release(This->outer_unk);
275 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
277 IAVIFileImpl *This = impl_from_IAVIFile(iface);
279 TRACE("(%p,%p,%ld)\n",iface,afi,size);
281 if (afi == NULL)
282 return AVIERR_BADPARAM;
283 if (size < 0)
284 return AVIERR_BADSIZE;
286 AVIFILE_UpdateInfo(This);
288 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
290 if ((DWORD)size < sizeof(This->fInfo))
291 return AVIERR_BUFFERTOOSMALL;
292 return AVIERR_OK;
295 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
296 LONG lParam)
298 IAVIFileImpl *This = impl_from_IAVIFile(iface);
299 ULONG nStream;
301 TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam);
303 if (avis == NULL || lParam < 0)
304 return AVIERR_BADPARAM;
306 nStream = AVIFILE_SearchStream(This, fccType, lParam);
308 /* Does the requested stream exist? */
309 if (nStream < This->fInfo.dwStreams &&
310 This->ppStreams[nStream] != NULL) {
311 *avis = &This->ppStreams[nStream]->IAVIStream_iface;
312 IAVIStream_AddRef(*avis);
314 return AVIERR_OK;
317 /* Sorry, but the specified stream doesn't exist */
318 *avis = NULL;
319 return AVIERR_NODATA;
322 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
323 AVISTREAMINFOW *asi)
325 IAVIFileImpl *This = impl_from_IAVIFile(iface);
326 DWORD n;
328 TRACE("(%p,%p,%p)\n", iface, avis, asi);
330 /* check parameters */
331 if (avis == NULL || asi == NULL)
332 return AVIERR_BADPARAM;
334 *avis = NULL;
336 /* Does the user have write permission? */
337 if ((This->uMode & MMIO_RWMODE) == 0)
338 return AVIERR_READONLY;
340 /* Can we add another stream? */
341 n = This->fInfo.dwStreams;
342 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
343 /* already reached max nr of streams
344 * or have already written frames to disk */
345 return AVIERR_UNSUPPORTED;
348 /* check AVISTREAMINFO for some really needed things */
349 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
350 return AVIERR_BADFORMAT;
352 /* now it seems to be save to add the stream */
353 assert(This->ppStreams[n] == NULL);
354 This->ppStreams[n] = calloc(1, sizeof(IAVIStreamImpl));
355 if (This->ppStreams[n] == NULL)
356 return AVIERR_MEMORY;
358 /* initialize the new allocated stream */
359 AVIFILE_ConstructAVIStream(This, n, asi);
361 This->fInfo.dwStreams++;
362 This->fDirty = TRUE;
364 /* update our AVIFILEINFO structure */
365 AVIFILE_UpdateInfo(This);
367 /* return it */
368 *avis = &This->ppStreams[n]->IAVIStream_iface;
369 IAVIStream_AddRef(*avis);
371 return AVIERR_OK;
374 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
376 IAVIFileImpl *This = impl_from_IAVIFile(iface);
378 TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
380 /* check parameters */
381 if (lpData == NULL)
382 return AVIERR_BADPARAM;
383 if (size < 0)
384 return AVIERR_BADSIZE;
386 /* Do we have write permission? */
387 if ((This->uMode & MMIO_RWMODE) == 0)
388 return AVIERR_READONLY;
390 This->fDirty = TRUE;
392 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
395 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
397 IAVIFileImpl *This = impl_from_IAVIFile(iface);
399 TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
401 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
404 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
406 IAVIFileImpl *This = impl_from_IAVIFile(iface);
408 TRACE("(%p)\n",iface);
410 if ((This->uMode & MMIO_RWMODE) == 0)
411 return AVIERR_READONLY;
413 This->fDirty = TRUE;
415 /* no frames written to any stream? -- compute start of 'movi'-chunk */
416 if (This->dwMoviChunkPos == 0)
417 AVIFILE_ComputeMoviStart(This);
419 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
421 /* already written frames to any stream, ... */
422 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
423 /* close last record */
424 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
425 return AVIERR_FILEWRITE;
427 AVIFILE_AddRecord(This);
429 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
430 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
433 /* write out a new record into file, but don't close it */
434 This->ckLastRecord.cksize = 0;
435 This->ckLastRecord.fccType = listtypeAVIRECORD;
436 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
437 return AVIERR_FILEWRITE;
438 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
439 return AVIERR_FILEWRITE;
440 This->dwNextFramePos += 3 * sizeof(DWORD);
442 return AVIERR_OK;
445 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
447 IAVIFileImpl *This = impl_from_IAVIFile(iface);
448 ULONG nStream;
450 TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
452 /* check parameter */
453 if (lParam < 0)
454 return AVIERR_BADPARAM;
456 /* Have user write permissions? */
457 if ((This->uMode & MMIO_RWMODE) == 0)
458 return AVIERR_READONLY;
460 nStream = AVIFILE_SearchStream(This, fccType, lParam);
462 /* Does the requested stream exist? */
463 if (nStream < This->fInfo.dwStreams &&
464 This->ppStreams[nStream] != NULL) {
465 /* ... so delete it now */
466 free(This->ppStreams[nStream]);
467 This->fInfo.dwStreams--;
468 if (nStream < This->fInfo.dwStreams)
469 memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1],
470 (This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0]));
472 This->ppStreams[This->fInfo.dwStreams] = NULL;
473 This->fDirty = TRUE;
475 /* This->fInfo will be updated further when asked for */
476 return AVIERR_OK;
477 } else
478 return AVIERR_NODATA;
481 static const struct IAVIFileVtbl avif_vt = {
482 IAVIFile_fnQueryInterface,
483 IAVIFile_fnAddRef,
484 IAVIFile_fnRelease,
485 IAVIFile_fnInfo,
486 IAVIFile_fnGetStream,
487 IAVIFile_fnCreateStream,
488 IAVIFile_fnWriteData,
489 IAVIFile_fnReadData,
490 IAVIFile_fnEndRecord,
491 IAVIFile_fnDeleteStream
495 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
497 IAVIFileImpl *This = impl_from_IPersistFile(iface);
499 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
502 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
504 IAVIFileImpl *This = impl_from_IPersistFile(iface);
506 return IUnknown_AddRef(This->outer_unk);
509 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
511 IAVIFileImpl *This = impl_from_IPersistFile(iface);
513 return IUnknown_Release(This->outer_unk);
516 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
518 TRACE("(%p,%p)\n", iface, pClassID);
520 if (pClassID == NULL)
521 return AVIERR_BADPARAM;
523 *pClassID = CLSID_AVIFile;
525 return AVIERR_OK;
528 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
530 IAVIFileImpl *This = impl_from_IPersistFile(iface);
532 TRACE("(%p)\n", iface);
534 return (This->fDirty ? S_OK : S_FALSE);
537 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
539 IAVIFileImpl *This = impl_from_IPersistFile(iface);
540 int len;
542 TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
544 /* check parameter */
545 if (pszFileName == NULL)
546 return AVIERR_BADPARAM;
548 if (This->hmmio != NULL)
549 return AVIERR_ERROR; /* No reuse of this object for another file! */
551 /* remember mode and name */
552 This->uMode = dwMode;
554 len = lstrlenW(pszFileName) + 1;
555 This->szFileName = malloc(len * sizeof(WCHAR));
556 if (This->szFileName == NULL)
557 return AVIERR_MEMORY;
558 lstrcpyW(This->szFileName, pszFileName);
560 /* try to open the file */
561 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
562 if (This->hmmio == NULL) {
563 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
564 LPSTR szFileName;
566 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
567 szFileName = malloc(len * sizeof(CHAR));
568 if (szFileName == NULL)
569 return AVIERR_MEMORY;
571 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
573 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
574 free(szFileName);
575 if (This->hmmio == NULL)
576 return AVIERR_FILEOPEN;
579 /* should we create a new file? */
580 if (dwMode & OF_CREATE) {
581 memset(& This->fInfo, 0, sizeof(This->fInfo));
582 This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
584 return AVIERR_OK;
585 } else
586 return AVIFILE_LoadFile(This);
589 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
590 BOOL fRemember)
592 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
594 /* We write directly to disk, so nothing to do. */
596 return AVIERR_OK;
599 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
601 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
603 /* We write directly to disk, so nothing to do. */
605 return AVIERR_OK;
608 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
610 IAVIFileImpl *This = impl_from_IPersistFile(iface);
612 TRACE("(%p,%p)\n", iface, ppszFileName);
614 if (ppszFileName == NULL)
615 return AVIERR_BADPARAM;
617 *ppszFileName = NULL;
619 if (This->szFileName != NULL) {
620 int len = lstrlenW(This->szFileName) + 1;
622 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
623 if (*ppszFileName == NULL)
624 return AVIERR_MEMORY;
626 lstrcpyW(*ppszFileName, This->szFileName);
629 return AVIERR_OK;
632 static const struct IPersistFileVtbl pf_vt = {
633 IPersistFile_fnQueryInterface,
634 IPersistFile_fnAddRef,
635 IPersistFile_fnRelease,
636 IPersistFile_fnGetClassID,
637 IPersistFile_fnIsDirty,
638 IPersistFile_fnLoad,
639 IPersistFile_fnSave,
640 IPersistFile_fnSaveCompleted,
641 IPersistFile_fnGetCurFile
644 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
646 IAVIFileImpl *obj;
647 HRESULT hr;
649 *ppv = NULL;
650 obj = calloc(1, sizeof(IAVIFileImpl));
651 if (!obj)
652 return AVIERR_MEMORY;
654 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
655 obj->IAVIFile_iface.lpVtbl = &avif_vt;
656 obj->IPersistFile_iface.lpVtbl = &pf_vt;
657 obj->ref = 1;
658 if (pUnkOuter)
659 obj->outer_unk = pUnkOuter;
660 else
661 obj->outer_unk = &obj->IUnknown_inner;
663 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
664 IUnknown_Release(&obj->IUnknown_inner);
666 return hr;
670 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
672 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
674 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
676 if (!ppv) {
677 WARN("invalid parameter\n");
678 return E_INVALIDARG;
680 *ppv = NULL;
682 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
683 *ppv = iface;
684 IAVIStream_AddRef(iface);
686 return S_OK;
688 /* FIXME: IAVIStreaming interface */
690 return E_NOINTERFACE;
693 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
695 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
696 ULONG ref = InterlockedIncrement(&This->ref);
698 TRACE("(%p) ref=%ld\n", This, ref);
700 /* also add ref to parent, so that it doesn't kill us */
701 if (This->paf != NULL && ref == 1)
702 IAVIFile_AddRef(&This->paf->IAVIFile_iface);
704 return ref;
707 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
709 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
710 ULONG ref = InterlockedDecrement(&This->ref);
712 TRACE("(%p) ref=%ld\n", This, ref);
714 if (This->paf != NULL && ref == 0)
715 IAVIFile_Release(&This->paf->IAVIFile_iface);
717 return ref;
720 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
722 TRACE("(%p,0x%08IX,0x%08IX)\n", iface, lParam1, lParam2);
724 /* This IAVIStream interface needs an AVIFile */
725 return AVIERR_UNSUPPORTED;
728 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
730 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
732 TRACE("(%p,%p,%ld)\n", iface, psi, size);
734 if (psi == NULL)
735 return AVIERR_BADPARAM;
736 if (size < 0)
737 return AVIERR_BADSIZE;
739 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
741 if ((DWORD)size < sizeof(This->sInfo))
742 return AVIERR_BUFFERTOOSMALL;
743 return AVIERR_OK;
746 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
748 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
749 LONG offset = 0;
751 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
753 if (flags & FIND_FROM_START) {
754 pos = This->sInfo.dwStart;
755 flags &= ~(FIND_FROM_START|FIND_PREV);
756 flags |= FIND_NEXT;
759 if (This->sInfo.dwSampleSize != 0) {
760 /* convert samples into block number with offset */
761 AVIFILE_SamplesToBlock(This, &pos, &offset);
764 if (flags & FIND_TYPE) {
765 if (flags & FIND_KEY) {
766 while (0 <= pos && pos <= This->lLastFrame) {
767 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
768 goto RETURN_FOUND;
770 if (flags & FIND_NEXT)
771 pos++;
772 else
773 pos--;
775 } else if (flags & FIND_ANY) {
776 while (0 <= pos && pos <= This->lLastFrame) {
777 if (This->idxFrames[pos].dwChunkLength > 0)
778 goto RETURN_FOUND;
780 if (flags & FIND_NEXT)
781 pos++;
782 else
783 pos--;
786 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
787 This->sInfo.fccType == streamtypeVIDEO) {
788 if (flags & FIND_NEXT) {
789 ULONG n;
791 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
792 if (This->idxFmtChanges[n].ckid >= pos) {
793 pos = This->idxFmtChanges[n].ckid;
794 goto RETURN_FOUND;
796 } else {
797 LONG n;
799 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
800 if (This->idxFmtChanges[n].ckid <= pos) {
801 pos = This->idxFmtChanges[n].ckid;
802 goto RETURN_FOUND;
806 if (pos > (LONG)This->sInfo.dwStart)
807 return 0; /* format changes always for first frame */
811 return -1;
814 RETURN_FOUND:
815 if (pos < (LONG)This->sInfo.dwStart)
816 return -1;
818 switch (flags & FIND_RET) {
819 case FIND_LENGTH:
820 /* physical size */
821 pos = This->idxFrames[pos].dwChunkLength;
822 break;
823 case FIND_OFFSET:
824 /* physical position */
825 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
826 + offset * This->sInfo.dwSampleSize;
827 break;
828 case FIND_SIZE:
829 /* logical size */
830 if (This->sInfo.dwSampleSize)
831 pos = This->sInfo.dwSampleSize;
832 else
833 pos = 1;
834 break;
835 case FIND_INDEX:
836 FIXME(": FIND_INDEX flag is not supported!\n");
837 /* This is an index in the index-table on disc. */
838 break;
839 }; /* else logical position */
841 return pos;
844 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
845 LONG *formatsize)
847 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
849 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
851 if (formatsize == NULL)
852 return AVIERR_BADPARAM;
854 /* only interested in needed buffersize? */
855 if (format == NULL || *formatsize <= 0) {
856 *formatsize = This->cbFormat;
858 return AVIERR_OK;
861 /* copy initial format (only as much as will fit) */
862 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
863 if (*(DWORD*)formatsize < This->cbFormat) {
864 *formatsize = This->cbFormat;
865 return AVIERR_BUFFERTOOSMALL;
868 /* Could format change? When yes will it change? */
869 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
870 pos > This->sInfo.dwStart) {
871 LONG lLastFmt;
873 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
874 if (lLastFmt > 0) {
875 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt);
879 *formatsize = This->cbFormat;
880 return AVIERR_OK;
883 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
884 LONG formatsize)
886 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
887 BITMAPINFOHEADER *lpbiNew = format;
889 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
891 /* check parameters */
892 if (format == NULL || formatsize <= 0)
893 return AVIERR_BADPARAM;
895 /* Do we have write permission? */
896 if ((This->paf->uMode & MMIO_RWMODE) == 0)
897 return AVIERR_READONLY;
899 /* can only set format before frame is written! */
900 if (This->lLastFrame > pos)
901 return AVIERR_UNSUPPORTED;
903 /* initial format or a formatchange? */
904 if (This->lpFormat == NULL) {
905 /* initial format */
906 if (This->paf->dwMoviChunkPos != 0)
907 return AVIERR_ERROR; /* user has used API in wrong sequence! */
909 This->lpFormat = malloc(formatsize);
910 if (This->lpFormat == NULL)
911 return AVIERR_MEMORY;
912 This->cbFormat = formatsize;
914 memcpy(This->lpFormat, format, formatsize);
916 /* update some infos about stream */
917 if (This->sInfo.fccType == streamtypeVIDEO) {
918 LONG lDim;
920 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
921 if (lDim < lpbiNew->biWidth)
922 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
923 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
924 if (lDim < lpbiNew->biHeight)
925 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
926 } else if (This->sInfo.fccType == streamtypeAUDIO)
927 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
929 return AVIERR_OK;
930 } else {
931 MMCKINFO ck;
932 LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
933 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
934 AVIPALCHANGE *lppc = NULL;
935 UINT n;
937 /* perhaps format change, check it ... */
938 if (This->cbFormat != formatsize)
939 return AVIERR_UNSUPPORTED;
941 /* no format change, only the initial one */
942 if (memcmp(This->lpFormat, format, formatsize) == 0)
943 return AVIERR_OK;
945 /* check that's only the palette, which changes */
946 if (lpbiOld->biSize != lpbiNew->biSize ||
947 lpbiOld->biWidth != lpbiNew->biWidth ||
948 lpbiOld->biHeight != lpbiNew->biHeight ||
949 lpbiOld->biPlanes != lpbiNew->biPlanes ||
950 lpbiOld->biBitCount != lpbiNew->biBitCount ||
951 lpbiOld->biCompression != lpbiNew->biCompression ||
952 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
953 return AVIERR_UNSUPPORTED;
955 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
957 /* simply say all colors have changed */
958 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
959 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
960 lppc = malloc(ck.cksize);
961 if (lppc == NULL)
962 return AVIERR_MEMORY;
964 lppc->bFirstEntry = 0;
965 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
966 lppc->wFlags = 0;
967 for (n = 0; n < lpbiOld->biClrUsed; n++) {
968 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
969 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
970 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
971 lppc->peNew[n].peFlags = 0;
974 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
975 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
976 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
977 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
979 free(lppc);
980 return AVIERR_FILEWRITE;
983 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
985 free(lppc);
987 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
991 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
992 LONG buffersize, LONG *bytesread, LONG *samplesread)
994 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
995 DWORD size;
996 HRESULT hr;
998 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
999 buffersize, bytesread, samplesread);
1001 /* clear return parameters if given */
1002 if (bytesread != NULL)
1003 *bytesread = 0;
1004 if (samplesread != NULL)
1005 *samplesread = 0;
1007 /* check parameters */
1008 if ((LONG)This->sInfo.dwStart > start)
1009 return AVIERR_NODATA; /* couldn't read before start of stream */
1010 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1011 return AVIERR_NODATA; /* start is past end of stream */
1013 /* should we read as much as possible? */
1014 if (samples == -1) {
1015 /* User should know how much we have read */
1016 if (bytesread == NULL && samplesread == NULL)
1017 return AVIERR_BADPARAM;
1019 if (This->sInfo.dwSampleSize != 0)
1020 samples = buffersize / This->sInfo.dwSampleSize;
1021 else
1022 samples = 1;
1025 /* limit to end of stream */
1026 if ((LONG)This->sInfo.dwLength < samples)
1027 samples = This->sInfo.dwLength;
1028 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1029 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1031 /* nothing to read? Then leave ... */
1032 if (samples == 0)
1033 return AVIERR_OK;
1035 if (This->sInfo.dwSampleSize != 0) {
1036 /* fixed samplesize -- we can read over frame/block boundaries */
1037 LONG block = start;
1038 LONG offset = 0;
1040 if (!buffer)
1042 if (bytesread)
1043 *bytesread = samples*This->sInfo.dwSampleSize;
1044 if (samplesread)
1045 *samplesread = samples;
1046 return AVIERR_OK;
1049 /* convert start sample to block,offset pair */
1050 AVIFILE_SamplesToBlock(This, &block, &offset);
1052 /* convert samples to bytes */
1053 samples *= This->sInfo.dwSampleSize;
1055 while (samples > 0 && buffersize > 0) {
1056 LONG blocksize;
1057 if (block != This->dwCurrentFrame) {
1058 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1059 if (FAILED(hr))
1060 return hr;
1063 size = min((DWORD)samples, (DWORD)buffersize);
1064 blocksize = This->lpBuffer[1];
1065 TRACE("blocksize = %lu\n",blocksize);
1066 size = min(size, blocksize - offset);
1067 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1069 block++;
1070 offset = 0;
1071 buffer = ((LPBYTE)buffer)+size;
1072 samples -= size;
1073 buffersize -= size;
1075 /* fill out return parameters if given */
1076 if (bytesread != NULL)
1077 *bytesread += size;
1078 if (samplesread != NULL)
1079 *samplesread += size / This->sInfo.dwSampleSize;
1082 if (samples == 0)
1083 return AVIERR_OK;
1084 else
1085 return AVIERR_BUFFERTOOSMALL;
1086 } else {
1087 /* variable samplesize -- we can only read one full frame/block */
1088 if (samples > 1)
1089 samples = 1;
1091 assert(start <= This->lLastFrame);
1092 size = This->idxFrames[start].dwChunkLength;
1093 if (buffer != NULL && buffersize >= size) {
1094 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1095 if (FAILED(hr))
1096 return hr;
1097 } else if (buffer != NULL)
1098 return AVIERR_BUFFERTOOSMALL;
1100 /* fill out return parameters if given */
1101 if (bytesread != NULL)
1102 *bytesread = size;
1103 if (samplesread != NULL)
1104 *samplesread = samples;
1106 return AVIERR_OK;
1110 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
1111 LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1113 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1114 FOURCC ckid;
1115 HRESULT hr;
1117 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
1118 buffer, buffersize, flags, sampwritten, byteswritten);
1120 /* clear return parameters if given */
1121 if (sampwritten != NULL)
1122 *sampwritten = 0;
1123 if (byteswritten != NULL)
1124 *byteswritten = 0;
1126 /* check parameters */
1127 if (buffer == NULL && (buffersize > 0 || samples > 0))
1128 return AVIERR_BADPARAM;
1130 /* Have we write permission? */
1131 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1132 return AVIERR_READONLY;
1134 switch (This->sInfo.fccType) {
1135 case streamtypeAUDIO:
1136 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1137 break;
1138 default:
1139 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1140 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1141 else
1142 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1143 break;
1146 /* append to end of stream? */
1147 if (start == -1) {
1148 if (This->lLastFrame == -1)
1149 start = This->sInfo.dwStart;
1150 else
1151 start = This->sInfo.dwLength;
1152 } else if (This->lLastFrame == -1)
1153 This->sInfo.dwStart = start;
1155 if (This->sInfo.dwSampleSize != 0) {
1156 /* fixed sample size -- audio like */
1157 if (samples * This->sInfo.dwSampleSize != buffersize)
1158 return AVIERR_BADPARAM;
1160 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1161 if (This->sInfo.dwLength != start)
1162 return AVIERR_UNSUPPORTED;
1164 /* Convert position to frame/block */
1165 start = This->lLastFrame + 1;
1167 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1168 FIXME(": not interleaved, could collect audio data!\n");
1170 } else {
1171 /* variable sample size -- video like */
1172 if (samples > 1)
1173 return AVIERR_UNSUPPORTED;
1175 /* must we fill up with empty frames? */
1176 if (This->lLastFrame != -1) {
1177 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1179 while (start > This->lLastFrame + 1) {
1180 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1181 if (FAILED(hr))
1182 return hr;
1187 /* write the block now */
1188 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1189 if (SUCCEEDED(hr)) {
1190 /* fill out return parameters if given */
1191 if (sampwritten != NULL)
1192 *sampwritten = samples;
1193 if (byteswritten != NULL)
1194 *byteswritten = buffersize;
1197 return hr;
1200 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1202 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1204 FIXME("(%p,%ld,%ld): stub\n", iface, start, samples);
1206 /* check parameters */
1207 if (start < 0 || samples < 0)
1208 return AVIERR_BADPARAM;
1210 /* Delete before start of stream? */
1211 if (start + samples < This->sInfo.dwStart)
1212 return AVIERR_OK;
1214 /* Delete after end of stream? */
1215 if (start > This->sInfo.dwLength)
1216 return AVIERR_OK;
1218 /* For the rest we need write permissions */
1219 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1220 return AVIERR_READONLY;
1222 /* 1. overwrite the data with JUNK
1224 * if ISINTERLEAVED {
1225 * 2. concat all neighboured JUNK-blocks in this record to one
1226 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1227 * to start of this record, repeat this.
1228 * } else {
1229 * 2. concat all neighboured JUNK-blocks.
1230 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1231 * start of this block.
1235 return AVIERR_UNSUPPORTED;
1238 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1240 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1242 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
1244 if (fcc == ckidSTREAMHANDLERDATA) {
1245 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1246 if (lp == NULL || *lpread <= 0) {
1247 *lpread = This->cbHandlerData;
1248 return AVIERR_OK;
1251 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1252 if (*lpread < This->cbHandlerData)
1253 return AVIERR_BUFFERTOOSMALL;
1254 return AVIERR_OK;
1255 } else
1256 return AVIERR_NODATA;
1257 } else
1258 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1261 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1263 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1265 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
1267 /* check parameters */
1268 if (lp == NULL)
1269 return AVIERR_BADPARAM;
1270 if (size <= 0)
1271 return AVIERR_BADSIZE;
1273 /* need write permission */
1274 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1275 return AVIERR_READONLY;
1277 /* already written something to this file? */
1278 if (This->paf->dwMoviChunkPos != 0) {
1279 /* the data will be inserted before the 'movi' chunk, so check for
1280 * enough space */
1281 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1283 /* ckid,size => 2 * sizeof(DWORD) */
1284 dwPos += 2 * sizeof(DWORD) + size;
1285 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1286 return AVIERR_UNSUPPORTED; /* not enough space left */
1289 This->paf->fDirty = TRUE;
1291 if (fcc == ckidSTREAMHANDLERDATA) {
1292 if (This->lpHandlerData != NULL) {
1293 FIXME(": handler data already set -- overwrite?\n");
1294 return AVIERR_UNSUPPORTED;
1297 This->lpHandlerData = malloc(size);
1298 if (This->lpHandlerData == NULL)
1299 return AVIERR_MEMORY;
1300 This->cbHandlerData = size;
1301 memcpy(This->lpHandlerData, lp, size);
1303 return AVIERR_OK;
1304 } else
1305 return WriteExtraChunk(&This->extra, fcc, lp, size);
1308 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1310 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
1312 return E_FAIL;
1315 static const struct IAVIStreamVtbl avist_vt = {
1316 IAVIStream_fnQueryInterface,
1317 IAVIStream_fnAddRef,
1318 IAVIStream_fnRelease,
1319 IAVIStream_fnCreate,
1320 IAVIStream_fnInfo,
1321 IAVIStream_fnFindSample,
1322 IAVIStream_fnReadFormat,
1323 IAVIStream_fnSetFormat,
1324 IAVIStream_fnRead,
1325 IAVIStream_fnWrite,
1326 IAVIStream_fnDelete,
1327 IAVIStream_fnReadData,
1328 IAVIStream_fnWriteData,
1329 IAVIStream_fnSetInfo
1333 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1335 UINT n;
1337 /* pre-conditions */
1338 assert(This != NULL);
1340 switch (TWOCCFromFOURCC(ckid)) {
1341 case cktypeDIBbits:
1342 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1343 flags |= AVIIF_KEYFRAME;
1344 break;
1345 case cktypeDIBcompressed:
1346 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1347 flags &= ~AVIIF_KEYFRAME;
1348 break;
1349 case cktypePALchange:
1350 if (This->sInfo.fccType != streamtypeVIDEO) {
1351 ERR(": found palette change in non-video stream!\n");
1352 return AVIERR_BADFORMAT;
1355 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1356 DWORD new_count = This->nIdxFmtChanges + 16;
1357 void *new_buffer = _recalloc(This->idxFmtChanges, new_count, sizeof(AVIINDEXENTRY));
1358 if (!new_buffer) return AVIERR_MEMORY;
1359 This->idxFmtChanges = new_buffer;
1360 This->nIdxFmtChanges = new_count;
1363 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1364 n = ++This->sInfo.dwFormatChangeCount;
1365 This->idxFmtChanges[n].ckid = This->lLastFrame;
1366 This->idxFmtChanges[n].dwFlags = 0;
1367 This->idxFmtChanges[n].dwChunkOffset = offset;
1368 This->idxFmtChanges[n].dwChunkLength = size;
1370 return AVIERR_OK;
1371 case cktypeWAVEbytes:
1372 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1373 flags |= AVIIF_KEYFRAME;
1374 break;
1375 default:
1376 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1377 break;
1380 /* first frame is always a keyframe */
1381 if (This->lLastFrame == -1)
1382 flags |= AVIIF_KEYFRAME;
1384 if (This->sInfo.dwSuggestedBufferSize < size)
1385 This->sInfo.dwSuggestedBufferSize = size;
1387 /* get memory for index */
1388 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1389 This->nIdxFrames += 512;
1390 This->idxFrames = _recalloc(This->idxFrames, This->nIdxFrames, sizeof(AVIINDEXENTRY));
1391 if (This->idxFrames == NULL)
1392 return AVIERR_MEMORY;
1395 This->lLastFrame++;
1396 This->idxFrames[This->lLastFrame].ckid = ckid;
1397 This->idxFrames[This->lLastFrame].dwFlags = flags;
1398 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1399 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1401 /* update AVISTREAMINFO structure if necessary */
1402 if (This->sInfo.dwLength <= This->lLastFrame)
1403 This->sInfo.dwLength = This->lLastFrame + 1;
1405 return AVIERR_OK;
1408 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1410 /* pre-conditions */
1411 assert(This != NULL && This->ppStreams[0] != NULL);
1413 if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) {
1414 DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY);
1415 void *mem;
1416 mem = _recalloc(This->idxRecords, 1, new_count);
1417 if (mem) {
1418 This->cbIdxRecords = new_count;
1419 This->idxRecords = mem;
1420 } else {
1421 free(This->idxRecords);
1422 This->idxRecords = NULL;
1423 return AVIERR_MEMORY;
1427 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1429 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1430 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1431 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1432 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1433 This->idxRecords[This->nIdxRecords].dwChunkLength =
1434 This->ckLastRecord.cksize;
1435 This->nIdxRecords++;
1437 return AVIERR_OK;
1440 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1442 DWORD dwPos;
1443 DWORD nStream;
1445 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1446 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1448 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1449 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1451 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1452 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1453 dwPos += ((pStream->cbFormat + 1) & ~1U);
1454 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1455 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1456 if (pStream->sInfo.szName[0])
1457 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1460 if (This->dwMoviChunkPos == 0) {
1461 This->dwNextFramePos = dwPos;
1463 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1464 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1465 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1467 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1470 return dwPos;
1473 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1475 IAVIStreamImpl *pstream;
1477 /* pre-conditions */
1478 assert(paf != NULL);
1479 assert(nr < MAX_AVISTREAMS);
1480 assert(paf->ppStreams[nr] != NULL);
1482 pstream = paf->ppStreams[nr];
1484 pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1485 pstream->ref = 0;
1486 pstream->paf = paf;
1487 pstream->nStream = nr;
1488 pstream->dwCurrentFrame = (DWORD)-1;
1489 pstream->lLastFrame = -1;
1491 if (asi != NULL) {
1492 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1494 if (asi->dwLength > 0) {
1495 /* pre-allocate mem for frame-index structure */
1496 pstream->idxFrames = calloc(asi->dwLength, sizeof(AVIINDEXENTRY));
1497 if (pstream->idxFrames != NULL)
1498 pstream->nIdxFrames = asi->dwLength;
1500 if (asi->dwFormatChangeCount > 0) {
1501 /* pre-allocate mem for formatchange-index structure */
1502 pstream->idxFmtChanges = calloc(asi->dwFormatChangeCount, sizeof(AVIINDEXENTRY));
1503 if (pstream->idxFmtChanges != NULL)
1504 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
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);
1516 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1519 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1521 /* pre-conditions */
1522 assert(This != NULL);
1524 This->dwCurrentFrame = (DWORD)-1;
1525 This->lLastFrame = -1;
1526 This->paf = NULL;
1527 if (This->idxFrames != NULL) {
1528 free(This->idxFrames);
1529 This->idxFrames = NULL;
1530 This->nIdxFrames = 0;
1532 free(This->idxFmtChanges);
1533 This->idxFmtChanges = NULL;
1534 if (This->lpBuffer != NULL) {
1535 free(This->lpBuffer);
1536 This->lpBuffer = NULL;
1537 This->cbBuffer = 0;
1539 if (This->lpHandlerData != NULL) {
1540 free(This->lpHandlerData);
1541 This->lpHandlerData = NULL;
1542 This->cbHandlerData = 0;
1544 if (This->extra.lp != NULL) {
1545 free(This->extra.lp);
1546 This->extra.lp = NULL;
1547 This->extra.cb = 0;
1549 if (This->lpFormat != NULL) {
1550 free(This->lpFormat);
1551 This->lpFormat = NULL;
1552 This->cbFormat = 0;
1556 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1558 MainAVIHeader MainAVIHdr;
1559 MMCKINFO ckRIFF;
1560 MMCKINFO ckLIST1;
1561 MMCKINFO ckLIST2;
1562 MMCKINFO ck;
1563 IAVIStreamImpl *pStream;
1564 DWORD nStream;
1565 HRESULT hr;
1567 if (This->hmmio == NULL)
1568 return AVIERR_FILEOPEN;
1570 /* initialize stream ptr's */
1571 memset(This->ppStreams, 0, sizeof(This->ppStreams));
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;
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;
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;
1592 if (ck.cksize != sizeof(MainAVIHdr)) {
1593 ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize);
1594 return AVIERR_BADFORMAT;
1596 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1597 return AVIERR_FILEREAD;
1599 /* check for MAX_AVISTREAMS limit */
1600 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1601 WARN("file contains %lu streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1602 return AVIERR_UNSUPPORTED;
1605 /* adjust permissions if copyrighted material in file */
1606 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1607 This->uMode &= ~MMIO_RWMODE;
1608 This->uMode |= MMIO_READ;
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 ARRAY_SIZE(This->fInfo.szFileType));
1626 /* go back to into header list */
1627 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1628 return AVIERR_FILEREAD;
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;
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] = calloc(1, sizeof(IAVIStreamImpl));
1640 if (pStream == NULL)
1641 return AVIERR_MEMORY;
1642 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1644 ck.ckid = 0;
1645 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1646 switch (ck.ckid) {
1647 case ckidSTREAMHANDLERDATA:
1648 if (pStream->lpHandlerData != NULL)
1649 return AVIERR_BADFORMAT;
1650 pStream->lpHandlerData = malloc(ck.cksize);
1651 if (pStream->lpHandlerData == NULL)
1652 return AVIERR_MEMORY;
1653 pStream->cbHandlerData = ck.cksize;
1655 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1656 return AVIERR_FILEREAD;
1657 break;
1658 case ckidSTREAMFORMAT:
1659 if (pStream->lpFormat != NULL)
1660 return AVIERR_BADFORMAT;
1661 if (ck.cksize == 0)
1662 break;
1664 pStream->lpFormat = malloc(ck.cksize);
1665 if (pStream->lpFormat == NULL)
1666 return AVIERR_MEMORY;
1667 pStream->cbFormat = ck.cksize;
1669 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1670 return AVIERR_FILEREAD;
1672 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1673 LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1675 /* some corrections to the video format */
1676 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1677 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1678 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1679 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1680 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1681 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1682 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1683 lpbi->biCompression = BI_RLE8;
1685 if (lpbi->biCompression == BI_RGB &&
1686 (pStream->sInfo.fccHandler == 0 ||
1687 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1688 pStream->sInfo.fccHandler = comptypeDIB;
1690 /* init rcFrame if it's empty */
1691 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1692 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1694 break;
1695 case ckidSTREAMHEADER:
1697 AVIStreamHeader streamHdr;
1698 WCHAR szType[25];
1699 UINT count;
1700 LONG n = ck.cksize;
1702 if (ck.cksize > sizeof(streamHdr))
1703 n = sizeof(streamHdr);
1705 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1706 return AVIERR_FILEREAD;
1708 pStream->sInfo.fccType = streamHdr.fccType;
1709 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1710 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1711 pStream->sInfo.wPriority = streamHdr.wPriority;
1712 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1713 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1714 pStream->sInfo.dwScale = streamHdr.dwScale;
1715 pStream->sInfo.dwRate = streamHdr.dwRate;
1716 pStream->sInfo.dwStart = streamHdr.dwStart;
1717 pStream->sInfo.dwLength = streamHdr.dwLength;
1718 pStream->sInfo.dwSuggestedBufferSize = 0;
1719 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1720 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1721 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1722 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1723 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1724 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1725 pStream->sInfo.dwEditCount = 0;
1726 pStream->sInfo.dwFormatChangeCount = 0;
1728 /* generate description for stream like "filename.avi Type #n" */
1729 if (streamHdr.fccType == streamtypeVIDEO)
1730 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, ARRAY_SIZE(szType));
1731 else if (streamHdr.fccType == streamtypeAUDIO)
1732 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, ARRAY_SIZE(szType));
1733 else
1734 wsprintfW(szType, L"%4.4hs", (char*)&streamHdr.fccType);
1736 /* get count of this streamtype up to this stream */
1737 count = 0;
1738 for (n = nStream; 0 <= n; n--) {
1739 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1740 count++;
1743 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1745 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1746 wsprintfW(pStream->sInfo.szName, L"%s %s #%d",
1747 AVIFILE_BasenameW(This->szFileName), szType, count);
1749 break;
1750 case ckidSTREAMNAME:
1751 { /* streamname will be saved as ASCII string */
1752 char *str = malloc(ck.cksize);
1753 if (str == NULL)
1754 return AVIERR_MEMORY;
1756 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1758 free(str);
1759 return AVIERR_FILEREAD;
1762 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1763 ARRAY_SIZE(pStream->sInfo.szName));
1765 free(str);
1767 break;
1768 case ckidAVIPADDING:
1769 case mmioFOURCC('p','a','d','d'):
1770 break;
1771 default:
1772 WARN(": found extra chunk 0x%08lX\n", ck.ckid);
1773 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1774 if (FAILED(hr))
1775 return hr;
1777 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1779 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1780 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1781 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1782 pStream->sInfo.dwScale = 1;
1783 pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1785 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1786 return AVIERR_FILEREAD;
1788 } else {
1789 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1790 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1791 if (FAILED(hr))
1792 return hr;
1794 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1795 return AVIERR_FILEREAD;
1798 /* read any extra headers in "LIST","hdrl" */
1799 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1800 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1801 return AVIERR_FILEREAD;
1803 /* search "LIST","movi" chunk in "RIFF","AVI " */
1804 ckLIST1.fccType = listtypeAVIMOVIE;
1805 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1806 MMIO_FINDLIST);
1807 if (FAILED(hr))
1808 return hr;
1810 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1811 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1812 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1813 return AVIERR_FILEREAD;
1815 /* try to find an index */
1816 ck.ckid = ckidAVINEWINDEX;
1817 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1818 &ck, &ckRIFF, MMIO_FINDCHUNK);
1819 if (SUCCEEDED(hr) && ck.cksize > 0) {
1820 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1821 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1824 /* when we haven't found an index or it's bad, then build one
1825 * by parsing 'movi' chunk */
1826 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1827 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1828 This->ppStreams[nStream]->lLastFrame = -1;
1830 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1831 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1832 return AVIERR_FILEREAD;
1835 /* seek through the 'movi' list until end */
1836 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1837 if (ck.ckid != FOURCC_LIST) {
1838 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1839 nStream = StreamFromFOURCC(ck.ckid);
1841 if (nStream > This->fInfo.dwStreams)
1842 return AVIERR_BADFORMAT;
1844 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1845 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1846 } else {
1847 nStream = StreamFromFOURCC(ck.ckid);
1848 WARN(": file seems to be truncated!\n");
1849 if (nStream <= This->fInfo.dwStreams &&
1850 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1851 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1852 if (ck.cksize != -1) {
1853 ck.cksize -= ck.dwDataOffset;
1854 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1856 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1857 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1865 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1867 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1868 if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1869 This->fInfo.dwSuggestedBufferSize = sugbuf;
1872 /* find other chunks */
1873 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1875 return AVIERR_OK;
1878 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1880 AVIINDEXENTRY *lp;
1881 DWORD pos, n;
1882 HRESULT hr = AVIERR_OK;
1883 BOOL bAbsolute = TRUE;
1885 lp = malloc(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1886 if (lp == NULL)
1887 return AVIERR_MEMORY;
1889 /* adjust limits for index tables, so that inserting will be faster */
1890 for (n = 0; n < This->fInfo.dwStreams; n++) {
1891 IAVIStreamImpl *pStream = This->ppStreams[n];
1893 pStream->lLastFrame = -1;
1895 if (pStream->idxFrames != NULL) {
1896 free(pStream->idxFrames);
1897 pStream->idxFrames = NULL;
1898 pStream->nIdxFrames = 0;
1901 if (pStream->sInfo.dwSampleSize != 0) {
1902 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1903 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1904 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1905 pStream->nIdxFrames =
1906 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1908 } else
1909 pStream->nIdxFrames = pStream->sInfo.dwLength;
1911 pStream->idxFrames = calloc(pStream->nIdxFrames, sizeof(AVIINDEXENTRY));
1912 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1913 pStream->nIdxFrames = 0;
1914 free(lp);
1915 return AVIERR_MEMORY;
1919 pos = (DWORD)-1;
1920 while (size != 0) {
1921 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1923 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1924 hr = AVIERR_FILEREAD;
1925 break;
1927 size -= read;
1929 if (pos == (DWORD)-1)
1930 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1932 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1933 pos, &bAbsolute);
1936 free(lp);
1938 /* checking ... */
1939 for (n = 0; n < This->fInfo.dwStreams; n++) {
1940 IAVIStreamImpl *pStream = This->ppStreams[n];
1942 if (pStream->sInfo.dwSampleSize == 0 &&
1943 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1944 ERR("stream %lu length mismatch: dwLength=%lu found=%ld\n",
1945 n, pStream->sInfo.dwLength, pStream->lLastFrame);
1948 return hr;
1951 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1952 LONG count, DWORD pos, BOOL *bAbsolute)
1954 if (lp == NULL)
1955 return AVIERR_BADPARAM;
1957 for (; count > 0; count--, lp++) {
1958 WORD nStream = StreamFromFOURCC(lp->ckid);
1960 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1961 continue; /* skip these */
1963 if (nStream > This->fInfo.dwStreams)
1964 return AVIERR_BADFORMAT;
1966 /* Video frames can be either indexed in a relative position to the
1967 * "movi" chunk or in a absolute position in the file. If the index
1968 * is relative the frame offset will always be so small that it will
1969 * virtually never reach the "movi" offset so we can detect if the
1970 * video is relative very fast.
1972 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1973 *bAbsolute = FALSE;
1975 if (!*bAbsolute)
1976 lp->dwChunkOffset += pos; /* make the offset absolute */
1978 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1979 return AVIERR_MEMORY;
1982 return AVIERR_OK;
1985 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1986 LPVOID buffer, DWORD size)
1988 /* pre-conditions */
1989 assert(This != NULL);
1990 assert(This->paf != NULL);
1991 assert(This->paf->hmmio != NULL);
1992 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
1993 assert(pos <= This->lLastFrame);
1995 /* should we read as much as block gives us? */
1996 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
1997 size = This->idxFrames[pos].dwChunkLength;
1999 /* read into out own buffer or given one? */
2000 if (buffer == NULL) {
2001 /* we also read the chunk */
2002 size += 2 * sizeof(DWORD);
2004 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2005 if (This->lpBuffer == NULL || This->cbBuffer < size) {
2006 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2007 void *new_buffer = realloc(This->lpBuffer, maxSize);
2008 if (!new_buffer) return AVIERR_MEMORY;
2009 This->lpBuffer = new_buffer;
2010 This->cbBuffer = maxSize;
2013 /* now read the complete chunk into our buffer */
2014 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2015 return AVIERR_FILEREAD;
2016 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2017 return AVIERR_FILEREAD;
2019 /* check if it was the correct block which we have read */
2020 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2021 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2022 ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset);
2023 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2024 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2025 This->idxFrames[pos].dwChunkLength);
2026 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2027 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2028 return AVIERR_FILEREAD;
2030 } else {
2031 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2032 return AVIERR_FILEREAD;
2033 if (mmioRead(This->paf->hmmio, buffer, size) != size)
2034 return AVIERR_FILEREAD;
2037 return AVIERR_OK;
2040 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2042 LONG block;
2044 /* pre-conditions */
2045 assert(This != NULL);
2046 assert(pos != NULL);
2047 assert(offset != NULL);
2048 assert(This->sInfo.dwSampleSize != 0);
2049 assert(*pos >= This->sInfo.dwStart);
2051 /* convert start sample to start bytes */
2052 (*offset) = (*pos) - This->sInfo.dwStart;
2053 (*offset) *= This->sInfo.dwSampleSize;
2055 /* convert bytes to block number */
2056 for (block = 0; block <= This->lLastFrame; block++) {
2057 if (This->idxFrames[block].dwChunkLength <= *offset)
2058 (*offset) -= This->idxFrames[block].dwChunkLength;
2059 else
2060 break;
2063 *pos = block;
2066 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2068 MainAVIHeader MainAVIHdr;
2069 IAVIStreamImpl* pStream;
2070 MMCKINFO ckRIFF;
2071 MMCKINFO ckLIST1;
2072 MMCKINFO ckLIST2;
2073 MMCKINFO ck;
2074 DWORD nStream;
2075 DWORD dwPos;
2076 HRESULT hr;
2078 /* initialize some things */
2079 if (This->dwMoviChunkPos == 0)
2080 AVIFILE_ComputeMoviStart(This);
2082 /* written one record too much? */
2083 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2084 This->dwNextFramePos -= 3 * sizeof(DWORD);
2085 if (This->nIdxRecords > 0)
2086 This->nIdxRecords--;
2089 AVIFILE_UpdateInfo(This);
2091 assert(This->fInfo.dwScale != 0);
2093 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2094 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2095 This->fInfo.dwScale);
2096 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2097 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2098 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2099 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2100 MainAVIHdr.dwInitialFrames = 0;
2101 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2102 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2103 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2104 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2105 MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2107 /* now begin writing ... */
2108 mmioSeek(This->hmmio, 0, SEEK_SET);
2110 /* RIFF chunk */
2111 ckRIFF.cksize = 0;
2112 ckRIFF.fccType = formtypeAVI;
2113 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2114 return AVIERR_FILEWRITE;
2116 /* AVI headerlist */
2117 ckLIST1.cksize = 0;
2118 ckLIST1.fccType = listtypeAVIHEADER;
2119 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2120 return AVIERR_FILEWRITE;
2122 /* MainAVIHeader */
2123 ck.ckid = ckidAVIMAINHDR;
2124 ck.cksize = sizeof(MainAVIHdr);
2125 ck.fccType = 0;
2126 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2127 return AVIERR_FILEWRITE;
2128 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2129 return AVIERR_FILEWRITE;
2130 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2131 return AVIERR_FILEWRITE;
2133 /* write the headers of each stream into a separate streamheader list */
2134 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2135 AVIStreamHeader strHdr;
2137 pStream = This->ppStreams[nStream];
2139 /* begin the new streamheader list */
2140 ckLIST2.cksize = 0;
2141 ckLIST2.fccType = listtypeSTREAMHEADER;
2142 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2143 return AVIERR_FILEWRITE;
2145 /* create an AVIStreamHeader from the AVSTREAMINFO */
2146 strHdr.fccType = pStream->sInfo.fccType;
2147 strHdr.fccHandler = pStream->sInfo.fccHandler;
2148 strHdr.dwFlags = pStream->sInfo.dwFlags;
2149 strHdr.wPriority = pStream->sInfo.wPriority;
2150 strHdr.wLanguage = pStream->sInfo.wLanguage;
2151 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2152 strHdr.dwScale = pStream->sInfo.dwScale;
2153 strHdr.dwRate = pStream->sInfo.dwRate;
2154 strHdr.dwStart = pStream->sInfo.dwStart;
2155 strHdr.dwLength = pStream->sInfo.dwLength;
2156 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2157 strHdr.dwQuality = pStream->sInfo.dwQuality;
2158 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2159 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2160 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2161 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2162 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2164 /* now write the AVIStreamHeader */
2165 ck.ckid = ckidSTREAMHEADER;
2166 ck.cksize = sizeof(strHdr);
2167 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2168 return AVIERR_FILEWRITE;
2169 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2170 return AVIERR_FILEWRITE;
2171 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2172 return AVIERR_FILEWRITE;
2174 /* ... the hopefully ever present streamformat ... */
2175 ck.ckid = ckidSTREAMFORMAT;
2176 ck.cksize = pStream->cbFormat;
2177 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2178 return AVIERR_FILEWRITE;
2179 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2180 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2181 return AVIERR_FILEWRITE;
2183 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2184 return AVIERR_FILEWRITE;
2186 /* ... some optional existing handler data ... */
2187 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2188 ck.ckid = ckidSTREAMHANDLERDATA;
2189 ck.cksize = pStream->cbHandlerData;
2190 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2191 return AVIERR_FILEWRITE;
2192 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2193 return AVIERR_FILEWRITE;
2194 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2195 return AVIERR_FILEWRITE;
2198 /* ... some optional additional extra chunk for this stream ... */
2199 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2200 /* the chunk header(s) are already in the structure */
2201 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2202 return AVIERR_FILEWRITE;
2205 /* ... an optional name for this stream ... */
2206 if (pStream->sInfo.szName[0]) {
2207 LPSTR str;
2209 ck.ckid = ckidSTREAMNAME;
2210 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2211 if (ck.cksize & 1) /* align */
2212 ck.cksize++;
2213 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2214 return AVIERR_FILEWRITE;
2216 /* the streamname must be saved in ASCII not Unicode */
2217 str = malloc(ck.cksize);
2218 if (str == NULL)
2219 return AVIERR_MEMORY;
2220 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2221 ck.cksize, NULL, NULL);
2223 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2224 free(str);
2225 return AVIERR_FILEWRITE;
2228 free(str);
2229 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2230 return AVIERR_FILEWRITE;
2233 /* close streamheader list for this stream */
2234 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2235 return AVIERR_FILEWRITE;
2236 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2238 /* close the aviheader list */
2239 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2240 return AVIERR_FILEWRITE;
2242 /* check for padding to pre-guessed 'movi'-chunk position */
2243 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2244 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2245 ck.ckid = ckidAVIPADDING;
2246 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2247 assert((LONG)ck.cksize >= 0);
2249 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2250 return AVIERR_FILEWRITE;
2251 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2252 return AVIERR_FILEWRITE;
2253 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2254 return AVIERR_FILEWRITE;
2257 /* now write the 'movi' chunk */
2258 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2259 ckLIST1.cksize = 0;
2260 ckLIST1.fccType = listtypeAVIMOVIE;
2261 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2262 return AVIERR_FILEWRITE;
2263 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2264 return AVIERR_FILEWRITE;
2265 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2266 return AVIERR_FILEWRITE;
2268 /* write 'idx1' chunk */
2269 hr = AVIFILE_SaveIndex(This);
2270 if (FAILED(hr))
2271 return hr;
2273 /* write optional extra file chunks */
2274 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2275 /* as for the streams, are the chunk header(s) in the structure */
2276 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2277 return AVIERR_FILEWRITE;
2280 /* close RIFF chunk */
2281 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2282 return AVIERR_FILEWRITE;
2284 /* add some JUNK at end for bad parsers */
2285 memset(&ckRIFF, 0, sizeof(ckRIFF));
2286 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2287 mmioFlush(This->hmmio, 0);
2289 return AVIERR_OK;
2292 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2294 IAVIStreamImpl *pStream;
2295 AVIINDEXENTRY idx;
2296 MMCKINFO ck;
2297 DWORD nStream;
2298 LONG n;
2300 ck.ckid = ckidAVINEWINDEX;
2301 ck.cksize = 0;
2302 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2303 return AVIERR_FILEWRITE;
2305 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2306 /* is interleaved -- write block of corresponding frames */
2307 LONG lInitialFrames = 0;
2308 LONG stepsize;
2309 LONG i;
2311 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2312 stepsize = 1;
2313 else
2314 stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000);
2316 assert(stepsize > 0);
2318 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2319 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2320 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2323 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2324 i += stepsize) {
2325 DWORD nFrame = lInitialFrames + i;
2327 assert(nFrame < This->nIdxRecords);
2329 idx.ckid = listtypeAVIRECORD;
2330 idx.dwFlags = AVIIF_LIST;
2331 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2332 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2333 - This->dwMoviChunkPos;
2334 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2335 return AVIERR_FILEWRITE;
2337 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2338 pStream = This->ppStreams[nStream];
2340 /* heave we reached start of this stream? */
2341 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2342 continue;
2344 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2345 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2347 /* reached end of this stream? */
2348 if (pStream->lLastFrame <= nFrame)
2349 continue;
2351 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2352 pStream->sInfo.dwFormatChangeCount != 0 &&
2353 pStream->idxFmtChanges != NULL) {
2354 DWORD pos;
2356 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2357 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2358 idx.dwFlags = AVIIF_NOTIME;
2359 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2360 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2361 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2362 - This->dwMoviChunkPos;
2364 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2365 return AVIERR_FILEWRITE;
2366 break;
2369 } /* if have formatchanges */
2371 idx.ckid = pStream->idxFrames[nFrame].ckid;
2372 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2373 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2374 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2375 - This->dwMoviChunkPos;
2376 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2377 return AVIERR_FILEWRITE;
2380 } else {
2381 /* not interleaved -- write index for each stream at once */
2382 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2383 pStream = This->ppStreams[nStream];
2385 for (n = 0; n <= pStream->lLastFrame; n++) {
2386 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2387 (pStream->sInfo.dwFormatChangeCount != 0)) {
2388 DWORD pos;
2390 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2391 if (pStream->idxFmtChanges[pos].ckid == n) {
2392 idx.dwFlags = AVIIF_NOTIME;
2393 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2394 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2395 idx.dwChunkOffset =
2396 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2397 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2398 return AVIERR_FILEWRITE;
2399 break;
2402 } /* if have formatchanges */
2404 idx.ckid = pStream->idxFrames[n].ckid;
2405 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2406 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2407 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2408 - This->dwMoviChunkPos;
2410 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2411 return AVIERR_FILEWRITE;
2414 } /* if not interleaved */
2416 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2417 return AVIERR_FILEWRITE;
2419 return AVIERR_OK;
2422 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2424 UINT i;
2425 UINT nStream;
2427 /* pre-condition */
2428 assert(lSkip >= 0);
2430 if (fcc != 0) {
2431 /* search the number of the specified stream */
2432 nStream = (ULONG)-1;
2433 for (i = 0; i < This->fInfo.dwStreams; i++) {
2434 assert(This->ppStreams[i] != NULL);
2436 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2437 if (lSkip == 0) {
2438 nStream = i;
2439 break;
2440 } else
2441 lSkip--;
2444 } else
2445 nStream = lSkip;
2447 return nStream;
2450 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2452 UINT i;
2454 /* pre-conditions */
2455 assert(This != NULL);
2457 This->fInfo.dwMaxBytesPerSec = 0;
2458 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2459 This->fInfo.dwSuggestedBufferSize = 0;
2460 This->fInfo.dwWidth = 0;
2461 This->fInfo.dwHeight = 0;
2462 This->fInfo.dwScale = 0;
2463 This->fInfo.dwRate = 0;
2464 This->fInfo.dwLength = 0;
2465 This->dwInitialFrames = 0;
2467 for (i = 0; i < This->fInfo.dwStreams; i++) {
2468 AVISTREAMINFOW *psi;
2469 DWORD n;
2471 /* pre-conditions */
2472 assert(This->ppStreams[i] != NULL);
2474 psi = &This->ppStreams[i]->sInfo;
2475 assert(psi->dwScale != 0);
2476 assert(psi->dwRate != 0);
2478 if (i == 0) {
2479 /* use first stream timings as base */
2480 This->fInfo.dwScale = psi->dwScale;
2481 This->fInfo.dwRate = psi->dwRate;
2482 This->fInfo.dwLength = psi->dwLength;
2483 } else {
2484 n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface,
2485 &This->ppStreams[i]->IAVIStream_iface, psi->dwLength);
2486 if (This->fInfo.dwLength < n)
2487 This->fInfo.dwLength = n;
2490 if (This->dwInitialFrames < psi->dwInitialFrames)
2491 This->dwInitialFrames = psi->dwInitialFrames;
2493 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2494 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2496 if (psi->dwSampleSize != 0) {
2497 /* fixed sample size -- exact computation */
2498 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2499 psi->dwScale);
2500 } else {
2501 /* variable sample size -- only upper limit */
2502 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2503 psi->dwRate, psi->dwScale);
2505 /* update dimensions */
2506 n = psi->rcFrame.right - psi->rcFrame.left;
2507 if (This->fInfo.dwWidth < n)
2508 This->fInfo.dwWidth = n;
2509 n = psi->rcFrame.bottom - psi->rcFrame.top;
2510 if (This->fInfo.dwHeight < n)
2511 This->fInfo.dwHeight = n;
2516 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2517 FOURCC ckid, DWORD flags, LPCVOID buffer,
2518 LONG size)
2520 MMCKINFO ck;
2522 ck.ckid = ckid;
2523 ck.cksize = size;
2524 ck.fccType = 0;
2526 /* if no frame/block is already written, we must compute start of movi chunk */
2527 if (This->paf->dwMoviChunkPos == 0)
2528 AVIFILE_ComputeMoviStart(This->paf);
2530 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2531 return AVIERR_FILEWRITE;
2533 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2534 return AVIERR_FILEWRITE;
2535 if (buffer != NULL && size > 0) {
2536 if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2537 return AVIERR_FILEWRITE;
2539 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2540 return AVIERR_FILEWRITE;
2542 This->paf->fDirty = TRUE;
2543 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2545 return AVIFILE_AddFrame(This, ckid, size,
2546 ck.dwDataOffset - 2 * sizeof(DWORD), flags);