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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
33 #define COM_NO_WINDOWS_H
47 #include "avifile_private.h"
48 #include "extrachunk.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
56 #define IDX_PER_BLOCK 2730
59 /***********************************************************************/
61 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
62 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
63 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
64 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
65 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
66 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
67 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
68 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
69 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
70 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
72 struct ICOM_VTABLE(IAVIFile) iavift = {
73 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
74 IAVIFile_fnQueryInterface,
79 IAVIFile_fnCreateStream,
83 IAVIFile_fnDeleteStream
86 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
87 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
88 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface);
89 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
90 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
91 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
92 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
93 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
94 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
96 struct ICOM_VTABLE(IPersistFile) ipersistft = {
97 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
98 IPersistFile_fnQueryInterface,
99 IPersistFile_fnAddRef,
100 IPersistFile_fnRelease,
101 IPersistFile_fnGetClassID,
102 IPersistFile_fnIsDirty,
105 IPersistFile_fnSaveCompleted,
106 IPersistFile_fnGetCurFile
109 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
110 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
111 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface);
112 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
113 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
114 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
115 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
116 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
117 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
118 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
119 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
120 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
121 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
122 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
124 struct ICOM_VTABLE(IAVIStream) iavist = {
125 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
126 IAVIStream_fnQueryInterface,
128 IAVIStream_fnRelease,
131 IAVIStream_fnFindSample,
132 IAVIStream_fnReadFormat,
133 IAVIStream_fnSetFormat,
137 IAVIStream_fnReadData,
138 IAVIStream_fnWriteData,
142 typedef struct _IAVIFileImpl IAVIFileImpl;
144 typedef struct _IPersistFileImpl {
146 ICOM_VFIELD(IPersistFile);
148 /* IPersistFile stuff */
152 typedef struct _IAVIStreamImpl {
154 ICOM_VFIELD(IAVIStream);
157 /* IAVIStream stuff */
159 DWORD nStream; /* the n-th stream in file */
160 AVISTREAMINFOW sInfo;
165 LPVOID lpHandlerData;
171 DWORD cbBuffer; /* size of lpBuffer */
172 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
174 LONG lLastFrame; /* last correct index in idxFrames */
175 AVIINDEXENTRY *idxFrames;
176 DWORD nIdxFrames; /* upper index limit of idxFrames */
177 AVIINDEXENTRY *idxFmtChanges;
178 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
181 struct _IAVIFileImpl {
183 ICOM_VFIELD(IAVIFile);
186 /* IAVIFile stuff... */
187 IPersistFileImpl iPersistFile;
190 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
192 EXTRACHUNKS fileextra;
194 DWORD dwMoviChunkPos; /* some stuff for saving ... */
196 DWORD dwNextFramePos;
197 DWORD dwInitialFrames;
199 MMCKINFO ckLastRecord;
200 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
201 DWORD nIdxRecords; /* current fill level */
202 DWORD cbIdxRecords; /* size of idxRecords */
204 /* IPersistFile stuff ... */
211 /***********************************************************************/
213 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
214 DWORD offset, DWORD flags);
215 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
216 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
217 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
218 LPAVISTREAMINFOW asi);
219 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
220 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
221 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset);
222 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
223 LONG count, DWORD pos, BOOL *bAbsolute);
224 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
225 LPVOID buffer, LONG size);
226 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
228 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
229 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This);
230 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fccType,
232 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
233 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
234 FOURCC ckid, DWORD flags, LPVOID buffer,
237 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
242 assert(riid != NULL && ppv != NULL);
246 pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl));
248 return AVIERR_MEMORY;
250 pfile->lpVtbl = &iavift;
252 pfile->iPersistFile.lpVtbl = &ipersistft;
253 pfile->iPersistFile.paf = pfile;
255 hr = IUnknown_QueryInterface((IUnknown*)pfile, riid, ppv);
257 LocalFree((HLOCAL)pfile);
262 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
265 ICOM_THIS(IAVIFileImpl,iface);
267 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
269 if (IsEqualGUID(&IID_IUnknown, refiid) ||
270 IsEqualGUID(&IID_IAVIFile, refiid)) {
272 IAVIFile_AddRef(iface);
275 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
276 *obj = &This->iPersistFile;
277 IAVIFile_AddRef(iface);
282 return OLE_E_ENUM_NOMORE;
285 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
287 ICOM_THIS(IAVIFileImpl,iface);
289 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
290 return ++(This->ref);
293 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
295 ICOM_THIS(IAVIFileImpl,iface);
298 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
300 if (!--(This->ref)) {
302 /* need to write headers to file */
303 AVIFILE_SaveFile(This);
306 for (i = 0; i < This->fInfo.dwStreams; i++) {
307 if (This->ppStreams[i] != NULL) {
308 if (This->ppStreams[i]->ref != 0) {
309 ERR(": someone has still %lu reference to stream %u (%p)!\n",
310 This->ppStreams[i]->ref, i, This->ppStreams[i]);
312 AVIFILE_DestructAVIStream(This->ppStreams[i]);
313 LocalFree((HLOCAL)This->ppStreams[i]);
314 This->ppStreams[i] = NULL;
318 if (This->idxRecords != NULL) {
319 GlobalFreePtr(This->idxRecords);
320 This->idxRecords = NULL;
321 This->nIdxRecords = 0;
324 if (This->fileextra.lp != NULL) {
325 GlobalFreePtr(This->fileextra.lp);
326 This->fileextra.lp = NULL;
327 This->fileextra.cb = 0;
330 if (This->szFileName != NULL) {
331 LocalFree((HLOCAL)This->szFileName);
332 This->szFileName = NULL;
334 if (This->hmmio != NULL) {
335 mmioClose(This->hmmio, 0);
339 LocalFree((HLOCAL)This);
345 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
348 ICOM_THIS(IAVIFileImpl,iface);
350 TRACE("(%p,%p,%ld)\n",iface,afi,size);
353 return AVIERR_BADPARAM;
355 return AVIERR_BADSIZE;
357 AVIFILE_UpdateInfo(This);
359 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
361 if ((DWORD)size < sizeof(This->fInfo))
362 return AVIERR_BUFFERTOOSMALL;
366 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
367 DWORD fccType, LONG lParam)
369 ICOM_THIS(IAVIFileImpl,iface);
373 TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam);
375 if (avis == NULL || lParam < 0)
376 return AVIERR_BADPARAM;
378 nStream = AVIFILE_SearchStream(This, fccType, lParam);
380 /* Does the requested stream exist? */
381 if (nStream < This->fInfo.dwStreams &&
382 This->ppStreams[nStream] != NULL) {
383 *avis = (PAVISTREAM)This->ppStreams[nStream];
384 IAVIStream_AddRef(*avis);
389 /* Sorry, but the specified stream doesn't exist */
390 return AVIERR_NODATA;
393 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
394 LPAVISTREAMINFOW asi)
396 ICOM_THIS(IAVIFileImpl,iface);
400 TRACE("(%p,%p,%p)\n", iface, avis, asi);
402 /* check parameters */
403 if (avis == NULL || asi == NULL)
404 return AVIERR_BADPARAM;
408 /* Does the user have write permission? */
409 if ((This->uMode & MMIO_RWMODE) == 0)
410 return AVIERR_READONLY;
412 /* Can we add another stream? */
413 n = This->fInfo.dwStreams;
414 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
415 /* already reached max nr of streams
416 * or have already written frames to disk */
417 return AVIERR_UNSUPPORTED;
420 /* check AVISTREAMINFO for some really needed things */
421 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
422 return AVIERR_BADFORMAT;
424 /* now it seems to be save to add the stream */
425 assert(This->ppStreams[n] == NULL);
426 This->ppStreams[n] = (IAVIStreamImpl*)LocalAlloc(LPTR,
427 sizeof(IAVIStreamImpl));
428 if (This->ppStreams[n] == NULL)
429 return AVIERR_MEMORY;
431 /* initialize the new allocated stream */
432 AVIFILE_ConstructAVIStream(This, n, asi);
434 This->fInfo.dwStreams++;
437 /* update our AVIFILEINFO structure */
438 AVIFILE_UpdateInfo(This);
441 *avis = (PAVISTREAM)This->ppStreams[n];
442 IAVIStream_AddRef(*avis);
447 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
448 LPVOID lpData, LONG size)
450 ICOM_THIS(IAVIFileImpl,iface);
452 TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
454 /* check parameters */
456 return AVIERR_BADPARAM;
458 return AVIERR_BADSIZE;
460 /* Do we have write permission? */
461 if ((This->uMode & MMIO_RWMODE) == 0)
462 return AVIERR_READONLY;
466 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
469 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
470 LPVOID lpData, LONG *size)
472 ICOM_THIS(IAVIFileImpl,iface);
474 TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
476 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
479 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
481 ICOM_THIS(IAVIFileImpl,iface);
483 TRACE("(%p)\n",iface);
485 if ((This->uMode & MMIO_RWMODE) == 0)
486 return AVIERR_READONLY;
490 /* no frames written to any stream? -- compute start of 'movi'-chunk */
491 if (This->dwMoviChunkPos == 0)
492 AVIFILE_ComputeMoviStart(This);
494 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
496 /* already written frames to any stream, ... */
497 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
498 /* close last record */
499 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
500 return AVIERR_FILEWRITE;
502 AVIFILE_AddRecord(This);
504 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
505 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
508 /* write out a new record into file, but don't close it */
509 This->ckLastRecord.cksize = 0;
510 This->ckLastRecord.fccType = listtypeAVIRECORD;
511 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
512 return AVIERR_FILEWRITE;
513 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
514 return AVIERR_FILEWRITE;
515 This->dwNextFramePos += 3 * sizeof(DWORD);
520 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
523 ICOM_THIS(IAVIFileImpl,iface);
527 TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
529 /* check parameter */
531 return AVIERR_BADPARAM;
533 /* Have user write permissions? */
534 if ((This->uMode & MMIO_RWMODE) == 0)
535 return AVIERR_READONLY;
537 nStream = AVIFILE_SearchStream(This, fccType, lParam);
539 /* Does the requested stream exist? */
540 if (nStream < This->fInfo.dwStreams &&
541 This->ppStreams[nStream] != NULL) {
542 /* ... so delete it now */
543 LocalFree((HLOCAL)This->ppStreams[nStream]);
545 if (This->fInfo.dwStreams - nStream > 0)
546 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
547 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
549 This->ppStreams[This->fInfo.dwStreams] = NULL;
550 This->fInfo.dwStreams--;
553 /* This->fInfo will be updated further when asked for */
556 return AVIERR_NODATA;
559 /***********************************************************************/
561 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
562 REFIID refiid, LPVOID *obj)
564 ICOM_THIS(IPersistFileImpl,iface);
566 assert(This->paf != NULL);
568 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
571 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
573 ICOM_THIS(IPersistFileImpl,iface);
575 assert(This->paf != NULL);
577 return IAVIFile_AddRef((PAVIFILE)This->paf);
580 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
582 ICOM_THIS(IPersistFileImpl,iface);
584 assert(This->paf != NULL);
586 return IAVIFile_Release((PAVIFILE)This->paf);
589 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
592 TRACE("(%p,%p)\n", iface, pClassID);
594 if (pClassID == NULL)
595 return AVIERR_BADPARAM;
597 memcpy(pClassID, &CLSID_AVIFile, sizeof(CLSID_AVIFile));
602 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
604 ICOM_THIS(IPersistFileImpl,iface);
606 TRACE("(%p)\n", iface);
608 assert(This->paf != NULL);
610 return (This->paf->fDirty ? S_OK : S_FALSE);
613 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
614 LPCOLESTR pszFileName, DWORD dwMode)
616 ICOM_THIS(IPersistFileImpl,iface);
620 TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
622 /* check parameter */
623 if (pszFileName == NULL)
624 return AVIERR_BADPARAM;
626 assert(This->paf != NULL);
627 if (This->paf->hmmio != NULL)
628 return AVIERR_ERROR; /* No reuse of this object for another file! */
630 /* remeber mode and name */
631 This->paf->uMode = dwMode;
633 len = lstrlenW(pszFileName) + 1;
634 This->paf->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR));
635 if (This->paf->szFileName == NULL)
636 return AVIERR_MEMORY;
637 lstrcpyW(This->paf->szFileName, pszFileName);
639 /* try to open the file */
640 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
641 MMIO_ALLOCBUF | dwMode);
642 if (This->paf->hmmio == NULL) {
643 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
646 len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1,
647 NULL, 0, NULL, NULL);
648 szFileName = LocalAlloc(LPTR, len * sizeof(CHAR));
649 if (szFileName == NULL)
650 return AVIERR_MEMORY;
652 WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName,
655 This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
656 LocalFree((HLOCAL)szFileName);
657 if (This->paf->hmmio == NULL)
658 return AVIERR_FILEOPEN;
661 /* should we create a new file? */
662 if (dwMode & OF_CREATE) {
663 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
664 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
668 return AVIFILE_LoadFile(This->paf);
671 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
672 LPCOLESTR pszFileName,BOOL fRemember)
674 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
676 /* We write directly to disk, so nothing to do. */
681 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
682 LPCOLESTR pszFileName)
684 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
686 /* We write directly to disk, so nothing to do. */
691 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
692 LPOLESTR *ppszFileName)
694 ICOM_THIS(IPersistFileImpl,iface);
696 TRACE("(%p,%p)\n", iface, ppszFileName);
698 if (ppszFileName == NULL)
699 return AVIERR_BADPARAM;
701 *ppszFileName = NULL;
703 assert(This->paf != NULL);
705 if (This->paf->szFileName != NULL) {
706 int len = lstrlenW(This->paf->szFileName) + 1;
708 *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR));
709 if (*ppszFileName == NULL)
710 return AVIERR_MEMORY;
712 strcpyW(*ppszFileName, This->paf->szFileName);
718 /***********************************************************************/
720 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
721 REFIID refiid, LPVOID *obj)
723 ICOM_THIS(IAVIStreamImpl,iface);
725 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
727 if (IsEqualGUID(&IID_IUnknown, refiid) ||
728 IsEqualGUID(&IID_IAVIStream, refiid)) {
730 IAVIStream_AddRef(iface);
734 /* FIXME: IAVIStreaming interface */
736 return OLE_E_ENUM_NOMORE;
739 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
741 ICOM_THIS(IAVIStreamImpl,iface);
743 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
745 /* also add ref to parent, so that it doesn't kill us */
746 if (This->paf != NULL)
747 IAVIFile_AddRef((PAVIFILE)This->paf);
749 return ++(This->ref);
752 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
754 ICOM_THIS(IAVIStreamImpl,iface);
756 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
758 /* we belong to the AVIFile, which must free us! */
759 if (This->ref == 0) {
760 ERR(": already released!\n");
766 if (This->paf != NULL)
767 IAVIFile_Release((PAVIFILE)This->paf);
772 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
775 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
777 /* This IAVIStream interface needs an AVIFile */
778 return AVIERR_UNSUPPORTED;
781 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
784 ICOM_THIS(IAVIStreamImpl,iface);
786 TRACE("(%p,%p,%ld)\n", iface, psi, size);
789 return AVIERR_BADPARAM;
791 return AVIERR_BADSIZE;
793 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
795 if ((DWORD)size < sizeof(This->sInfo))
796 return AVIERR_BUFFERTOOSMALL;
800 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
803 ICOM_THIS(IAVIStreamImpl,iface);
807 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
809 if (flags & FIND_FROM_START) {
810 pos = This->sInfo.dwStart;
811 flags &= ~(FIND_FROM_START|FIND_PREV);
815 if (This->sInfo.dwSampleSize != 0) {
816 /* convert samples into block number with offset */
817 AVIFILE_SamplesToBlock(This, &pos, &offset);
820 if (flags & FIND_TYPE) {
821 if (flags & FIND_KEY) {
822 while (0 <= pos && pos <= This->lLastFrame) {
823 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
826 if (flags & FIND_NEXT)
831 } else if (flags & FIND_ANY) {
832 while (0 <= pos && pos <= This->lLastFrame) {
833 if (This->idxFrames[pos].dwChunkLength > 0)
836 if (flags & FIND_NEXT)
842 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
843 This->sInfo.fccType == streamtypeVIDEO) {
844 if (flags & FIND_NEXT) {
847 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
848 if (This->idxFmtChanges[n].ckid >= pos) {
849 pos = This->idxFmtChanges[n].ckid;
855 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
856 if (This->idxFmtChanges[n].ckid <= pos) {
857 pos = This->idxFmtChanges[n].ckid;
862 if (pos > (LONG)This->sInfo.dwStart)
863 return 0; /* format changes always for first frame */
871 if (pos < (LONG)This->sInfo.dwStart)
874 switch (flags & FIND_RET) {
877 pos = This->idxFrames[pos].dwChunkLength;
880 /* physical position */
881 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
882 + offset * This->sInfo.dwSampleSize;
886 if (This->sInfo.dwSampleSize)
887 pos = This->sInfo.dwSampleSize;
892 FIXME(": FIND_INDEX flag is not supported!\n");
893 /* This is an index in the index-table on disc. */
895 }; /* else logical position */
900 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
901 LPVOID format, LONG *formatsize)
903 ICOM_THIS(IAVIStreamImpl,iface);
905 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
907 if (formatsize == NULL)
908 return AVIERR_BADPARAM;
910 /* only interested in needed buffersize? */
911 if (format == NULL || *formatsize <= 0) {
912 *formatsize = This->cbFormat;
917 /* copy initial format (only as much as will fit) */
918 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
919 if (*(DWORD*)formatsize < This->cbFormat) {
920 *formatsize = This->cbFormat;
921 return AVIERR_BUFFERTOOSMALL;
924 /* Could format change? When yes will it change? */
925 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
926 pos > This->sInfo.dwStart) {
929 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
931 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt);
935 *formatsize = This->cbFormat;
939 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
940 LPVOID format, LONG formatsize)
942 ICOM_THIS(IAVIStreamImpl,iface);
944 LPBITMAPINFOHEADER lpbiNew = (LPBITMAPINFOHEADER)format;
946 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
948 /* check parameters */
949 if (format == NULL || formatsize <= 0)
950 return AVIERR_BADPARAM;
952 /* Do we have write permission? */
953 if ((This->paf->uMode & MMIO_RWMODE) == 0)
954 return AVIERR_READONLY;
956 /* can only set format before frame is written! */
957 if (This->lLastFrame > pos)
958 return AVIERR_UNSUPPORTED;
960 /* initial format or a formatchange? */
961 if (This->lpFormat == NULL) {
963 if (This->paf->dwMoviChunkPos != 0)
964 return AVIERR_ERROR; /* user has used API in wrong sequnece! */
966 This->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, formatsize);
967 if (This->lpFormat == NULL)
968 return AVIERR_MEMORY;
969 This->cbFormat = formatsize;
971 memcpy(This->lpFormat, format, formatsize);
973 /* update some infos about stream */
974 if (This->sInfo.fccType == streamtypeVIDEO) {
977 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
978 if (lDim < lpbiNew->biWidth)
979 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
980 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
981 if (lDim < lpbiNew->biHeight)
982 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
983 } else if (This->sInfo.fccType == streamtypeAUDIO)
984 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
989 LPBITMAPINFOHEADER lpbiOld = (LPBITMAPINFOHEADER)This->lpFormat;
990 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
991 AVIPALCHANGE *lppc = NULL;
994 /* pherhaps formatchange, check it ... */
995 if (This->cbFormat != formatsize)
996 return AVIERR_UNSUPPORTED;
998 /* no formatchange, only the initial one */
999 if (memcmp(This->lpFormat, format, formatsize) == 0)
1002 /* check that's only the palette, which changes */
1003 if (lpbiOld->biSize != lpbiNew->biSize ||
1004 lpbiOld->biWidth != lpbiNew->biWidth ||
1005 lpbiOld->biHeight != lpbiNew->biHeight ||
1006 lpbiOld->biPlanes != lpbiNew->biPlanes ||
1007 lpbiOld->biBitCount != lpbiNew->biBitCount ||
1008 lpbiOld->biCompression != lpbiNew->biCompression ||
1009 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
1010 return AVIERR_UNSUPPORTED;
1012 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1014 /* simply say all colors have changed */
1015 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
1016 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
1017 lppc = (AVIPALCHANGE*)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
1019 return AVIERR_MEMORY;
1021 lppc->bFirstEntry = 0;
1022 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
1024 for (n = 0; n < lpbiOld->biClrUsed; n++) {
1025 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
1026 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
1027 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
1028 lppc->peNew[n].peFlags = 0;
1031 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
1032 return AVIERR_FILEWRITE;
1033 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
1034 return AVIERR_FILEWRITE;
1035 if (mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize)
1036 return AVIERR_FILEWRITE;
1037 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
1038 return AVIERR_FILEWRITE;
1039 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
1041 GlobalFreePtr(lppc);
1043 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
1047 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
1048 LONG samples, LPVOID buffer,
1049 LONG buffersize, LPLONG bytesread,
1052 ICOM_THIS(IAVIStreamImpl,iface);
1057 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
1058 buffersize, bytesread, samplesread);
1060 /* clear return parameters if given */
1061 if (bytesread != NULL)
1063 if (samplesread != NULL)
1066 /* check parameters */
1067 if ((LONG)This->sInfo.dwStart > start)
1068 return AVIERR_NODATA; /* couldn't read before start of stream */
1069 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1070 return AVIERR_NODATA; /* start is past end of stream */
1072 /* should we read as much as possible? */
1073 if (samples == -1) {
1074 /* User should know how much we have read */
1075 if (bytesread == NULL && samplesread == NULL)
1076 return AVIERR_BADPARAM;
1078 if (This->sInfo.dwSampleSize != 0)
1079 samples = buffersize / This->sInfo.dwSampleSize;
1084 /* limit to end of stream */
1085 if ((LONG)This->sInfo.dwLength < samples)
1086 samples = This->sInfo.dwLength;
1087 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1088 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1090 /* nothing to read? Then leave ... */
1094 if (This->sInfo.dwSampleSize != 0) {
1095 /* fixed samplesize -- we can read over frame/block boundaries */
1099 /* convert start sample to block,offset pair */
1100 AVIFILE_SamplesToBlock(This, &block, &offset);
1102 /* convert samples to bytes */
1103 samples *= This->sInfo.dwSampleSize;
1105 while (samples > 0 && buffersize > 0) {
1106 if (block != This->dwCurrentFrame) {
1107 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1112 size = min((DWORD)samples, (DWORD)buffersize);
1113 size = min(size, This->cbBuffer - 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 ICOM_THIS(IAVIStreamImpl,iface);
1168 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%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 ICOM_THIS(IAVIStreamImpl,iface);
1256 FIXME("(%p,%ld,%ld): 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 ICOM_THIS(IAVIStreamImpl,iface);
1295 TRACE("(%p,0x%08lX,%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 ICOM_THIS(IAVIStreamImpl,iface);
1319 TRACE("(%p,0x%08lx,%p,%ld)\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 (size >= 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 = GlobalAllocPtr(GMEM_MOVEABLE, 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,%ld): stub\n", iface, info, infolen);
1370 /***********************************************************************/
1372 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1374 /* pre-conditions */
1375 assert(This != NULL);
1377 switch (TWOCCFromFOURCC(ckid)) {
1379 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1380 flags |= AVIIF_KEYFRAME;
1382 case cktypeDIBcompressed:
1383 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1384 flags &= ~AVIIF_KEYFRAME;
1386 case cktypePALchange:
1387 if (This->sInfo.fccType != streamtypeVIDEO) {
1388 ERR(": found palette change in non-video stream!\n");
1389 return AVIERR_BADFORMAT;
1391 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1392 This->sInfo.dwFormatChangeCount++;
1394 if (This->idxFmtChanges == NULL || This->sInfo.dwFormatChangeCount < This->nIdxFmtChanges) {
1395 UINT n = This->sInfo.dwFormatChangeCount;
1397 This->nIdxFmtChanges += 16;
1398 if (This->idxFmtChanges == NULL)
1399 This->idxFmtChanges =
1400 GlobalAllocPtr(GHND, This->nIdxFmtChanges * sizeof(AVIINDEXENTRY));
1402 This->idxFmtChanges =
1403 GlobalReAllocPtr(This->idxFmtChanges,
1404 This->nIdxFmtChanges * sizeof(AVIINDEXENTRY), GHND);
1405 if (This->idxFmtChanges == NULL)
1406 return AVIERR_MEMORY;
1408 This->idxFmtChanges[n].ckid = This->lLastFrame;
1409 This->idxFmtChanges[n].dwFlags = 0;
1410 This->idxFmtChanges[n].dwChunkOffset = offset;
1411 This->idxFmtChanges[n].dwChunkLength = size;
1416 case cktypeWAVEbytes:
1417 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1418 flags |= AVIIF_KEYFRAME;
1421 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1425 /* first frame is alwasy a keyframe */
1426 if (This->lLastFrame == -1)
1427 flags |= AVIIF_KEYFRAME;
1429 if (This->sInfo.dwSuggestedBufferSize < size)
1430 This->sInfo.dwSuggestedBufferSize = size;
1432 /* get memory for index */
1433 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1434 This->nIdxFrames += 512;
1435 if (This->idxFrames == NULL)
1437 GlobalAllocPtr(GHND, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1440 GlobalReAllocPtr(This->idxFrames,
1441 This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND);
1442 if (This->idxFrames == NULL)
1443 return AVIERR_MEMORY;
1447 This->idxFrames[This->lLastFrame].ckid = ckid;
1448 This->idxFrames[This->lLastFrame].dwFlags = flags;
1449 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1450 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1452 /* update AVISTREAMINFO structure if necessary */
1453 if (This->sInfo.dwLength <= This->lLastFrame)
1454 This->sInfo.dwLength = This->lLastFrame + 1;
1459 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1461 /* pre-conditions */
1462 assert(This != NULL && This->ppStreams[0] != NULL);
1464 if (This->idxRecords == NULL || This->cbIdxRecords == 0) {
1465 This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY);
1466 This->idxRecords = GlobalAllocPtr(GHND, This->cbIdxRecords);
1467 if (This->idxRecords == NULL)
1468 return AVIERR_MEMORY;
1471 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1473 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1474 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1475 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1476 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1477 This->idxRecords[This->nIdxRecords].dwChunkLength =
1478 This->ckLastRecord.cksize;
1479 This->nIdxRecords++;
1484 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1489 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1490 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1492 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1493 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1495 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1496 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1497 dwPos += ((pStream->cbFormat + 1) & ~1U);
1498 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1499 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1500 if (lstrlenW(pStream->sInfo.szName) > 0)
1501 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1504 if (This->dwMoviChunkPos == 0) {
1505 This->dwNextFramePos = dwPos;
1507 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1508 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1509 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1511 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1517 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, LPAVISTREAMINFOW asi)
1519 IAVIStreamImpl *pstream;
1521 /* pre-conditions */
1522 assert(paf != NULL);
1523 assert(nr < MAX_AVISTREAMS);
1524 assert(paf->ppStreams[nr] != NULL);
1526 pstream = paf->ppStreams[nr];
1528 pstream->lpVtbl = &iavist;
1531 pstream->nStream = nr;
1532 pstream->dwCurrentFrame = (DWORD)-1;
1533 pstream->lLastFrame = -1;
1536 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1538 if (asi->dwLength > 0) {
1539 /* pre-allocate mem for frame-index structure */
1540 pstream->idxFrames =
1541 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwLength * sizeof(AVIINDEXENTRY));
1542 if (pstream->idxFrames != NULL)
1543 pstream->nIdxFrames = asi->dwLength;
1545 if (asi->dwFormatChangeCount > 0) {
1546 /* pre-allocate mem for formatchange-index structure */
1547 pstream->idxFmtChanges =
1548 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1549 if (pstream->idxFmtChanges != NULL)
1550 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1553 /* These values will be computed */
1554 pstream->sInfo.dwLength = 0;
1555 pstream->sInfo.dwSuggestedBufferSize = 0;
1556 pstream->sInfo.dwFormatChangeCount = 0;
1557 pstream->sInfo.dwEditCount = 1;
1558 if (pstream->sInfo.dwSampleSize > 0)
1559 SetRectEmpty(&pstream->sInfo.rcFrame);
1562 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1565 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1567 /* pre-conditions */
1568 assert(This != NULL);
1570 This->dwCurrentFrame = (DWORD)-1;
1571 This->lLastFrame = -1;
1573 if (This->idxFrames != NULL) {
1574 GlobalFreePtr(This->idxFrames);
1575 This->idxFrames = NULL;
1576 This->nIdxFrames = 0;
1578 if (This->idxFmtChanges != NULL) {
1579 GlobalFreePtr(This->idxFmtChanges);
1580 This->idxFmtChanges = NULL;
1582 if (This->lpBuffer != NULL) {
1583 GlobalFreePtr(This->lpBuffer);
1584 This->lpBuffer = NULL;
1587 if (This->lpHandlerData != NULL) {
1588 GlobalFreePtr(This->lpHandlerData);
1589 This->lpHandlerData = NULL;
1590 This->cbHandlerData = 0;
1592 if (This->extra.lp != NULL) {
1593 GlobalFreePtr(This->extra.lp);
1594 This->extra.lp = NULL;
1597 if (This->lpFormat != NULL) {
1598 GlobalFreePtr(This->lpFormat);
1599 This->lpFormat = NULL;
1604 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1606 MainAVIHeader MainAVIHdr;
1611 IAVIStreamImpl *pStream;
1615 if (This->hmmio == NULL)
1616 return AVIERR_FILEOPEN;
1618 /* initialize stream ptr's */
1619 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1621 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1622 ckRIFF.fccType = formtypeAVI;
1623 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1624 ERR(": not an AVI!\n");
1625 return AVIERR_FILEREAD;
1628 /* get "LIST" "hdrl" */
1629 ckLIST1.fccType = listtypeAVIHEADER;
1630 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1634 /* get "avih" chunk */
1635 ck.ckid = ckidAVIMAINHDR;
1636 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1640 if (ck.cksize != sizeof(MainAVIHdr)) {
1641 ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize);
1642 return AVIERR_BADFORMAT;
1644 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1645 return AVIERR_FILEREAD;
1647 /* check for MAX_AVISTREAMS limit */
1648 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1649 WARN("file contains %lu streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1650 return AVIERR_UNSUPPORTED;
1653 /* adjust permissions if copyrighted material in file */
1654 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1655 This->uMode &= ~MMIO_RWMODE;
1656 This->uMode |= MMIO_READ;
1659 /* convert MainAVIHeader into AVIFILINFOW */
1660 memset(&This->fInfo, 0, sizeof(This->fInfo));
1661 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1662 This->fInfo.dwScale = 1000000;
1663 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1664 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1665 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1666 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1667 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1668 This->fInfo.dwSuggestedBufferSize = MainAVIHdr.dwSuggestedBufferSize;
1669 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1670 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1671 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1672 sizeof(This->fInfo.szFileType));
1674 /* go back to into header list */
1675 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1676 return AVIERR_FILEREAD;
1678 /* foreach stream exists a "LIST","strl" chunk */
1679 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1680 /* get next nested chunk in this "LIST","strl" */
1681 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1682 return AVIERR_FILEREAD;
1684 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1685 if (ckLIST2.ckid == FOURCC_LIST &&
1686 ckLIST2.fccType == listtypeSTREAMHEADER) {
1687 pStream = This->ppStreams[nStream] =
1688 (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
1689 if (pStream == NULL)
1690 return AVIERR_MEMORY;
1691 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1694 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1696 case ckidSTREAMHANDLERDATA:
1697 if (pStream->lpHandlerData != NULL)
1698 return AVIERR_BADFORMAT;
1699 pStream->lpHandlerData = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1701 if (pStream->lpHandlerData == NULL)
1702 return AVIERR_MEMORY;
1703 pStream->cbHandlerData = ck.cksize;
1705 if (mmioRead(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
1706 return AVIERR_FILEREAD;
1708 case ckidSTREAMFORMAT:
1709 if (pStream->lpFormat != NULL)
1710 return AVIERR_BADFORMAT;
1714 pStream->lpFormat = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1716 if (pStream->lpFormat == NULL)
1717 return AVIERR_MEMORY;
1718 pStream->cbFormat = ck.cksize;
1720 if (mmioRead(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
1721 return AVIERR_FILEREAD;
1723 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1724 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pStream->lpFormat;
1726 /* some corrections to the video format */
1727 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1728 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1729 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1730 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1731 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1732 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1733 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1734 lpbi->biCompression = BI_RLE8;
1736 if (lpbi->biCompression == BI_RGB &&
1737 (pStream->sInfo.fccHandler == 0 ||
1738 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1739 pStream->sInfo.fccHandler = comptypeDIB;
1741 /* init rcFrame if it's empty */
1742 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1743 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1746 case ckidSTREAMHEADER:
1748 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1750 AVIStreamHeader streamHdr;
1752 WCHAR streamNameFmt[25];
1756 if (ck.cksize > sizeof(streamHdr))
1757 n = sizeof(streamHdr);
1759 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1760 return AVIERR_FILEREAD;
1762 pStream->sInfo.fccType = streamHdr.fccType;
1763 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1764 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1765 pStream->sInfo.wPriority = streamHdr.wPriority;
1766 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1767 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1768 pStream->sInfo.dwScale = streamHdr.dwScale;
1769 pStream->sInfo.dwRate = streamHdr.dwRate;
1770 pStream->sInfo.dwStart = streamHdr.dwStart;
1771 pStream->sInfo.dwLength = streamHdr.dwLength;
1772 pStream->sInfo.dwSuggestedBufferSize =
1773 streamHdr.dwSuggestedBufferSize;
1774 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1775 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1776 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1777 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1778 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1779 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1780 pStream->sInfo.dwEditCount = 0;
1781 pStream->sInfo.dwFormatChangeCount = 0;
1783 /* generate description for stream like "filename.avi Type #n" */
1784 if (streamHdr.fccType == streamtypeVIDEO)
1785 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType));
1786 else if (streamHdr.fccType == streamtypeAUDIO)
1787 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType));
1789 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1791 /* get count of this streamtype up to this stream */
1793 for (n = nStream; 0 <= n; n--) {
1794 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1798 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1800 LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt));
1802 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1803 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1804 AVIFILE_BasenameW(This->szFileName), szType, count);
1807 case ckidSTREAMNAME:
1808 { /* streamname will be saved as ASCII string */
1809 LPSTR str = (LPSTR)LocalAlloc(LMEM_FIXED, ck.cksize);
1811 return AVIERR_MEMORY;
1813 if (mmioRead(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize)
1814 return AVIERR_FILEREAD;
1816 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1817 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1819 LocalFree((HLOCAL)str);
1822 case ckidAVIPADDING:
1823 case mmioFOURCC('p','a','d','d'):
1826 WARN(": found extra chunk 0x%08lX\n", ck.ckid);
1827 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1832 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1833 return AVIERR_FILEREAD;
1836 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1837 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1841 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1842 return AVIERR_FILEREAD;
1845 /* read any extra headers in "LIST","hdrl" */
1846 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1847 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1848 return AVIERR_FILEREAD;
1850 /* search "LIST","movi" chunk in "RIFF","AVI " */
1851 ckLIST1.fccType = listtypeAVIMOVIE;
1852 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1857 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1858 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1859 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1860 return AVIERR_FILEREAD;
1862 /* try to find an index */
1863 ck.ckid = ckidAVINEWINDEX;
1864 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1865 &ck, &ckRIFF, MMIO_FINDCHUNK);
1866 if (SUCCEEDED(hr) && ck.cksize > 0) {
1867 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1868 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1871 /* when we haven't found an index or it's bad, then build one
1872 * by parsing 'movi' chunk */
1873 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1874 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1875 This->ppStreams[nStream]->lLastFrame = -1;
1877 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1878 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1879 return AVIERR_FILEREAD;
1882 /* seek through the 'movi' list until end */
1883 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1884 if (ck.ckid != FOURCC_LIST) {
1885 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1886 nStream = StreamFromFOURCC(ck.ckid);
1888 if (nStream > This->fInfo.dwStreams)
1889 return AVIERR_BADFORMAT;
1891 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1892 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1894 nStream = StreamFromFOURCC(ck.ckid);
1895 WARN(": file seems to be truncated!\n");
1896 if (nStream <= This->fInfo.dwStreams &&
1897 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1898 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1899 if (ck.cksize != -1) {
1900 ck.cksize -= ck.dwDataOffset;
1901 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1903 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1904 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1912 /* find other chunks */
1913 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1918 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset)
1922 HRESULT hr = AVIERR_OK;
1923 BOOL bAbsolute = TRUE;
1925 lp = (AVIINDEXENTRY*)GlobalAllocPtr(GMEM_MOVEABLE,
1926 IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1928 return AVIERR_MEMORY;
1930 /* adjust limits for index tables, so that inserting will be faster */
1931 for (n = 0; n < This->fInfo.dwStreams; n++) {
1932 IAVIStreamImpl *pStream = This->ppStreams[n];
1934 pStream->lLastFrame = -1;
1936 if (pStream->idxFrames != NULL) {
1937 GlobalFreePtr(pStream->idxFrames);
1938 pStream->idxFrames = NULL;
1939 pStream->nIdxFrames = 0;
1942 if (pStream->sInfo.dwSampleSize != 0) {
1943 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1944 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1945 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1946 pStream->nIdxFrames =
1947 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1950 pStream->nIdxFrames = pStream->sInfo.dwLength;
1952 pStream->idxFrames =
1953 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1954 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1955 pStream->nIdxFrames = 0;
1956 return AVIERR_MEMORY;
1962 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1964 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1965 hr = AVIERR_FILEREAD;
1970 if (pos == (DWORD)-1)
1971 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1973 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1981 for (n = 0; n < This->fInfo.dwStreams; n++) {
1982 IAVIStreamImpl *pStream = This->ppStreams[n];
1984 if (pStream->sInfo.dwSampleSize == 0 &&
1985 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1986 ERR("stream %lu length mismatch: dwLength=%lu found=%ld\n",
1987 n, pStream->sInfo.dwLength, pStream->lLastFrame);
1993 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
1994 LONG count, DWORD pos, BOOL *bAbsolute)
1997 return AVIERR_BADPARAM;
1999 for (; count > 0; count--, lp++) {
2000 WORD nStream = StreamFromFOURCC(lp->ckid);
2002 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
2003 continue; /* skip these */
2005 if (nStream > This->fInfo.dwStreams)
2006 return AVIERR_BADFORMAT;
2008 if (*bAbsolute == TRUE && lp->dwChunkOffset < This->dwMoviChunkPos)
2012 lp->dwChunkOffset += sizeof(DWORD);
2014 lp->dwChunkOffset += pos;
2016 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
2017 return AVIERR_MEMORY;
2023 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
2024 LPVOID buffer, LONG size)
2026 /* pre-conditions */
2027 assert(This != NULL);
2028 assert(This->paf != NULL);
2029 assert(This->paf->hmmio != NULL);
2030 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2031 assert(pos <= This->lLastFrame);
2033 /* should we read as much as block gives us? */
2034 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2035 size = This->idxFrames[pos].dwChunkLength;
2037 /* read into out own buffer or given one? */
2038 if (buffer == NULL) {
2039 /* we also read the chunk */
2040 size += 2 * sizeof(DWORD);
2042 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2043 if (This->lpBuffer == NULL || size < This->cbBuffer) {
2044 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2046 if (This->lpBuffer == NULL)
2047 This->lpBuffer = (LPDWORD)GlobalAllocPtr(GMEM_MOVEABLE, maxSize);
2050 (LPDWORD)GlobalReAllocPtr(This->lpBuffer, maxSize, GMEM_MOVEABLE);
2051 if (This->lpBuffer == NULL)
2052 return AVIERR_MEMORY;
2053 This->cbBuffer = max(size, This->sInfo.dwSuggestedBufferSize);
2056 /* now read the complete chunk into our buffer */
2057 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2058 return AVIERR_FILEREAD;
2059 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2060 return AVIERR_FILEREAD;
2062 /* check if it was the correct block which we have read */
2063 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2064 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2065 ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset);
2066 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2067 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2068 This->idxFrames[pos].dwChunkLength);
2069 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2070 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2071 return AVIERR_FILEREAD;
2074 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2075 return AVIERR_FILEREAD;
2076 if (mmioRead(This->paf->hmmio, (HPSTR)buffer, size) != size)
2077 return AVIERR_FILEREAD;
2083 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
2088 /* pre-conditions */
2089 assert(This != NULL);
2090 assert(pos != NULL);
2091 assert(offset != NULL);
2092 assert(This->sInfo.dwSampleSize != 0);
2093 assert(*pos >= This->sInfo.dwStart);
2095 /* convert start sample to start bytes */
2096 (*offset) = (*pos) - This->sInfo.dwStart;
2097 (*offset) *= This->sInfo.dwSampleSize;
2099 /* convert bytes to block number */
2100 for (block = 0; block <= This->lLastFrame; block++) {
2101 if (This->idxFrames[block].dwChunkLength <= *offset)
2102 (*offset) -= This->idxFrames[block].dwChunkLength;
2110 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2112 MainAVIHeader MainAVIHdr;
2113 IAVIStreamImpl* pStream;
2122 /* initialize some things */
2123 if (This->dwMoviChunkPos == 0)
2124 AVIFILE_ComputeMoviStart(This);
2126 /* written one record to much? */
2127 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2128 This->dwNextFramePos -= 3 * sizeof(DWORD);
2129 if (This->nIdxRecords > 0)
2130 This->nIdxRecords--;
2133 AVIFILE_UpdateInfo(This);
2135 assert(This->fInfo.dwScale != 0);
2137 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2138 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2139 This->fInfo.dwScale);
2140 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2141 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2142 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2143 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2144 MainAVIHdr.dwInitialFrames = 0;
2145 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2146 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2147 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2148 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2149 MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2151 /* now begin writing ... */
2152 mmioSeek(This->hmmio, 0, SEEK_SET);
2156 ckRIFF.fccType = formtypeAVI;
2157 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2158 return AVIERR_FILEWRITE;
2160 /* AVI headerlist */
2162 ckLIST1.fccType = listtypeAVIHEADER;
2163 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2164 return AVIERR_FILEWRITE;
2167 ck.ckid = ckidAVIMAINHDR;
2168 ck.cksize = sizeof(MainAVIHdr);
2170 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2171 return AVIERR_FILEWRITE;
2172 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2173 return AVIERR_FILEWRITE;
2174 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2175 return AVIERR_FILEWRITE;
2177 /* write the headers of each stream into a separate streamheader list */
2178 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2179 AVIStreamHeader strHdr;
2181 pStream = This->ppStreams[nStream];
2183 /* begin the new streamheader list */
2185 ckLIST2.fccType = listtypeSTREAMHEADER;
2186 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2187 return AVIERR_FILEWRITE;
2189 /* create an AVIStreamHeader from the AVSTREAMINFO */
2190 strHdr.fccType = pStream->sInfo.fccType;
2191 strHdr.fccHandler = pStream->sInfo.fccHandler;
2192 strHdr.dwFlags = pStream->sInfo.dwFlags;
2193 strHdr.wPriority = pStream->sInfo.wPriority;
2194 strHdr.wLanguage = pStream->sInfo.wLanguage;
2195 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2196 strHdr.dwScale = pStream->sInfo.dwScale;
2197 strHdr.dwRate = pStream->sInfo.dwRate;
2198 strHdr.dwStart = pStream->sInfo.dwStart;
2199 strHdr.dwLength = pStream->sInfo.dwLength;
2200 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2201 strHdr.dwQuality = pStream->sInfo.dwQuality;
2202 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2203 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2204 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2205 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2206 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2208 /* now write the AVIStreamHeader */
2209 ck.ckid = ckidSTREAMHEADER;
2210 ck.cksize = sizeof(strHdr);
2211 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2212 return AVIERR_FILEWRITE;
2213 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2214 return AVIERR_FILEWRITE;
2215 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2216 return AVIERR_FILEWRITE;
2218 /* ... the hopefully ever present streamformat ... */
2219 ck.ckid = ckidSTREAMFORMAT;
2220 ck.cksize = pStream->cbFormat;
2221 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2222 return AVIERR_FILEWRITE;
2223 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2224 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
2225 return AVIERR_FILEWRITE;
2227 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2228 return AVIERR_FILEWRITE;
2230 /* ... some optional existing handler data ... */
2231 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2232 ck.ckid = ckidSTREAMHANDLERDATA;
2233 ck.cksize = pStream->cbHandlerData;
2234 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2235 return AVIERR_FILEWRITE;
2236 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
2237 return AVIERR_FILEWRITE;
2238 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2239 return AVIERR_FILEWRITE;
2242 /* ... some optional additional extra chunk for this stream ... */
2243 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2244 /* the chunk header(s) are already in the strucuture */
2245 if (mmioWrite(This->hmmio, (HPSTR)pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2246 return AVIERR_FILEWRITE;
2249 /* ... an optional name for this stream ... */
2250 if (lstrlenW(pStream->sInfo.szName) > 0) {
2253 ck.ckid = ckidSTREAMNAME;
2254 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2255 if (ck.cksize & 1) /* align */
2257 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2258 return AVIERR_FILEWRITE;
2260 /* the streamname must be saved in ASCII not Unicode */
2261 str = (LPSTR)LocalAlloc(LPTR, ck.cksize);
2263 return AVIERR_MEMORY;
2264 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2265 ck.cksize, NULL, NULL);
2267 if (mmioWrite(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize) {
2268 LocalFree((HLOCAL)str);
2269 return AVIERR_FILEWRITE;
2272 LocalFree((HLOCAL)str);
2273 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2274 return AVIERR_FILEWRITE;
2277 /* close streamheader list for this stream */
2278 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2279 return AVIERR_FILEWRITE;
2280 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2282 /* close the aviheader list */
2283 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2284 return AVIERR_FILEWRITE;
2286 /* check for padding to pre-guessed 'movi'-chunk position */
2287 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2288 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2289 ck.ckid = ckidAVIPADDING;
2290 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2291 assert((LONG)ck.cksize >= 0);
2293 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2294 return AVIERR_FILEWRITE;
2295 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2296 return AVIERR_FILEWRITE;
2297 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2298 return AVIERR_FILEWRITE;
2301 /* now write the 'movi' chunk */
2302 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2304 ckLIST1.fccType = listtypeAVIMOVIE;
2305 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2306 return AVIERR_FILEWRITE;
2307 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2308 return AVIERR_FILEWRITE;
2309 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2310 return AVIERR_FILEWRITE;
2312 /* write 'idx1' chunk */
2313 hr = AVIFILE_SaveIndex(This);
2317 /* write optional extra file chunks */
2318 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2319 /* as for the streams, are the chunk header(s) in the structure */
2320 if (mmioWrite(This->hmmio, (HPSTR)This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2321 return AVIERR_FILEWRITE;
2324 /* close RIFF chunk */
2325 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2326 return AVIERR_FILEWRITE;
2328 /* add some JUNK at end for bad parsers */
2329 memset(&ckRIFF, 0, sizeof(ckRIFF));
2330 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2331 mmioFlush(This->hmmio, 0);
2336 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This)
2338 IAVIStreamImpl *pStream;
2344 ck.ckid = ckidAVINEWINDEX;
2346 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2347 return AVIERR_FILEWRITE;
2349 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2350 /* is interleaved -- write block of coresponding frames */
2351 LONG lInitialFrames = 0;
2355 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2358 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
2360 assert(stepsize > 0);
2362 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2363 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2364 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2367 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2369 DWORD nFrame = lInitialFrames + i;
2371 assert(nFrame < This->nIdxRecords);
2373 idx.ckid = listtypeAVIRECORD;
2374 idx.dwFlags = AVIIF_LIST;
2375 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2376 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2377 - This->dwMoviChunkPos;
2378 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2379 return AVIERR_FILEWRITE;
2381 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2382 pStream = This->ppStreams[nStream];
2384 /* heave we reached start of this stream? */
2385 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2388 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2389 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2391 /* reached end of this stream? */
2392 if (pStream->lLastFrame <= nFrame)
2395 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2396 pStream->sInfo.dwFormatChangeCount != 0 &&
2397 pStream->idxFmtChanges != NULL) {
2400 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2401 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2402 idx.dwFlags = AVIIF_NOTIME;
2403 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2404 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2405 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2406 - This->dwMoviChunkPos;
2408 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2409 return AVIERR_FILEWRITE;
2413 } /* if have formatchanges */
2415 idx.ckid = pStream->idxFrames[nFrame].ckid;
2416 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2417 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2418 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2419 - This->dwMoviChunkPos;
2420 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2421 return AVIERR_FILEWRITE;
2425 /* not interleaved -- write index for each stream at once */
2426 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2427 pStream = This->ppStreams[nStream];
2429 for (n = 0; n <= pStream->lLastFrame; n++) {
2430 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2431 (pStream->sInfo.dwFormatChangeCount != 0)) {
2434 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2435 if (pStream->idxFmtChanges[pos].ckid == n) {
2436 idx.dwFlags = AVIIF_NOTIME;
2437 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2438 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2440 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2441 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2442 return AVIERR_FILEWRITE;
2446 } /* if have formatchanges */
2448 idx.ckid = pStream->idxFrames[n].ckid;
2449 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2450 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2451 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2452 - This->dwMoviChunkPos;
2454 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2455 return AVIERR_FILEWRITE;
2458 } /* if not interleaved */
2460 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2461 return AVIERR_FILEWRITE;
2466 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2475 /* search the number of the specified stream */
2476 nStream = (ULONG)-1;
2477 for (i = 0; i < This->fInfo.dwStreams; i++) {
2478 assert(This->ppStreams[i] != NULL);
2480 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2494 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2498 /* pre-conditions */
2499 assert(This != NULL);
2501 This->fInfo.dwMaxBytesPerSec = 0;
2502 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2503 This->fInfo.dwSuggestedBufferSize = 0;
2504 This->fInfo.dwWidth = 0;
2505 This->fInfo.dwHeight = 0;
2506 This->fInfo.dwScale = 0;
2507 This->fInfo.dwRate = 0;
2508 This->fInfo.dwLength = 0;
2509 This->dwInitialFrames = 0;
2511 for (i = 0; i < This->fInfo.dwStreams; i++) {
2512 AVISTREAMINFOW *psi;
2515 /* pre-conditions */
2516 assert(This->ppStreams[i] != NULL);
2518 psi = &This->ppStreams[i]->sInfo;
2519 assert(psi->dwScale != 0);
2520 assert(psi->dwRate != 0);
2523 /* use first stream timings as base */
2524 This->fInfo.dwScale = psi->dwScale;
2525 This->fInfo.dwRate = psi->dwRate;
2526 This->fInfo.dwLength = psi->dwLength;
2528 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
2529 (PAVISTREAM)This->ppStreams[i],psi->dwLength);
2530 if (This->fInfo.dwLength < n)
2531 This->fInfo.dwLength = n;
2534 if (This->dwInitialFrames < psi->dwInitialFrames)
2535 This->dwInitialFrames = psi->dwInitialFrames;
2537 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2538 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2540 if (psi->dwSampleSize != 0) {
2541 /* fixed sample size -- exact computation */
2542 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2545 /* variable sample size -- only upper limit */
2546 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2547 psi->dwRate, psi->dwScale);
2549 /* update dimensions */
2550 n = psi->rcFrame.right - psi->rcFrame.left;
2551 if (This->fInfo.dwWidth < n)
2552 This->fInfo.dwWidth = n;
2553 n = psi->rcFrame.bottom - psi->rcFrame.top;
2554 if (This->fInfo.dwHeight < n)
2555 This->fInfo.dwHeight = n;
2560 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2561 FOURCC ckid, DWORD flags, LPVOID buffer,
2570 /* if no frame/block is already written, we must compute start of movi chunk */
2571 if (This->paf->dwMoviChunkPos == 0)
2572 AVIFILE_ComputeMoviStart(This->paf);
2574 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2575 return AVIERR_FILEWRITE;
2577 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2578 return AVIERR_FILEWRITE;
2579 if (buffer != NULL && size > 0) {
2580 if (mmioWrite(This->paf->hmmio, (HPSTR)buffer, size) != size)
2581 return AVIERR_FILEWRITE;
2583 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2584 return AVIERR_FILEWRITE;
2586 This->paf->fDirty = TRUE;
2587 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2589 return AVIFILE_AddFrame(This, ckid, size,
2590 ck.dwDataOffset - 2 * sizeof(DWORD), flags);