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
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.
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.
45 #include "avifile_private.h"
46 #include "extrachunk.h"
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
54 #define IDX_PER_BLOCK 2730
57 /***********************************************************************/
59 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
60 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
61 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
62 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
63 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
64 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
65 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
66 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
67 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
68 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
70 static const struct IAVIFileVtbl iavift = {
71 IAVIFile_fnQueryInterface,
76 IAVIFile_fnCreateStream,
80 IAVIFile_fnDeleteStream
83 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
84 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
85 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface);
86 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
87 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
88 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
89 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
90 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
91 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
93 static const struct IPersistFileVtbl ipersistft = {
94 IPersistFile_fnQueryInterface,
95 IPersistFile_fnAddRef,
96 IPersistFile_fnRelease,
97 IPersistFile_fnGetClassID,
98 IPersistFile_fnIsDirty,
101 IPersistFile_fnSaveCompleted,
102 IPersistFile_fnGetCurFile
105 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
106 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
107 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface);
108 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
109 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
110 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
111 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
112 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
113 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
114 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
115 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
116 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
117 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
118 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
120 static const struct IAVIStreamVtbl iavist = {
121 IAVIStream_fnQueryInterface,
123 IAVIStream_fnRelease,
126 IAVIStream_fnFindSample,
127 IAVIStream_fnReadFormat,
128 IAVIStream_fnSetFormat,
132 IAVIStream_fnReadData,
133 IAVIStream_fnWriteData,
137 typedef struct _IAVIFileImpl IAVIFileImpl;
139 typedef struct _IPersistFileImpl {
141 const IPersistFileVtbl *lpVtbl;
143 /* IPersistFile stuff */
147 typedef struct _IAVIStreamImpl {
149 const IAVIStreamVtbl *lpVtbl;
152 /* IAVIStream stuff */
154 DWORD nStream; /* the n-th stream in file */
155 AVISTREAMINFOW sInfo;
160 LPVOID lpHandlerData;
166 DWORD cbBuffer; /* size of lpBuffer */
167 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
169 LONG lLastFrame; /* last correct index in idxFrames */
170 AVIINDEXENTRY *idxFrames;
171 DWORD nIdxFrames; /* upper index limit of idxFrames */
172 AVIINDEXENTRY *idxFmtChanges;
173 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
176 struct _IAVIFileImpl {
177 IAVIFile IAVIFile_iface;
180 /* IAVIFile stuff... */
181 IPersistFileImpl iPersistFile;
184 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
186 EXTRACHUNKS fileextra;
188 DWORD dwMoviChunkPos; /* some stuff for saving ... */
190 DWORD dwNextFramePos;
191 DWORD dwInitialFrames;
193 MMCKINFO ckLastRecord;
194 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
195 DWORD nIdxRecords; /* current fill level */
196 DWORD cbIdxRecords; /* size of idxRecords */
198 /* IPersistFile stuff ... */
205 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
207 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
210 /***********************************************************************/
212 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
213 DWORD offset, DWORD flags);
214 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
215 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
216 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
217 const AVISTREAMINFOW *asi);
218 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
219 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
220 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
221 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
222 LONG count, DWORD pos, BOOL *bAbsolute);
223 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
224 LPVOID buffer, DWORD size);
225 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
227 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
228 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
229 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
231 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
232 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
233 FOURCC ckid, DWORD flags, LPCVOID buffer,
236 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
241 assert(riid != NULL && ppv != NULL);
245 pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
247 return AVIERR_MEMORY;
249 pfile->IAVIFile_iface.lpVtbl = &iavift;
251 pfile->iPersistFile.lpVtbl = &ipersistft;
252 pfile->iPersistFile.paf = pfile;
254 hr = IAVIFile_QueryInterface(&pfile->IAVIFile_iface, riid, ppv);
256 HeapFree(GetProcessHeap(), 0, pfile);
261 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
264 IAVIFileImpl *This = impl_from_IAVIFile(iface);
266 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
268 if (IsEqualGUID(&IID_IUnknown, refiid) ||
269 IsEqualGUID(&IID_IAVIFile, refiid)) {
271 IAVIFile_AddRef(iface);
274 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
275 *obj = &This->iPersistFile;
276 IAVIFile_AddRef(iface);
281 return E_NOINTERFACE;
284 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
286 IAVIFileImpl *This = impl_from_IAVIFile(iface);
287 ULONG ref = InterlockedIncrement(&This->ref);
289 TRACE("(%p) ref=%d\n", This, ref);
294 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
296 IAVIFileImpl *This = impl_from_IAVIFile(iface);
297 ULONG ref = InterlockedDecrement(&This->ref);
300 TRACE("(%p) ref=%d\n", This, ref);
304 /* need to write headers to file */
305 AVIFILE_SaveFile(This);
308 for (i = 0; i < This->fInfo.dwStreams; i++) {
309 if (This->ppStreams[i] != NULL) {
310 if (This->ppStreams[i]->ref != 0) {
311 ERR(": someone has still %u reference to stream %u (%p)!\n",
312 This->ppStreams[i]->ref, i, This->ppStreams[i]);
314 AVIFILE_DestructAVIStream(This->ppStreams[i]);
315 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
316 This->ppStreams[i] = NULL;
320 if (This->idxRecords != NULL) {
321 HeapFree(GetProcessHeap(), 0, This->idxRecords);
322 This->idxRecords = NULL;
323 This->nIdxRecords = 0;
326 if (This->fileextra.lp != NULL) {
327 HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
328 This->fileextra.lp = NULL;
329 This->fileextra.cb = 0;
332 HeapFree(GetProcessHeap(), 0, This->szFileName);
333 This->szFileName = NULL;
335 if (This->hmmio != NULL) {
336 mmioClose(This->hmmio, 0);
340 HeapFree(GetProcessHeap(), 0, This);
345 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
347 IAVIFileImpl *This = impl_from_IAVIFile(iface);
349 TRACE("(%p,%p,%d)\n",iface,afi,size);
352 return AVIERR_BADPARAM;
354 return AVIERR_BADSIZE;
356 AVIFILE_UpdateInfo(This);
358 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
360 if ((DWORD)size < sizeof(This->fInfo))
361 return AVIERR_BUFFERTOOSMALL;
365 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
368 IAVIFileImpl *This = impl_from_IAVIFile(iface);
371 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
373 if (avis == NULL || lParam < 0)
374 return AVIERR_BADPARAM;
376 nStream = AVIFILE_SearchStream(This, fccType, lParam);
378 /* Does the requested stream exist? */
379 if (nStream < This->fInfo.dwStreams &&
380 This->ppStreams[nStream] != NULL) {
381 *avis = (PAVISTREAM)This->ppStreams[nStream];
382 IAVIStream_AddRef(*avis);
387 /* Sorry, but the specified stream doesn't exist */
388 return AVIERR_NODATA;
391 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
394 IAVIFileImpl *This = impl_from_IAVIFile(iface);
397 TRACE("(%p,%p,%p)\n", iface, avis, asi);
399 /* check parameters */
400 if (avis == NULL || asi == NULL)
401 return AVIERR_BADPARAM;
405 /* Does the user have write permission? */
406 if ((This->uMode & MMIO_RWMODE) == 0)
407 return AVIERR_READONLY;
409 /* Can we add another stream? */
410 n = This->fInfo.dwStreams;
411 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
412 /* already reached max nr of streams
413 * or have already written frames to disk */
414 return AVIERR_UNSUPPORTED;
417 /* check AVISTREAMINFO for some really needed things */
418 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
419 return AVIERR_BADFORMAT;
421 /* now it seems to be save to add the stream */
422 assert(This->ppStreams[n] == NULL);
423 This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
424 sizeof(IAVIStreamImpl));
425 if (This->ppStreams[n] == NULL)
426 return AVIERR_MEMORY;
428 /* initialize the new allocated stream */
429 AVIFILE_ConstructAVIStream(This, n, asi);
431 This->fInfo.dwStreams++;
434 /* update our AVIFILEINFO structure */
435 AVIFILE_UpdateInfo(This);
438 *avis = (PAVISTREAM)This->ppStreams[n];
439 IAVIStream_AddRef(*avis);
444 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
446 IAVIFileImpl *This = impl_from_IAVIFile(iface);
448 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
450 /* check parameters */
452 return AVIERR_BADPARAM;
454 return AVIERR_BADSIZE;
456 /* Do we have write permission? */
457 if ((This->uMode & MMIO_RWMODE) == 0)
458 return AVIERR_READONLY;
462 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
465 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
467 IAVIFileImpl *This = impl_from_IAVIFile(iface);
469 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
471 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
474 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
476 IAVIFileImpl *This = impl_from_IAVIFile(iface);
478 TRACE("(%p)\n",iface);
480 if ((This->uMode & MMIO_RWMODE) == 0)
481 return AVIERR_READONLY;
485 /* no frames written to any stream? -- compute start of 'movi'-chunk */
486 if (This->dwMoviChunkPos == 0)
487 AVIFILE_ComputeMoviStart(This);
489 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
491 /* already written frames to any stream, ... */
492 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
493 /* close last record */
494 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
495 return AVIERR_FILEWRITE;
497 AVIFILE_AddRecord(This);
499 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
500 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
503 /* write out a new record into file, but don't close it */
504 This->ckLastRecord.cksize = 0;
505 This->ckLastRecord.fccType = listtypeAVIRECORD;
506 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
507 return AVIERR_FILEWRITE;
508 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
509 return AVIERR_FILEWRITE;
510 This->dwNextFramePos += 3 * sizeof(DWORD);
515 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
517 IAVIFileImpl *This = impl_from_IAVIFile(iface);
520 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
522 /* check parameter */
524 return AVIERR_BADPARAM;
526 /* Have user write permissions? */
527 if ((This->uMode & MMIO_RWMODE) == 0)
528 return AVIERR_READONLY;
530 nStream = AVIFILE_SearchStream(This, fccType, lParam);
532 /* Does the requested stream exist? */
533 if (nStream < This->fInfo.dwStreams &&
534 This->ppStreams[nStream] != NULL) {
535 /* ... so delete it now */
536 HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
538 if (This->fInfo.dwStreams - nStream > 0)
539 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
540 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
542 This->ppStreams[This->fInfo.dwStreams] = NULL;
543 This->fInfo.dwStreams--;
546 /* This->fInfo will be updated further when asked for */
549 return AVIERR_NODATA;
552 /***********************************************************************/
554 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
555 REFIID refiid, LPVOID *obj)
557 IPersistFileImpl *This = (IPersistFileImpl *)iface;
559 assert(This->paf != NULL);
561 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
564 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
566 IPersistFileImpl *This = (IPersistFileImpl *)iface;
568 assert(This->paf != NULL);
570 return IAVIFile_AddRef((PAVIFILE)This->paf);
573 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
575 IPersistFileImpl *This = (IPersistFileImpl *)iface;
577 assert(This->paf != NULL);
579 return IAVIFile_Release((PAVIFILE)This->paf);
582 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
585 TRACE("(%p,%p)\n", iface, pClassID);
587 if (pClassID == NULL)
588 return AVIERR_BADPARAM;
590 *pClassID = CLSID_AVIFile;
595 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
597 IPersistFileImpl *This = (IPersistFileImpl *)iface;
599 TRACE("(%p)\n", iface);
601 assert(This->paf != NULL);
603 return (This->paf->fDirty ? S_OK : S_FALSE);
606 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
607 LPCOLESTR pszFileName, DWORD dwMode)
609 IPersistFileImpl *This = (IPersistFileImpl *)iface;
613 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
615 /* check parameter */
616 if (pszFileName == NULL)
617 return AVIERR_BADPARAM;
619 assert(This->paf != NULL);
620 if (This->paf->hmmio != NULL)
621 return AVIERR_ERROR; /* No reuse of this object for another file! */
623 /* remember mode and name */
624 This->paf->uMode = dwMode;
626 len = lstrlenW(pszFileName) + 1;
627 This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
628 if (This->paf->szFileName == NULL)
629 return AVIERR_MEMORY;
630 lstrcpyW(This->paf->szFileName, pszFileName);
632 /* try to open the file */
633 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
634 MMIO_ALLOCBUF | dwMode);
635 if (This->paf->hmmio == NULL) {
636 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
639 len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1,
640 NULL, 0, NULL, NULL);
641 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
642 if (szFileName == NULL)
643 return AVIERR_MEMORY;
645 WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName,
648 This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
649 HeapFree(GetProcessHeap(), 0, szFileName);
650 if (This->paf->hmmio == NULL)
651 return AVIERR_FILEOPEN;
654 /* should we create a new file? */
655 if (dwMode & OF_CREATE) {
656 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
657 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
661 return AVIFILE_LoadFile(This->paf);
664 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
665 LPCOLESTR pszFileName,BOOL fRemember)
667 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
669 /* We write directly to disk, so nothing to do. */
674 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
675 LPCOLESTR pszFileName)
677 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
679 /* We write directly to disk, so nothing to do. */
684 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
685 LPOLESTR *ppszFileName)
687 IPersistFileImpl *This = (IPersistFileImpl *)iface;
689 TRACE("(%p,%p)\n", iface, ppszFileName);
691 if (ppszFileName == NULL)
692 return AVIERR_BADPARAM;
694 *ppszFileName = NULL;
696 assert(This->paf != NULL);
698 if (This->paf->szFileName != NULL) {
699 int len = lstrlenW(This->paf->szFileName) + 1;
701 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
702 if (*ppszFileName == NULL)
703 return AVIERR_MEMORY;
705 strcpyW(*ppszFileName, This->paf->szFileName);
711 /***********************************************************************/
713 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
714 REFIID refiid, LPVOID *obj)
716 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
718 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
720 if (IsEqualGUID(&IID_IUnknown, refiid) ||
721 IsEqualGUID(&IID_IAVIStream, refiid)) {
723 IAVIStream_AddRef(iface);
727 /* FIXME: IAVIStreaming interface */
729 return OLE_E_ENUM_NOMORE;
732 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
734 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
735 ULONG ref = InterlockedIncrement(&This->ref);
737 TRACE("(%p) -> %d\n", iface, ref);
739 /* also add ref to parent, so that it doesn't kill us */
740 if (This->paf != NULL)
741 IAVIFile_AddRef(&This->paf->IAVIFile_iface);
746 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
748 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
749 ULONG ref = InterlockedDecrement(&This->ref);
751 TRACE("(%p) -> %d\n", iface, ref);
753 if (This->paf != NULL)
754 IAVIFile_Release(&This->paf->IAVIFile_iface);
759 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
762 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
764 /* This IAVIStream interface needs an AVIFile */
765 return AVIERR_UNSUPPORTED;
768 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
771 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
773 TRACE("(%p,%p,%d)\n", iface, psi, size);
776 return AVIERR_BADPARAM;
778 return AVIERR_BADSIZE;
780 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
782 if ((DWORD)size < sizeof(This->sInfo))
783 return AVIERR_BUFFERTOOSMALL;
787 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
790 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
794 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
796 if (flags & FIND_FROM_START) {
797 pos = This->sInfo.dwStart;
798 flags &= ~(FIND_FROM_START|FIND_PREV);
802 if (This->sInfo.dwSampleSize != 0) {
803 /* convert samples into block number with offset */
804 AVIFILE_SamplesToBlock(This, &pos, &offset);
807 if (flags & FIND_TYPE) {
808 if (flags & FIND_KEY) {
809 while (0 <= pos && pos <= This->lLastFrame) {
810 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
813 if (flags & FIND_NEXT)
818 } else if (flags & FIND_ANY) {
819 while (0 <= pos && pos <= This->lLastFrame) {
820 if (This->idxFrames[pos].dwChunkLength > 0)
823 if (flags & FIND_NEXT)
829 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
830 This->sInfo.fccType == streamtypeVIDEO) {
831 if (flags & FIND_NEXT) {
834 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
835 if (This->idxFmtChanges[n].ckid >= pos) {
836 pos = This->idxFmtChanges[n].ckid;
842 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
843 if (This->idxFmtChanges[n].ckid <= pos) {
844 pos = This->idxFmtChanges[n].ckid;
849 if (pos > (LONG)This->sInfo.dwStart)
850 return 0; /* format changes always for first frame */
858 if (pos < (LONG)This->sInfo.dwStart)
861 switch (flags & FIND_RET) {
864 pos = This->idxFrames[pos].dwChunkLength;
867 /* physical position */
868 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
869 + offset * This->sInfo.dwSampleSize;
873 if (This->sInfo.dwSampleSize)
874 pos = This->sInfo.dwSampleSize;
879 FIXME(": FIND_INDEX flag is not supported!\n");
880 /* This is an index in the index-table on disc. */
882 }; /* else logical position */
887 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
888 LPVOID format, LONG *formatsize)
890 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
892 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
894 if (formatsize == NULL)
895 return AVIERR_BADPARAM;
897 /* only interested in needed buffersize? */
898 if (format == NULL || *formatsize <= 0) {
899 *formatsize = This->cbFormat;
904 /* copy initial format (only as much as will fit) */
905 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
906 if (*(DWORD*)formatsize < This->cbFormat) {
907 *formatsize = This->cbFormat;
908 return AVIERR_BUFFERTOOSMALL;
911 /* Could format change? When yes will it change? */
912 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
913 pos > This->sInfo.dwStart) {
916 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
918 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
922 *formatsize = This->cbFormat;
926 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
927 LPVOID format, LONG formatsize)
929 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
931 LPBITMAPINFOHEADER lpbiNew = format;
933 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
935 /* check parameters */
936 if (format == NULL || formatsize <= 0)
937 return AVIERR_BADPARAM;
939 /* Do we have write permission? */
940 if ((This->paf->uMode & MMIO_RWMODE) == 0)
941 return AVIERR_READONLY;
943 /* can only set format before frame is written! */
944 if (This->lLastFrame > pos)
945 return AVIERR_UNSUPPORTED;
947 /* initial format or a formatchange? */
948 if (This->lpFormat == NULL) {
950 if (This->paf->dwMoviChunkPos != 0)
951 return AVIERR_ERROR; /* user has used API in wrong sequence! */
953 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
954 if (This->lpFormat == NULL)
955 return AVIERR_MEMORY;
956 This->cbFormat = formatsize;
958 memcpy(This->lpFormat, format, formatsize);
960 /* update some infos about stream */
961 if (This->sInfo.fccType == streamtypeVIDEO) {
964 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
965 if (lDim < lpbiNew->biWidth)
966 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
967 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
968 if (lDim < lpbiNew->biHeight)
969 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
970 } else if (This->sInfo.fccType == streamtypeAUDIO)
971 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
976 LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
977 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
978 AVIPALCHANGE *lppc = NULL;
981 /* perhaps format change, check it ... */
982 if (This->cbFormat != formatsize)
983 return AVIERR_UNSUPPORTED;
985 /* no format change, only the initial one */
986 if (memcmp(This->lpFormat, format, formatsize) == 0)
989 /* check that's only the palette, which changes */
990 if (lpbiOld->biSize != lpbiNew->biSize ||
991 lpbiOld->biWidth != lpbiNew->biWidth ||
992 lpbiOld->biHeight != lpbiNew->biHeight ||
993 lpbiOld->biPlanes != lpbiNew->biPlanes ||
994 lpbiOld->biBitCount != lpbiNew->biBitCount ||
995 lpbiOld->biCompression != lpbiNew->biCompression ||
996 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
997 return AVIERR_UNSUPPORTED;
999 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1001 /* simply say all colors have changed */
1002 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
1003 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
1004 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1006 return AVIERR_MEMORY;
1008 lppc->bFirstEntry = 0;
1009 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
1011 for (n = 0; n < lpbiOld->biClrUsed; n++) {
1012 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
1013 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
1014 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
1015 lppc->peNew[n].peFlags = 0;
1018 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
1019 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
1020 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
1021 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
1023 HeapFree(GetProcessHeap(), 0, lppc);
1024 return AVIERR_FILEWRITE;
1027 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
1029 HeapFree(GetProcessHeap(), 0, lppc);
1031 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
1035 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
1036 LONG samples, LPVOID buffer,
1037 LONG buffersize, LPLONG bytesread,
1040 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1045 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
1046 buffersize, bytesread, samplesread);
1048 /* clear return parameters if given */
1049 if (bytesread != NULL)
1051 if (samplesread != NULL)
1054 /* check parameters */
1055 if ((LONG)This->sInfo.dwStart > start)
1056 return AVIERR_NODATA; /* couldn't read before start of stream */
1057 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1058 return AVIERR_NODATA; /* start is past end of stream */
1060 /* should we read as much as possible? */
1061 if (samples == -1) {
1062 /* User should know how much we have read */
1063 if (bytesread == NULL && samplesread == NULL)
1064 return AVIERR_BADPARAM;
1066 if (This->sInfo.dwSampleSize != 0)
1067 samples = buffersize / This->sInfo.dwSampleSize;
1072 /* limit to end of stream */
1073 if ((LONG)This->sInfo.dwLength < samples)
1074 samples = This->sInfo.dwLength;
1075 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1076 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1078 /* nothing to read? Then leave ... */
1082 if (This->sInfo.dwSampleSize != 0) {
1083 /* fixed samplesize -- we can read over frame/block boundaries */
1090 *bytesread = samples*This->sInfo.dwSampleSize;
1092 *samplesread = samples;
1096 /* convert start sample to block,offset pair */
1097 AVIFILE_SamplesToBlock(This, &block, &offset);
1099 /* convert samples to bytes */
1100 samples *= This->sInfo.dwSampleSize;
1102 while (samples > 0 && buffersize > 0) {
1104 if (block != This->dwCurrentFrame) {
1105 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1110 size = min((DWORD)samples, (DWORD)buffersize);
1111 blocksize = This->lpBuffer[1];
1112 TRACE("blocksize = %u\n",blocksize);
1113 size = min(size, blocksize - offset);
1114 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1118 buffer = ((LPBYTE)buffer)+size;
1122 /* fill out return parameters if given */
1123 if (bytesread != NULL)
1125 if (samplesread != NULL)
1126 *samplesread += size / This->sInfo.dwSampleSize;
1132 return AVIERR_BUFFERTOOSMALL;
1134 /* variable samplesize -- we can only read one full frame/block */
1138 assert(start <= This->lLastFrame);
1139 size = This->idxFrames[start].dwChunkLength;
1140 if (buffer != NULL && buffersize >= size) {
1141 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1144 } else if (buffer != NULL)
1145 return AVIERR_BUFFERTOOSMALL;
1147 /* fill out return parameters if given */
1148 if (bytesread != NULL)
1150 if (samplesread != NULL)
1151 *samplesread = samples;
1157 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
1158 LONG samples, LPVOID buffer,
1159 LONG buffersize, DWORD flags,
1161 LPLONG byteswritten)
1163 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1168 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1169 buffer, buffersize, flags, sampwritten, byteswritten);
1171 /* clear return parameters if given */
1172 if (sampwritten != NULL)
1174 if (byteswritten != NULL)
1177 /* check parameters */
1178 if (buffer == NULL && (buffersize > 0 || samples > 0))
1179 return AVIERR_BADPARAM;
1181 /* Have we write permission? */
1182 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1183 return AVIERR_READONLY;
1185 switch (This->sInfo.fccType) {
1186 case streamtypeAUDIO:
1187 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1190 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1191 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1193 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1197 /* append to end of stream? */
1199 if (This->lLastFrame == -1)
1200 start = This->sInfo.dwStart;
1202 start = This->sInfo.dwLength;
1203 } else if (This->lLastFrame == -1)
1204 This->sInfo.dwStart = start;
1206 if (This->sInfo.dwSampleSize != 0) {
1207 /* fixed sample size -- audio like */
1208 if (samples * This->sInfo.dwSampleSize != buffersize)
1209 return AVIERR_BADPARAM;
1211 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1212 if (This->sInfo.dwLength != start)
1213 return AVIERR_UNSUPPORTED;
1215 /* Convert position to frame/block */
1216 start = This->lLastFrame + 1;
1218 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1219 FIXME(": not interleaved, could collect audio data!\n");
1222 /* variable sample size -- video like */
1224 return AVIERR_UNSUPPORTED;
1226 /* must we fill up with empty frames? */
1227 if (This->lLastFrame != -1) {
1228 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1230 while (start > This->lLastFrame + 1) {
1231 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1238 /* write the block now */
1239 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1240 if (SUCCEEDED(hr)) {
1241 /* fill out return parameters if given */
1242 if (sampwritten != NULL)
1243 *sampwritten = samples;
1244 if (byteswritten != NULL)
1245 *byteswritten = buffersize;
1251 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
1254 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1256 FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1258 /* check parameters */
1259 if (start < 0 || samples < 0)
1260 return AVIERR_BADPARAM;
1262 /* Delete before start of stream? */
1263 if (start + samples < This->sInfo.dwStart)
1266 /* Delete after end of stream? */
1267 if (start > This->sInfo.dwLength)
1270 /* For the rest we need write permissions */
1271 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1272 return AVIERR_READONLY;
1274 /* 1. overwrite the data with JUNK
1276 * if ISINTERLEAVED {
1277 * 2. concat all neighboured JUNK-blocks in this record to one
1278 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1279 * to start of this record, repeat this.
1281 * 2. concat all neighboured JUNK-blocks.
1282 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1283 * start of this block.
1287 return AVIERR_UNSUPPORTED;
1290 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
1291 LPVOID lp, LPLONG lpread)
1293 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1295 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1297 if (fcc == ckidSTREAMHANDLERDATA) {
1298 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1299 if (lp == NULL || *lpread <= 0) {
1300 *lpread = This->cbHandlerData;
1304 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1305 if (*lpread < This->cbHandlerData)
1306 return AVIERR_BUFFERTOOSMALL;
1309 return AVIERR_NODATA;
1311 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1314 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
1315 LPVOID lp, LONG size)
1317 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1319 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1321 /* check parameters */
1323 return AVIERR_BADPARAM;
1325 return AVIERR_BADSIZE;
1327 /* need write permission */
1328 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1329 return AVIERR_READONLY;
1331 /* already written something to this file? */
1332 if (This->paf->dwMoviChunkPos != 0) {
1333 /* the data will be inserted before the 'movi' chunk, so check for
1335 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1337 /* ckid,size => 2 * sizeof(DWORD) */
1338 dwPos += 2 * sizeof(DWORD) + size;
1339 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1340 return AVIERR_UNSUPPORTED; /* not enough space left */
1343 This->paf->fDirty = TRUE;
1345 if (fcc == ckidSTREAMHANDLERDATA) {
1346 if (This->lpHandlerData != NULL) {
1347 FIXME(": handler data already set -- overwirte?\n");
1348 return AVIERR_UNSUPPORTED;
1351 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1352 if (This->lpHandlerData == NULL)
1353 return AVIERR_MEMORY;
1354 This->cbHandlerData = size;
1355 memcpy(This->lpHandlerData, lp, size);
1359 return WriteExtraChunk(&This->extra, fcc, lp, size);
1362 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
1363 LPAVISTREAMINFOW info, LONG infolen)
1365 FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1370 /***********************************************************************/
1372 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1376 /* pre-conditions */
1377 assert(This != NULL);
1379 switch (TWOCCFromFOURCC(ckid)) {
1381 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1382 flags |= AVIIF_KEYFRAME;
1384 case cktypeDIBcompressed:
1385 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1386 flags &= ~AVIIF_KEYFRAME;
1388 case cktypePALchange:
1389 if (This->sInfo.fccType != streamtypeVIDEO) {
1390 ERR(": found palette change in non-video stream!\n");
1391 return AVIERR_BADFORMAT;
1394 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1395 DWORD new_count = This->nIdxFmtChanges + 16;
1398 if (This->idxFmtChanges == NULL) {
1399 This->idxFmtChanges =
1400 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
1401 if (!This->idxFmtChanges) return AVIERR_MEMORY;
1403 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
1404 new_count * sizeof(AVIINDEXENTRY));
1405 if (!new_buffer) return AVIERR_MEMORY;
1406 This->idxFmtChanges = new_buffer;
1408 This->nIdxFmtChanges = new_count;
1411 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1412 n = ++This->sInfo.dwFormatChangeCount;
1413 This->idxFmtChanges[n].ckid = This->lLastFrame;
1414 This->idxFmtChanges[n].dwFlags = 0;
1415 This->idxFmtChanges[n].dwChunkOffset = offset;
1416 This->idxFmtChanges[n].dwChunkLength = size;
1419 case cktypeWAVEbytes:
1420 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1421 flags |= AVIIF_KEYFRAME;
1424 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1428 /* first frame is always a keyframe */
1429 if (This->lLastFrame == -1)
1430 flags |= AVIIF_KEYFRAME;
1432 if (This->sInfo.dwSuggestedBufferSize < size)
1433 This->sInfo.dwSuggestedBufferSize = size;
1435 /* get memory for index */
1436 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1437 This->nIdxFrames += 512;
1438 if (This->idxFrames == NULL)
1439 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1441 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1442 This->nIdxFrames * sizeof(AVIINDEXENTRY));
1443 if (This->idxFrames == NULL)
1444 return AVIERR_MEMORY;
1448 This->idxFrames[This->lLastFrame].ckid = ckid;
1449 This->idxFrames[This->lLastFrame].dwFlags = flags;
1450 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1451 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1453 /* update AVISTREAMINFO structure if necessary */
1454 if (This->sInfo.dwLength <= This->lLastFrame)
1455 This->sInfo.dwLength = This->lLastFrame + 1;
1460 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1462 /* pre-conditions */
1463 assert(This != NULL && This->ppStreams[0] != NULL);
1465 if (This->idxRecords == NULL || This->cbIdxRecords == 0) {
1466 This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY);
1467 This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords);
1468 if (This->idxRecords == NULL)
1469 return AVIERR_MEMORY;
1472 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1474 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1475 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1476 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1477 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1478 This->idxRecords[This->nIdxRecords].dwChunkLength =
1479 This->ckLastRecord.cksize;
1480 This->nIdxRecords++;
1485 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1490 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1491 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1493 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1494 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1496 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1497 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1498 dwPos += ((pStream->cbFormat + 1) & ~1U);
1499 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1500 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1501 if (lstrlenW(pStream->sInfo.szName) > 0)
1502 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1505 if (This->dwMoviChunkPos == 0) {
1506 This->dwNextFramePos = dwPos;
1508 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1509 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1510 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1512 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1518 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1520 IAVIStreamImpl *pstream;
1522 /* pre-conditions */
1523 assert(paf != NULL);
1524 assert(nr < MAX_AVISTREAMS);
1525 assert(paf->ppStreams[nr] != NULL);
1527 pstream = paf->ppStreams[nr];
1529 pstream->lpVtbl = &iavist;
1532 pstream->nStream = nr;
1533 pstream->dwCurrentFrame = (DWORD)-1;
1534 pstream->lLastFrame = -1;
1537 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1539 if (asi->dwLength > 0) {
1540 /* pre-allocate mem for frame-index structure */
1541 pstream->idxFrames =
1542 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1543 if (pstream->idxFrames != NULL)
1544 pstream->nIdxFrames = asi->dwLength;
1546 if (asi->dwFormatChangeCount > 0) {
1547 /* pre-allocate mem for formatchange-index structure */
1548 pstream->idxFmtChanges =
1549 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1550 if (pstream->idxFmtChanges != NULL)
1551 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1554 /* These values will be computed */
1555 pstream->sInfo.dwLength = 0;
1556 pstream->sInfo.dwSuggestedBufferSize = 0;
1557 pstream->sInfo.dwFormatChangeCount = 0;
1558 pstream->sInfo.dwEditCount = 1;
1559 if (pstream->sInfo.dwSampleSize > 0)
1560 SetRectEmpty(&pstream->sInfo.rcFrame);
1563 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1566 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1568 /* pre-conditions */
1569 assert(This != NULL);
1571 This->dwCurrentFrame = (DWORD)-1;
1572 This->lLastFrame = -1;
1574 if (This->idxFrames != NULL) {
1575 HeapFree(GetProcessHeap(), 0, This->idxFrames);
1576 This->idxFrames = NULL;
1577 This->nIdxFrames = 0;
1579 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
1580 This->idxFmtChanges = NULL;
1581 if (This->lpBuffer != NULL) {
1582 HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1583 This->lpBuffer = NULL;
1586 if (This->lpHandlerData != NULL) {
1587 HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1588 This->lpHandlerData = NULL;
1589 This->cbHandlerData = 0;
1591 if (This->extra.lp != NULL) {
1592 HeapFree(GetProcessHeap(), 0, This->extra.lp);
1593 This->extra.lp = NULL;
1596 if (This->lpFormat != NULL) {
1597 HeapFree(GetProcessHeap(), 0, This->lpFormat);
1598 This->lpFormat = NULL;
1603 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1605 MainAVIHeader MainAVIHdr;
1610 IAVIStreamImpl *pStream;
1614 if (This->hmmio == NULL)
1615 return AVIERR_FILEOPEN;
1617 /* initialize stream ptr's */
1618 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1620 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1621 ckRIFF.fccType = formtypeAVI;
1622 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1623 ERR(": not an AVI!\n");
1624 return AVIERR_FILEREAD;
1627 /* get "LIST" "hdrl" */
1628 ckLIST1.fccType = listtypeAVIHEADER;
1629 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1633 /* get "avih" chunk */
1634 ck.ckid = ckidAVIMAINHDR;
1635 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1639 if (ck.cksize != sizeof(MainAVIHdr)) {
1640 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1641 return AVIERR_BADFORMAT;
1643 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1644 return AVIERR_FILEREAD;
1646 /* check for MAX_AVISTREAMS limit */
1647 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1648 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1649 return AVIERR_UNSUPPORTED;
1652 /* adjust permissions if copyrighted material in file */
1653 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1654 This->uMode &= ~MMIO_RWMODE;
1655 This->uMode |= MMIO_READ;
1658 /* convert MainAVIHeader into AVIFILINFOW */
1659 memset(&This->fInfo, 0, sizeof(This->fInfo));
1660 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1661 This->fInfo.dwScale = 1000000;
1662 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1663 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1664 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1665 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1666 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1667 This->fInfo.dwSuggestedBufferSize = 0;
1668 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1669 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1670 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1671 sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
1673 /* go back to into header list */
1674 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1675 return AVIERR_FILEREAD;
1677 /* foreach stream exists a "LIST","strl" chunk */
1678 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1679 /* get next nested chunk in this "LIST","strl" */
1680 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1681 return AVIERR_FILEREAD;
1683 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1684 if (ckLIST2.ckid == FOURCC_LIST &&
1685 ckLIST2.fccType == listtypeSTREAMHEADER) {
1686 pStream = This->ppStreams[nStream] =
1687 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1688 if (pStream == NULL)
1689 return AVIERR_MEMORY;
1690 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1693 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1695 case ckidSTREAMHANDLERDATA:
1696 if (pStream->lpHandlerData != NULL)
1697 return AVIERR_BADFORMAT;
1698 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1699 if (pStream->lpHandlerData == NULL)
1700 return AVIERR_MEMORY;
1701 pStream->cbHandlerData = ck.cksize;
1703 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1704 return AVIERR_FILEREAD;
1706 case ckidSTREAMFORMAT:
1707 if (pStream->lpFormat != NULL)
1708 return AVIERR_BADFORMAT;
1712 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1713 if (pStream->lpFormat == NULL)
1714 return AVIERR_MEMORY;
1715 pStream->cbFormat = ck.cksize;
1717 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1718 return AVIERR_FILEREAD;
1720 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1721 LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1723 /* some corrections to the video format */
1724 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1725 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1726 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1727 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1728 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1729 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1730 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1731 lpbi->biCompression = BI_RLE8;
1733 if (lpbi->biCompression == BI_RGB &&
1734 (pStream->sInfo.fccHandler == 0 ||
1735 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1736 pStream->sInfo.fccHandler = comptypeDIB;
1738 /* init rcFrame if it's empty */
1739 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1740 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1743 case ckidSTREAMHEADER:
1745 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1746 static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
1748 AVIStreamHeader streamHdr;
1753 if (ck.cksize > sizeof(streamHdr))
1754 n = sizeof(streamHdr);
1756 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1757 return AVIERR_FILEREAD;
1759 pStream->sInfo.fccType = streamHdr.fccType;
1760 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1761 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1762 pStream->sInfo.wPriority = streamHdr.wPriority;
1763 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1764 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1765 pStream->sInfo.dwScale = streamHdr.dwScale;
1766 pStream->sInfo.dwRate = streamHdr.dwRate;
1767 pStream->sInfo.dwStart = streamHdr.dwStart;
1768 pStream->sInfo.dwLength = streamHdr.dwLength;
1769 pStream->sInfo.dwSuggestedBufferSize = 0;
1770 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1771 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1772 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1773 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1774 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1775 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1776 pStream->sInfo.dwEditCount = 0;
1777 pStream->sInfo.dwFormatChangeCount = 0;
1779 /* generate description for stream like "filename.avi Type #n" */
1780 if (streamHdr.fccType == streamtypeVIDEO)
1781 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
1782 else if (streamHdr.fccType == streamtypeAUDIO)
1783 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
1785 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1787 /* get count of this streamtype up to this stream */
1789 for (n = nStream; 0 <= n; n--) {
1790 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1794 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1796 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1797 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1798 AVIFILE_BasenameW(This->szFileName), szType, count);
1801 case ckidSTREAMNAME:
1802 { /* streamname will be saved as ASCII string */
1803 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1805 return AVIERR_MEMORY;
1807 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1809 HeapFree(GetProcessHeap(), 0, str);
1810 return AVIERR_FILEREAD;
1813 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1814 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1816 HeapFree(GetProcessHeap(), 0, str);
1819 case ckidAVIPADDING:
1820 case mmioFOURCC('p','a','d','d'):
1823 WARN(": found extra chunk 0x%08X\n", ck.ckid);
1824 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1828 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1830 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1831 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1832 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1833 pStream->sInfo.dwScale = 1;
1834 pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1836 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1837 return AVIERR_FILEREAD;
1840 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1841 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1845 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1846 return AVIERR_FILEREAD;
1849 /* read any extra headers in "LIST","hdrl" */
1850 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1851 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1852 return AVIERR_FILEREAD;
1854 /* search "LIST","movi" chunk in "RIFF","AVI " */
1855 ckLIST1.fccType = listtypeAVIMOVIE;
1856 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1861 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1862 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1863 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1864 return AVIERR_FILEREAD;
1866 /* try to find an index */
1867 ck.ckid = ckidAVINEWINDEX;
1868 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1869 &ck, &ckRIFF, MMIO_FINDCHUNK);
1870 if (SUCCEEDED(hr) && ck.cksize > 0) {
1871 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1872 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1875 /* when we haven't found an index or it's bad, then build one
1876 * by parsing 'movi' chunk */
1877 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1878 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1879 This->ppStreams[nStream]->lLastFrame = -1;
1881 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1882 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1883 return AVIERR_FILEREAD;
1886 /* seek through the 'movi' list until end */
1887 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1888 if (ck.ckid != FOURCC_LIST) {
1889 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1890 nStream = StreamFromFOURCC(ck.ckid);
1892 if (nStream > This->fInfo.dwStreams)
1893 return AVIERR_BADFORMAT;
1895 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1896 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1898 nStream = StreamFromFOURCC(ck.ckid);
1899 WARN(": file seems to be truncated!\n");
1900 if (nStream <= This->fInfo.dwStreams &&
1901 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1902 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1903 if (ck.cksize != -1) {
1904 ck.cksize -= ck.dwDataOffset;
1905 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1907 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1908 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1916 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1918 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1919 if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1920 This->fInfo.dwSuggestedBufferSize = sugbuf;
1923 /* find other chunks */
1924 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1929 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1933 HRESULT hr = AVIERR_OK;
1934 BOOL bAbsolute = TRUE;
1936 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1938 return AVIERR_MEMORY;
1940 /* adjust limits for index tables, so that inserting will be faster */
1941 for (n = 0; n < This->fInfo.dwStreams; n++) {
1942 IAVIStreamImpl *pStream = This->ppStreams[n];
1944 pStream->lLastFrame = -1;
1946 if (pStream->idxFrames != NULL) {
1947 HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1948 pStream->idxFrames = NULL;
1949 pStream->nIdxFrames = 0;
1952 if (pStream->sInfo.dwSampleSize != 0) {
1953 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1954 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1955 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1956 pStream->nIdxFrames =
1957 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1960 pStream->nIdxFrames = pStream->sInfo.dwLength;
1962 pStream->idxFrames =
1963 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1964 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1965 pStream->nIdxFrames = 0;
1966 HeapFree(GetProcessHeap(), 0, lp);
1967 return AVIERR_MEMORY;
1973 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1975 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1976 hr = AVIERR_FILEREAD;
1981 if (pos == (DWORD)-1)
1982 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1984 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1988 HeapFree(GetProcessHeap(), 0, lp);
1991 for (n = 0; n < This->fInfo.dwStreams; n++) {
1992 IAVIStreamImpl *pStream = This->ppStreams[n];
1994 if (pStream->sInfo.dwSampleSize == 0 &&
1995 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1996 ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1997 n, pStream->sInfo.dwLength, pStream->lLastFrame);
2003 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
2004 LONG count, DWORD pos, BOOL *bAbsolute)
2007 return AVIERR_BADPARAM;
2009 for (; count > 0; count--, lp++) {
2010 WORD nStream = StreamFromFOURCC(lp->ckid);
2012 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
2013 continue; /* skip these */
2015 if (nStream > This->fInfo.dwStreams)
2016 return AVIERR_BADFORMAT;
2018 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
2022 lp->dwChunkOffset += sizeof(DWORD);
2024 lp->dwChunkOffset += pos;
2026 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
2027 return AVIERR_MEMORY;
2033 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
2034 LPVOID buffer, DWORD size)
2036 /* pre-conditions */
2037 assert(This != NULL);
2038 assert(This->paf != NULL);
2039 assert(This->paf->hmmio != NULL);
2040 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2041 assert(pos <= This->lLastFrame);
2043 /* should we read as much as block gives us? */
2044 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2045 size = This->idxFrames[pos].dwChunkLength;
2047 /* read into out own buffer or given one? */
2048 if (buffer == NULL) {
2049 /* we also read the chunk */
2050 size += 2 * sizeof(DWORD);
2052 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2053 if (This->lpBuffer == NULL || This->cbBuffer < size) {
2054 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2056 if (This->lpBuffer == NULL) {
2057 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2058 if (!This->lpBuffer) return AVIERR_MEMORY;
2060 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
2061 if (!new_buffer) return AVIERR_MEMORY;
2062 This->lpBuffer = new_buffer;
2064 This->cbBuffer = maxSize;
2067 /* now read the complete chunk into our buffer */
2068 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2069 return AVIERR_FILEREAD;
2070 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2071 return AVIERR_FILEREAD;
2073 /* check if it was the correct block which we have read */
2074 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2075 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2076 ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
2077 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2078 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2079 This->idxFrames[pos].dwChunkLength);
2080 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n",
2081 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2082 return AVIERR_FILEREAD;
2085 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2086 return AVIERR_FILEREAD;
2087 if (mmioRead(This->paf->hmmio, buffer, size) != size)
2088 return AVIERR_FILEREAD;
2094 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2098 /* pre-conditions */
2099 assert(This != NULL);
2100 assert(pos != NULL);
2101 assert(offset != NULL);
2102 assert(This->sInfo.dwSampleSize != 0);
2103 assert(*pos >= This->sInfo.dwStart);
2105 /* convert start sample to start bytes */
2106 (*offset) = (*pos) - This->sInfo.dwStart;
2107 (*offset) *= This->sInfo.dwSampleSize;
2109 /* convert bytes to block number */
2110 for (block = 0; block <= This->lLastFrame; block++) {
2111 if (This->idxFrames[block].dwChunkLength <= *offset)
2112 (*offset) -= This->idxFrames[block].dwChunkLength;
2120 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2122 MainAVIHeader MainAVIHdr;
2123 IAVIStreamImpl* pStream;
2132 /* initialize some things */
2133 if (This->dwMoviChunkPos == 0)
2134 AVIFILE_ComputeMoviStart(This);
2136 /* written one record to much? */
2137 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2138 This->dwNextFramePos -= 3 * sizeof(DWORD);
2139 if (This->nIdxRecords > 0)
2140 This->nIdxRecords--;
2143 AVIFILE_UpdateInfo(This);
2145 assert(This->fInfo.dwScale != 0);
2147 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2148 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2149 This->fInfo.dwScale);
2150 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2151 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2152 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2153 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2154 MainAVIHdr.dwInitialFrames = 0;
2155 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2156 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2157 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2158 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2159 MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2161 /* now begin writing ... */
2162 mmioSeek(This->hmmio, 0, SEEK_SET);
2166 ckRIFF.fccType = formtypeAVI;
2167 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2168 return AVIERR_FILEWRITE;
2170 /* AVI headerlist */
2172 ckLIST1.fccType = listtypeAVIHEADER;
2173 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2174 return AVIERR_FILEWRITE;
2177 ck.ckid = ckidAVIMAINHDR;
2178 ck.cksize = sizeof(MainAVIHdr);
2180 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2181 return AVIERR_FILEWRITE;
2182 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2183 return AVIERR_FILEWRITE;
2184 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2185 return AVIERR_FILEWRITE;
2187 /* write the headers of each stream into a separate streamheader list */
2188 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2189 AVIStreamHeader strHdr;
2191 pStream = This->ppStreams[nStream];
2193 /* begin the new streamheader list */
2195 ckLIST2.fccType = listtypeSTREAMHEADER;
2196 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2197 return AVIERR_FILEWRITE;
2199 /* create an AVIStreamHeader from the AVSTREAMINFO */
2200 strHdr.fccType = pStream->sInfo.fccType;
2201 strHdr.fccHandler = pStream->sInfo.fccHandler;
2202 strHdr.dwFlags = pStream->sInfo.dwFlags;
2203 strHdr.wPriority = pStream->sInfo.wPriority;
2204 strHdr.wLanguage = pStream->sInfo.wLanguage;
2205 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2206 strHdr.dwScale = pStream->sInfo.dwScale;
2207 strHdr.dwRate = pStream->sInfo.dwRate;
2208 strHdr.dwStart = pStream->sInfo.dwStart;
2209 strHdr.dwLength = pStream->sInfo.dwLength;
2210 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2211 strHdr.dwQuality = pStream->sInfo.dwQuality;
2212 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2213 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2214 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2215 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2216 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2218 /* now write the AVIStreamHeader */
2219 ck.ckid = ckidSTREAMHEADER;
2220 ck.cksize = sizeof(strHdr);
2221 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2222 return AVIERR_FILEWRITE;
2223 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2224 return AVIERR_FILEWRITE;
2225 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2226 return AVIERR_FILEWRITE;
2228 /* ... the hopefully ever present streamformat ... */
2229 ck.ckid = ckidSTREAMFORMAT;
2230 ck.cksize = pStream->cbFormat;
2231 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2232 return AVIERR_FILEWRITE;
2233 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2234 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2235 return AVIERR_FILEWRITE;
2237 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2238 return AVIERR_FILEWRITE;
2240 /* ... some optional existing handler data ... */
2241 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2242 ck.ckid = ckidSTREAMHANDLERDATA;
2243 ck.cksize = pStream->cbHandlerData;
2244 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2245 return AVIERR_FILEWRITE;
2246 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2247 return AVIERR_FILEWRITE;
2248 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2249 return AVIERR_FILEWRITE;
2252 /* ... some optional additional extra chunk for this stream ... */
2253 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2254 /* the chunk header(s) are already in the structure */
2255 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2256 return AVIERR_FILEWRITE;
2259 /* ... an optional name for this stream ... */
2260 if (lstrlenW(pStream->sInfo.szName) > 0) {
2263 ck.ckid = ckidSTREAMNAME;
2264 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2265 if (ck.cksize & 1) /* align */
2267 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2268 return AVIERR_FILEWRITE;
2270 /* the streamname must be saved in ASCII not Unicode */
2271 str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
2273 return AVIERR_MEMORY;
2274 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2275 ck.cksize, NULL, NULL);
2277 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2278 HeapFree(GetProcessHeap(), 0, str);
2279 return AVIERR_FILEWRITE;
2282 HeapFree(GetProcessHeap(), 0, str);
2283 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2284 return AVIERR_FILEWRITE;
2287 /* close streamheader list for this stream */
2288 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2289 return AVIERR_FILEWRITE;
2290 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2292 /* close the aviheader list */
2293 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2294 return AVIERR_FILEWRITE;
2296 /* check for padding to pre-guessed 'movi'-chunk position */
2297 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2298 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2299 ck.ckid = ckidAVIPADDING;
2300 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2301 assert((LONG)ck.cksize >= 0);
2303 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2304 return AVIERR_FILEWRITE;
2305 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2306 return AVIERR_FILEWRITE;
2307 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2308 return AVIERR_FILEWRITE;
2311 /* now write the 'movi' chunk */
2312 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2314 ckLIST1.fccType = listtypeAVIMOVIE;
2315 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2316 return AVIERR_FILEWRITE;
2317 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2318 return AVIERR_FILEWRITE;
2319 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2320 return AVIERR_FILEWRITE;
2322 /* write 'idx1' chunk */
2323 hr = AVIFILE_SaveIndex(This);
2327 /* write optional extra file chunks */
2328 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2329 /* as for the streams, are the chunk header(s) in the structure */
2330 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2331 return AVIERR_FILEWRITE;
2334 /* close RIFF chunk */
2335 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2336 return AVIERR_FILEWRITE;
2338 /* add some JUNK at end for bad parsers */
2339 memset(&ckRIFF, 0, sizeof(ckRIFF));
2340 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2341 mmioFlush(This->hmmio, 0);
2346 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2348 IAVIStreamImpl *pStream;
2354 ck.ckid = ckidAVINEWINDEX;
2356 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2357 return AVIERR_FILEWRITE;
2359 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2360 /* is interleaved -- write block of corresponding frames */
2361 LONG lInitialFrames = 0;
2365 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2368 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
2370 assert(stepsize > 0);
2372 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2373 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2374 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2377 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2379 DWORD nFrame = lInitialFrames + i;
2381 assert(nFrame < This->nIdxRecords);
2383 idx.ckid = listtypeAVIRECORD;
2384 idx.dwFlags = AVIIF_LIST;
2385 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2386 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2387 - This->dwMoviChunkPos;
2388 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2389 return AVIERR_FILEWRITE;
2391 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2392 pStream = This->ppStreams[nStream];
2394 /* heave we reached start of this stream? */
2395 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2398 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2399 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2401 /* reached end of this stream? */
2402 if (pStream->lLastFrame <= nFrame)
2405 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2406 pStream->sInfo.dwFormatChangeCount != 0 &&
2407 pStream->idxFmtChanges != NULL) {
2410 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2411 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2412 idx.dwFlags = AVIIF_NOTIME;
2413 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2414 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2415 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2416 - This->dwMoviChunkPos;
2418 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2419 return AVIERR_FILEWRITE;
2423 } /* if have formatchanges */
2425 idx.ckid = pStream->idxFrames[nFrame].ckid;
2426 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2427 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2428 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2429 - This->dwMoviChunkPos;
2430 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2431 return AVIERR_FILEWRITE;
2435 /* not interleaved -- write index for each stream at once */
2436 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2437 pStream = This->ppStreams[nStream];
2439 for (n = 0; n <= pStream->lLastFrame; n++) {
2440 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2441 (pStream->sInfo.dwFormatChangeCount != 0)) {
2444 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2445 if (pStream->idxFmtChanges[pos].ckid == n) {
2446 idx.dwFlags = AVIIF_NOTIME;
2447 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2448 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2450 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2451 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2452 return AVIERR_FILEWRITE;
2456 } /* if have formatchanges */
2458 idx.ckid = pStream->idxFrames[n].ckid;
2459 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2460 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2461 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2462 - This->dwMoviChunkPos;
2464 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2465 return AVIERR_FILEWRITE;
2468 } /* if not interleaved */
2470 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2471 return AVIERR_FILEWRITE;
2476 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2485 /* search the number of the specified stream */
2486 nStream = (ULONG)-1;
2487 for (i = 0; i < This->fInfo.dwStreams; i++) {
2488 assert(This->ppStreams[i] != NULL);
2490 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2504 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2508 /* pre-conditions */
2509 assert(This != NULL);
2511 This->fInfo.dwMaxBytesPerSec = 0;
2512 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2513 This->fInfo.dwSuggestedBufferSize = 0;
2514 This->fInfo.dwWidth = 0;
2515 This->fInfo.dwHeight = 0;
2516 This->fInfo.dwScale = 0;
2517 This->fInfo.dwRate = 0;
2518 This->fInfo.dwLength = 0;
2519 This->dwInitialFrames = 0;
2521 for (i = 0; i < This->fInfo.dwStreams; i++) {
2522 AVISTREAMINFOW *psi;
2525 /* pre-conditions */
2526 assert(This->ppStreams[i] != NULL);
2528 psi = &This->ppStreams[i]->sInfo;
2529 assert(psi->dwScale != 0);
2530 assert(psi->dwRate != 0);
2533 /* use first stream timings as base */
2534 This->fInfo.dwScale = psi->dwScale;
2535 This->fInfo.dwRate = psi->dwRate;
2536 This->fInfo.dwLength = psi->dwLength;
2538 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
2539 (PAVISTREAM)This->ppStreams[i],psi->dwLength);
2540 if (This->fInfo.dwLength < n)
2541 This->fInfo.dwLength = n;
2544 if (This->dwInitialFrames < psi->dwInitialFrames)
2545 This->dwInitialFrames = psi->dwInitialFrames;
2547 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2548 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2550 if (psi->dwSampleSize != 0) {
2551 /* fixed sample size -- exact computation */
2552 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2555 /* variable sample size -- only upper limit */
2556 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2557 psi->dwRate, psi->dwScale);
2559 /* update dimensions */
2560 n = psi->rcFrame.right - psi->rcFrame.left;
2561 if (This->fInfo.dwWidth < n)
2562 This->fInfo.dwWidth = n;
2563 n = psi->rcFrame.bottom - psi->rcFrame.top;
2564 if (This->fInfo.dwHeight < n)
2565 This->fInfo.dwHeight = n;
2570 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2571 FOURCC ckid, DWORD flags, LPCVOID buffer,
2580 /* if no frame/block is already written, we must compute start of movi chunk */
2581 if (This->paf->dwMoviChunkPos == 0)
2582 AVIFILE_ComputeMoviStart(This->paf);
2584 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2585 return AVIERR_FILEWRITE;
2587 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2588 return AVIERR_FILEWRITE;
2589 if (buffer != NULL && size > 0) {
2590 if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2591 return AVIERR_FILEWRITE;
2593 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2594 return AVIERR_FILEWRITE;
2596 This->paf->fDirty = TRUE;
2597 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2599 return AVIFILE_AddFrame(This, ckid, size,
2600 ck.dwDataOffset - 2 * sizeof(DWORD), flags);