2 * Copyright 1999 Marcus Meissner
3 * Copyright 2002 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 * - IAVIFile_fnEndRecord: a stub -- needed for creating interleaved AVIs.
22 * - IAVIStreaming interface is missing for the IAVIStreamImpl
23 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
24 * - IAVIStream_fnReadFormat: formatchanges aren't read in.
25 * - IAVIStream_fnDelete: a stub.
26 * - IAVIStream_fnSetInfo: a stub.
29 #define COM_NO_WINDOWS_H
40 #include "avifile_private.h"
41 #include "extrachunk.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
48 #define IDX_PER_BLOCK 2730
51 /***********************************************************************/
53 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
54 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
55 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
56 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
57 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
58 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
59 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
60 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
61 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
62 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
64 struct ICOM_VTABLE(IAVIFile) iavift = {
65 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
66 IAVIFile_fnQueryInterface,
71 IAVIFile_fnCreateStream,
75 IAVIFile_fnDeleteStream
78 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
79 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
80 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface);
81 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
82 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
83 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
84 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
85 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
86 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
88 struct ICOM_VTABLE(IPersistFile) ipersistft = {
89 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
90 IPersistFile_fnQueryInterface,
91 IPersistFile_fnAddRef,
92 IPersistFile_fnRelease,
93 IPersistFile_fnGetClassID,
94 IPersistFile_fnIsDirty,
97 IPersistFile_fnSaveCompleted,
98 IPersistFile_fnGetCurFile
101 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
102 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
103 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface);
104 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
105 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
106 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
107 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
108 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
109 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
110 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
111 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
112 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
113 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
114 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
116 struct ICOM_VTABLE(IAVIStream) iavist = {
117 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
118 IAVIStream_fnQueryInterface,
120 IAVIStream_fnRelease,
123 IAVIStream_fnFindSample,
124 IAVIStream_fnReadFormat,
125 IAVIStream_fnSetFormat,
129 IAVIStream_fnReadData,
130 IAVIStream_fnWriteData,
134 typedef struct _IAVIFileImpl IAVIFileImpl;
136 typedef struct _IPersistFileImpl {
138 ICOM_VFIELD(IPersistFile);
140 /* IPersistFile stuff */
144 typedef struct _IAVIStreamImpl {
146 ICOM_VFIELD(IAVIStream);
149 /* IAVIStream stuff */
151 DWORD nStream; /* the n-th stream in file */
152 AVISTREAMINFOW sInfo;
157 LPVOID lpHandlerData;
163 DWORD cbBuffer; /* size of lpBuffer */
164 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
166 LONG lLastFrame; /* last correct index in idxFrames */
167 AVIINDEXENTRY *idxFrames;
168 DWORD nIdxFrames; /* upper index limit of idxFrames */
169 AVIINDEXENTRY *idxFmtChanges;
170 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
173 struct _IAVIFileImpl {
175 ICOM_VFIELD(IAVIFile);
178 /* IAVIFile stuff... */
179 IPersistFileImpl iPersistFile;
182 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
184 EXTRACHUNKS fileextra;
186 DWORD dwMoviChunkPos; /* some stuff for saving ... */
188 DWORD dwNextFramePos;
190 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
193 /* IPersistFile stuff ... */
200 /***********************************************************************/
202 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
203 DWORD offset, DWORD flags);
204 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
205 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
206 LPAVISTREAMINFOW asi);
207 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
208 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
209 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset);
210 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
211 LONG count, DWORD pos, BOOL *bAbsolute);
212 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
213 LPVOID buffer, LONG size);
214 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
216 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
217 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This);
218 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fccType,
220 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
221 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
222 FOURCC ckid, DWORD flags, LPVOID buffer,
225 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
230 assert(riid != NULL && ppv != NULL);
234 pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl));
236 return AVIERR_MEMORY;
238 pfile->lpVtbl = &iavift;
240 pfile->iPersistFile.lpVtbl = &ipersistft;
241 pfile->iPersistFile.paf = pfile;
243 hr = IUnknown_QueryInterface((IUnknown*)pfile, riid, ppv);
245 LocalFree((HLOCAL)pfile);
250 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
253 ICOM_THIS(IAVIFileImpl,iface);
255 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
257 if (IsEqualGUID(&IID_IUnknown, refiid) ||
258 IsEqualGUID(&IID_IAVIFile, refiid)) {
260 IAVIFile_AddRef(iface);
263 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
264 *obj = &This->iPersistFile;
265 IAVIFile_AddRef(iface);
270 return OLE_E_ENUM_NOMORE;
273 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
275 ICOM_THIS(IAVIFileImpl,iface);
277 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
278 return ++(This->ref);
281 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
283 ICOM_THIS(IAVIFileImpl,iface);
286 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
288 if (!--(This->ref)) {
290 /* need to write headers to file */
291 AVIFILE_SaveFile(This);
294 for (i = 0; i < This->fInfo.dwStreams; i++) {
295 if (This->ppStreams[i] != NULL) {
296 if (This->ppStreams[i]->ref != 0) {
297 ERR(": someone has still a reference to stream %u (%p)!\n",
298 i, This->ppStreams[i]);
300 AVIFILE_DestructAVIStream(This->ppStreams[i]);
301 LocalFree((HLOCAL)This->ppStreams[i]);
302 This->ppStreams[i] = NULL;
306 if (This->idxRecords != NULL) {
307 GlobalFreePtr(This->idxRecords);
308 This->idxRecords = NULL;
309 This->nIdxRecords = 0;
312 if (This->fileextra.lp != NULL) {
313 GlobalFreePtr(This->fileextra.lp);
314 This->fileextra.lp = NULL;
315 This->fileextra.cb = 0;
318 if (This->szFileName != NULL) {
319 LocalFree((HLOCAL)This->szFileName);
320 This->szFileName = NULL;
322 if (This->hmmio != NULL) {
323 mmioClose(This->hmmio, 0);
327 LocalFree((HLOCAL)This);
333 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
336 ICOM_THIS(IAVIFileImpl,iface);
338 TRACE("(%p,%p,%ld)\n",iface,afi,size);
341 return AVIERR_BADPARAM;
343 return AVIERR_BADSIZE;
345 AVIFILE_UpdateInfo(This);
347 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
349 if ((DWORD)size < sizeof(This->fInfo))
350 return AVIERR_BUFFERTOOSMALL;
354 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
355 DWORD fccType, LONG lParam)
357 ICOM_THIS(IAVIFileImpl,iface);
361 TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam);
363 if (avis == NULL || lParam < 0)
364 return AVIERR_BADPARAM;
366 nStream = AVIFILE_SearchStream(This, fccType, lParam);
368 /* Does the requested stream exist? */
369 if (nStream < This->fInfo.dwStreams &&
370 This->ppStreams[nStream] != NULL) {
371 *avis = (PAVISTREAM)This->ppStreams[nStream];
372 IAVIStream_AddRef(*avis);
377 /* Sorry, but the specified stream doesn't exist */
378 return AVIERR_NODATA;
381 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
382 LPAVISTREAMINFOW asi)
384 ICOM_THIS(IAVIFileImpl,iface);
388 TRACE("(%p,%p,%p)\n", iface, avis, asi);
390 /* check parameters */
391 if (avis == NULL || asi == NULL)
392 return AVIERR_BADPARAM;
396 /* Does the user have write permission? */
397 if ((This->uMode & MMIO_RWMODE) == 0)
398 return AVIERR_READONLY;
400 /* Can we add another stream? */
401 n = This->fInfo.dwStreams;
402 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
403 /* already reached max nr of streams
404 * or have already written frames to disk */
405 return AVIERR_UNSUPPORTED;
408 /* check AVISTREAMINFO for some really needed things */
409 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
410 return AVIERR_BADFORMAT;
412 /* now it seems to be save to add the stream */
413 assert(This->ppStreams[n] == NULL);
414 This->ppStreams[n] = (IAVIStreamImpl*)LocalAlloc(LPTR,
415 sizeof(IAVIStreamImpl));
416 if (This->ppStreams[n] == NULL)
417 return AVIERR_MEMORY;
419 /* initialize the new allocated stream */
420 AVIFILE_ConstructAVIStream(This, n, asi);
422 This->fInfo.dwStreams++;
425 /* update our AVIFILEINFO structure */
426 AVIFILE_UpdateInfo(This);
429 *avis = (PAVISTREAM)This->ppStreams[n];
430 IAVIStream_AddRef(*avis);
435 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
436 LPVOID lpData, LONG size)
438 ICOM_THIS(IAVIFileImpl,iface);
440 TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
442 /* check parameters */
444 return AVIERR_BADPARAM;
446 return AVIERR_BADSIZE;
448 /* Do we have write permission? */
449 if ((This->uMode & MMIO_RWMODE) == 0)
450 return AVIERR_READONLY;
454 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
457 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
458 LPVOID lpData, LONG *size)
460 ICOM_THIS(IAVIFileImpl,iface);
462 TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
464 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
467 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
469 ICOM_THIS(IAVIFileImpl,iface);
471 FIXME("(%p): stub\n",iface);
473 if ((This->uMode & MMIO_RWMODE) == 0)
474 return AVIERR_READONLY;
478 /* FIXME: end record -- for interleaved files */
483 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
486 ICOM_THIS(IAVIFileImpl,iface);
490 TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
492 /* check parameter */
494 return AVIERR_BADPARAM;
496 /* Habe user write permissions? */
497 if ((This->uMode & MMIO_RWMODE) == 0)
498 return AVIERR_READONLY;
500 nStream = AVIFILE_SearchStream(This, fccType, lParam);
502 /* Does the requested stream exist? */
503 if (nStream < This->fInfo.dwStreams &&
504 This->ppStreams[nStream] != NULL) {
505 /* ... so delete it now */
506 LocalFree((HLOCAL)This->ppStreams[nStream]);
508 if (This->fInfo.dwStreams - nStream > 0)
509 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
510 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
512 This->ppStreams[This->fInfo.dwStreams] = NULL;
513 This->fInfo.dwStreams--;
516 /* This->fInfo will be updated further when asked for */
519 return AVIERR_NODATA;
522 /***********************************************************************/
524 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
525 REFIID refiid, LPVOID *obj)
527 ICOM_THIS(IPersistFileImpl,iface);
529 assert(This->paf != NULL);
531 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
534 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
536 ICOM_THIS(IPersistFileImpl,iface);
538 assert(This->paf != NULL);
540 return IAVIFile_AddRef((PAVIFILE)This->paf);
543 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
545 ICOM_THIS(IPersistFileImpl,iface);
547 assert(This->paf != NULL);
549 return IAVIFile_Release((PAVIFILE)This->paf);
552 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
555 TRACE("(%p,%p)\n", iface, pClassID);
557 if (pClassID == NULL)
558 return AVIERR_BADPARAM;
560 memcpy(pClassID, &CLSID_AVIFile, sizeof(CLSID_AVIFile));
565 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
567 ICOM_THIS(IPersistFileImpl,iface);
569 TRACE("(%p)\n", iface);
571 assert(This->paf != NULL);
573 return (This->paf->fDirty ? S_OK : S_FALSE);
576 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
577 LPCOLESTR pszFileName, DWORD dwMode)
579 ICOM_THIS(IPersistFileImpl,iface);
583 TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
585 /* check parameter */
586 if (pszFileName == NULL)
587 return AVIERR_BADPARAM;
589 assert(This->paf != NULL);
590 if (This->paf->hmmio != NULL)
591 return AVIERR_ERROR; /* No reuse of this object for another file! */
593 /* remeber mode and name */
594 This->paf->uMode = dwMode;
596 len = lstrlenW(pszFileName) + 1;
597 This->paf->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR));
598 if (This->paf->szFileName == NULL)
599 return AVIERR_MEMORY;
600 lstrcpyW(This->paf->szFileName, pszFileName);
602 /* try to open the file */
603 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
604 MMIO_ALLOCBUF | dwMode);
605 if (This->paf->hmmio == NULL)
606 return AVIERR_FILEOPEN;
608 /* should we create a new file? */
609 if (dwMode & OF_CREATE) {
610 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
611 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
615 return AVIFILE_LoadFile(This->paf);
618 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
619 LPCOLESTR pszFileName,BOOL fRemember)
621 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
623 /* We write directly to disk, so nothing to do. */
628 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
629 LPCOLESTR pszFileName)
631 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
633 /* We write directly to disk, so nothing to do. */
638 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
639 LPOLESTR *ppszFileName)
641 ICOM_THIS(IPersistFileImpl,iface);
643 TRACE("(%p,%p)\n", iface, ppszFileName);
645 if (ppszFileName == NULL)
646 return AVIERR_BADPARAM;
648 *ppszFileName = NULL;
650 assert(This->paf != NULL);
652 if (This->paf->szFileName != NULL) {
653 int len = lstrlenW(This->paf->szFileName);
655 *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR));
656 if (*ppszFileName == NULL)
657 return AVIERR_MEMORY;
659 memcpy(*ppszFileName, This->paf->szFileName, len * sizeof(WCHAR));
665 /***********************************************************************/
667 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
668 REFIID refiid, LPVOID *obj)
670 ICOM_THIS(IAVIStreamImpl,iface);
672 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
674 if (IsEqualGUID(&IID_IUnknown, refiid) ||
675 IsEqualGUID(&IID_IAVIStream, refiid)) {
677 IAVIStream_AddRef(iface);
681 /* FIXME: IAVIStreaming interface */
683 return OLE_E_ENUM_NOMORE;
686 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
688 ICOM_THIS(IAVIStreamImpl,iface);
690 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
692 /* also add ref to parent, so that it doesn't kill us */
693 if (This->paf != NULL)
694 IAVIFile_AddRef((PAVIFILE)This->paf);
696 return ++(This->ref);
699 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
701 ICOM_THIS(IAVIStreamImpl,iface);
703 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
705 /* we belong to the AVIFile, which must free us! */
706 if (This->ref == 0) {
707 ERR(": already released!\n");
711 if (This->paf != NULL)
712 IAVIFile_Release((PAVIFILE)This->paf);
717 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
720 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
722 /* This IAVIStream interface needs an AVIFile */
723 return AVIERR_UNSUPPORTED;
726 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
729 ICOM_THIS(IAVIStreamImpl,iface);
731 TRACE("(%p,%p,%ld)\n", iface, psi, size);
734 return AVIERR_BADPARAM;
736 return AVIERR_BADSIZE;
738 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
740 if ((DWORD)size < sizeof(This->sInfo))
741 return AVIERR_BUFFERTOOSMALL;
745 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
748 ICOM_THIS(IAVIStreamImpl,iface);
752 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
754 if (flags & FIND_FROM_START) {
755 pos = This->sInfo.dwStart;
756 flags &= ~(FIND_FROM_START|FIND_PREV);
760 if (This->sInfo.dwSampleSize != 0) {
761 /* convert samples into block number with offset */
762 AVIFILE_SamplesToBlock(This, &pos, &offset);
765 if (flags & FIND_TYPE) {
766 if (flags & FIND_KEY) {
767 while (0 <= pos && pos <= This->lLastFrame) {
768 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
771 if (flags & FIND_NEXT)
776 } else if (flags & FIND_ANY) {
777 while (0 <= pos && pos <= This->lLastFrame) {
778 if (This->idxFrames[pos].dwChunkLength > 0)
781 if (flags & FIND_NEXT)
787 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
788 This->sInfo.fccType == streamtypeVIDEO) {
789 if (flags & FIND_NEXT) {
792 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
793 if (This->idxFmtChanges[n].ckid >= pos)
798 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
799 if (This->idxFmtChanges[n].ckid <= pos)
803 if (pos > (LONG)This->sInfo.dwStart)
804 return 0; /* format changes always for first frame */
811 if (flags & FIND_RET) {
813 if (flags & FIND_LENGTH) {
815 if (This->sInfo.dwSampleSize)
816 pos = This->sInfo.dwSampleSize;
819 } else if (flags & FIND_OFFSET) {
820 /* physical position */
821 pos = This->idxFrames[pos].dwChunkOffset + offset * This->sInfo.dwSampleSize;
822 } else if (flags & FIND_SIZE) {
824 pos = This->idxFrames[pos].dwChunkLength;
825 } else if (flags & FIND_INDEX) {
826 FIXME(": FIND_INDEX flag is not supported!");
828 pos = This->paf->dwIdxChunkPos;
829 } /* else logical position */
837 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
838 LPVOID format, LONG *formatsize)
840 ICOM_THIS(IAVIStreamImpl,iface);
842 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
844 if (formatsize == NULL)
845 return AVIERR_BADPARAM;
847 /* only interested in needed buffersize? */
848 if (format == NULL || *formatsize <= 0) {
849 *formatsize = This->cbFormat;
854 /* copy initial format (only as much as will fit) */
855 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
856 if (*(DWORD*)formatsize < This->cbFormat) {
857 *formatsize = This->cbFormat;
858 return AVIERR_BUFFERTOOSMALL;
861 /* Could format change? When yes will it change? */
862 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
863 pos > This->sInfo.dwStart) {
866 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
868 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt);
872 *formatsize = This->cbFormat;
876 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
877 LPVOID format, LONG formatsize)
879 ICOM_THIS(IAVIStreamImpl,iface);
881 LPBITMAPINFOHEADER lpbiNew = (LPBITMAPINFOHEADER)format;
883 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
885 /* check parameters */
886 if (format == NULL || formatsize <= 0)
887 return AVIERR_BADPARAM;
889 /* Do we have write permission? */
890 if ((This->paf->uMode & MMIO_RWMODE) == 0)
891 return AVIERR_READONLY;
893 /* can only set format before frame is written! */
894 if (This->lLastFrame > pos)
895 return AVIERR_UNSUPPORTED;
897 /* initial format or a formatchange? */
898 if (This->lpFormat == NULL) {
900 if (This->paf->dwMoviChunkPos != 0)
901 return AVIERR_ERROR; /* user has used API in wrong sequnece! */
903 This->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, formatsize);
904 if (This->lpFormat == NULL)
905 return AVIERR_MEMORY;
906 This->cbFormat = formatsize;
908 memcpy(This->lpFormat, format, formatsize);
910 /* update some infos about stream */
911 if (This->sInfo.fccType == streamtypeVIDEO) {
914 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
915 if (lDim < lpbiNew->biWidth)
916 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
917 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
918 if (lDim < lpbiNew->biHeight)
919 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
920 } else if (This->sInfo.fccType == streamtypeAUDIO)
921 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
926 LPBITMAPINFOHEADER lpbiOld = (LPBITMAPINFOHEADER)This->lpFormat;
927 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
928 AVIPALCHANGE *lppc = NULL;
931 /* pherhaps formatchange, check it ... */
932 if (This->cbFormat != formatsize)
933 return AVIERR_UNSUPPORTED;
935 /* no formatchange, only the initial one */
936 if (memcmp(This->lpFormat, format, formatsize) == 0)
939 /* check that's only the palette, which changes */
940 if (lpbiOld->biSize != lpbiNew->biSize ||
941 lpbiOld->biWidth != lpbiNew->biWidth ||
942 lpbiOld->biHeight != lpbiNew->biHeight ||
943 lpbiOld->biPlanes != lpbiNew->biPlanes ||
944 lpbiOld->biBitCount != lpbiNew->biBitCount ||
945 lpbiOld->biCompression != lpbiNew->biCompression ||
946 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
947 return AVIERR_UNSUPPORTED;
949 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
951 /* simply say all colors have changed */
952 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
953 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
954 lppc = (AVIPALCHANGE*)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
956 return AVIERR_MEMORY;
958 lppc->bFirstEntry = 0;
959 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
961 for (n = 0; n < lpbiOld->biClrUsed; n++) {
962 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
963 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
964 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
965 lppc->peNew[n].peFlags = 0;
968 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
969 return AVIERR_FILEWRITE;
970 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
971 return AVIERR_FILEWRITE;
972 if (mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize)
973 return AVIERR_FILEWRITE;
974 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
975 return AVIERR_FILEWRITE;
976 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
980 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
984 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
985 LONG samples, LPVOID buffer,
986 LONG buffersize, LPLONG bytesread,
989 ICOM_THIS(IAVIStreamImpl,iface);
994 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
995 buffersize, bytesread, samplesread);
997 /* clear return parameters if given */
998 if (bytesread != NULL)
1000 if (samplesread != NULL)
1003 /* check parameters */
1004 if ((LONG)This->sInfo.dwStart > start)
1005 return AVIERR_NODATA; /* couldn't read before start of stream */
1006 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1007 return AVIERR_NODATA; /* start is past end of stream */
1009 /* should we read as much as possible? */
1010 if (samples == -1) {
1011 /* User should know how much we have read */
1012 if (bytesread == NULL && samplesread == NULL)
1013 return AVIERR_BADPARAM;
1015 if (This->sInfo.dwSampleSize != 0)
1016 samples = buffersize / This->sInfo.dwSampleSize;
1021 /* limit to end of stream */
1022 if ((LONG)This->sInfo.dwLength < samples)
1023 samples = This->sInfo.dwLength;
1024 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1025 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1027 /* nothing to read? Then leave ... */
1031 if (This->sInfo.dwSampleSize != 0) {
1032 /* fixed samplesize -- we can read over frame/block boundaries */
1036 /* convert start sample to block,offset pair */
1037 AVIFILE_SamplesToBlock(This, &block, &offset);
1039 /* convert samples to bytes */
1040 samples *= This->sInfo.dwSampleSize;
1042 while (samples > 0 && buffersize > 0) {
1043 if (block != This->dwCurrentFrame) {
1044 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1049 size = min((DWORD)samples, (DWORD)buffersize);
1050 size = min(size, This->cbBuffer - offset);
1051 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1055 ((BYTE*)buffer) += size;
1059 /* fill out return parameters if given */
1060 if (bytesread != NULL)
1062 if (samplesread != NULL)
1063 *samplesread += size / This->sInfo.dwSampleSize;
1069 return AVIERR_BUFFERTOOSMALL;
1071 /* variable samplesize -- we can only read one full frame/block */
1075 assert(start <= This->lLastFrame);
1076 size = This->idxFrames[start].dwChunkLength;
1077 if (buffer != NULL && buffersize >= size) {
1078 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1081 } else if (buffer != NULL)
1082 return AVIERR_BUFFERTOOSMALL;
1084 /* fill out return parameters if given */
1085 if (bytesread != NULL)
1087 if (samplesread != NULL)
1088 *samplesread = samples;
1094 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
1095 LONG samples, LPVOID buffer,
1096 LONG buffersize, DWORD flags,
1098 LPLONG byteswritten)
1100 ICOM_THIS(IAVIStreamImpl,iface);
1105 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
1106 buffer, buffersize, flags, sampwritten, byteswritten);
1108 /* clear return parameters if given */
1109 if (sampwritten != NULL)
1111 if (byteswritten != NULL)
1114 /* check parameters */
1115 if (buffer == NULL && (buffersize > 0 || samples > 0))
1116 return AVIERR_BADPARAM;
1118 /* Have we write permission? */
1119 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1120 return AVIERR_READONLY;
1122 switch (This->sInfo.fccType) {
1123 case streamtypeAUDIO:
1124 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1127 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1128 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1130 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1134 /* append to end of stream? */
1136 if (This->lLastFrame == -1)
1137 start = This->sInfo.dwStart;
1139 start = This->sInfo.dwLength;
1140 } else if (This->lLastFrame == -1)
1141 This->sInfo.dwStart = start;
1143 if (This->sInfo.dwSampleSize != 0) {
1144 /* fixed sample size -- audio like */
1145 if (samples * This->sInfo.dwSampleSize != buffersize)
1146 return AVIERR_BADPARAM;
1148 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1149 if (This->sInfo.dwLength != start)
1150 return AVIERR_UNSUPPORTED;
1152 /* Convert position to frame/block */
1153 start = This->lLastFrame + 1;
1155 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1156 FIXME(": not interleaved, could collect audio data!\n");
1159 /* variable sample size -- video like */
1161 return AVIERR_UNSUPPORTED;
1163 /* must we fill up with empty frames? */
1164 if (This->lLastFrame != -1) {
1165 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1167 while (start > This->lLastFrame + 1) {
1168 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1175 /* write the block now */
1176 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1177 if (SUCCEEDED(hr)) {
1178 /* fill out return parameters if given */
1179 if (sampwritten != NULL)
1180 *sampwritten = samples;
1181 if (byteswritten != NULL)
1182 *byteswritten = buffersize;
1188 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
1191 ICOM_THIS(IAVIStreamImpl,iface);
1193 FIXME("(%p,%ld,%ld): stub\n", iface, start, samples);
1195 /* check parameters */
1196 if (start < 0 || samples < 0)
1197 return AVIERR_BADPARAM;
1199 /* Delete before start of stream? */
1200 if (start + samples < This->sInfo.dwStart)
1203 /* Delete after end of stream? */
1204 if (start > This->sInfo.dwLength)
1207 /* For the rest we need write permissions */
1208 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1209 return AVIERR_READONLY;
1211 /* 1. overwrite the data with JUNK
1213 * if ISINTERLEAVED {
1214 * 2. concat all neighboured JUNK-blocks in this record to one
1215 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1216 * to start of this record, repeat this.
1218 * 2. concat all neighboured JUNK-blocks.
1219 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1220 * start of this block.
1224 return AVIERR_UNSUPPORTED;
1227 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
1228 LPVOID lp, LPLONG lpread)
1230 ICOM_THIS(IAVIStreamImpl,iface);
1232 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
1234 if (fcc == ckidSTREAMHANDLERDATA) {
1235 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1236 if (lp == NULL || *lpread <= 0) {
1237 *lpread = This->cbHandlerData;
1241 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1242 if (*lpread < This->cbHandlerData)
1243 return AVIERR_BUFFERTOOSMALL;
1246 return AVIERR_NODATA;
1248 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1251 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
1252 LPVOID lp, LONG size)
1254 ICOM_THIS(IAVIStreamImpl,iface);
1256 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
1258 /* check parameters */
1260 return AVIERR_BADPARAM;
1262 return AVIERR_BADSIZE;
1264 /* need write permission */
1265 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1266 return AVIERR_READONLY;
1268 /* already written something to this file? */
1269 if (This->paf->dwMoviChunkPos != 0) {
1270 /* the data will be inserted before the 'movi' chunk, so check for
1272 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1274 /* ckid,size => 2 * sizeof(DWORD) */
1275 dwPos += 2 * sizeof(DWORD) + size;
1276 if (size >= This->paf->dwMoviChunkPos)
1277 return AVIERR_UNSUPPORTED; /* not enough space left */
1280 This->paf->fDirty = TRUE;
1282 if (fcc == ckidSTREAMHANDLERDATA) {
1283 if (This->lpHandlerData != NULL) {
1284 FIXME(": handler data already set -- overwirte?\n");
1285 return AVIERR_UNSUPPORTED;
1288 This->lpHandlerData = GlobalAllocPtr(GMEM_MOVEABLE, size);
1289 if (This->lpHandlerData == NULL)
1290 return AVIERR_MEMORY;
1291 This->cbHandlerData = size;
1292 memcpy(This->lpHandlerData, lp, size);
1296 return WriteExtraChunk(&This->extra, fcc, lp, size);
1299 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
1300 LPAVISTREAMINFOW info, LONG infolen)
1302 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
1307 /***********************************************************************/
1309 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1311 /* pre-conditions */
1312 assert(This != NULL);
1314 switch (TWOCCFromFOURCC(ckid)) {
1316 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1317 flags |= AVIIF_KEYFRAME;
1319 case cktypeDIBcompressed:
1320 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1321 flags &= ~AVIIF_KEYFRAME;
1323 case cktypePALchange:
1324 if (This->sInfo.fccType != streamtypeVIDEO) {
1325 ERR(": found palette change in non-video stream!\n");
1326 return AVIERR_BADFORMAT;
1328 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1329 This->sInfo.dwFormatChangeCount++;
1331 if (This->idxFmtChanges == NULL || This->sInfo.dwFormatChangeCount < This->nIdxFmtChanges) {
1332 UINT n = This->sInfo.dwFormatChangeCount;
1334 This->nIdxFmtChanges += 16;
1335 This->idxFmtChanges = GlobalReAllocPtr(This->idxFmtChanges, This->nIdxFmtChanges * sizeof(AVIINDEXENTRY), GHND);
1336 if (This->idxFmtChanges == NULL)
1337 return AVIERR_MEMORY;
1339 This->idxFmtChanges[n].ckid = This->lLastFrame;
1340 This->idxFmtChanges[n].dwFlags = 0;
1341 This->idxFmtChanges[n].dwChunkOffset = offset;
1342 This->idxFmtChanges[n].dwChunkLength = size;
1347 case cktypeWAVEbytes:
1348 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1349 flags |= AVIIF_KEYFRAME;
1352 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1356 /* first frame is alwasy a keyframe */
1357 if (This->lLastFrame == -1)
1358 flags |= AVIIF_KEYFRAME;
1360 if (This->sInfo.dwSuggestedBufferSize < size)
1361 This->sInfo.dwSuggestedBufferSize = size;
1363 /* get memory for index */
1364 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1365 This->nIdxFrames += 512;
1366 This->idxFrames = GlobalReAllocPtr(This->idxFrames, This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND);
1367 if (This->idxFrames == NULL)
1368 return AVIERR_MEMORY;
1372 This->idxFrames[This->lLastFrame].ckid = ckid;
1373 This->idxFrames[This->lLastFrame].dwFlags = flags;
1374 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1375 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1377 /* update AVISTREAMINFO structure if necessary */
1378 if (This->sInfo.dwLength < This->lLastFrame)
1379 This->sInfo.dwLength = This->lLastFrame;
1384 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1389 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1390 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1392 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1393 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1395 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1396 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1397 dwPos += ((pStream->cbFormat + 1) & ~1U);
1398 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1399 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1400 if (lstrlenW(pStream->sInfo.szName) > 0)
1401 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1404 if (This->dwMoviChunkPos == 0) {
1405 This->dwNextFramePos = dwPos;
1407 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1408 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1409 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1411 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1417 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, LPAVISTREAMINFOW asi)
1419 IAVIStreamImpl *pstream;
1421 /* pre-conditions */
1422 assert(paf != NULL);
1423 assert(nr < MAX_AVISTREAMS);
1424 assert(paf->ppStreams[nr] != NULL);
1426 pstream = paf->ppStreams[nr];
1428 pstream->lpVtbl = &iavist;
1431 pstream->nStream = nr;
1432 pstream->dwCurrentFrame = (DWORD)-1;
1433 pstream->lLastFrame = -1;
1436 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1438 if (asi->dwLength > 0) {
1439 /* pre-allocate mem for frame-index structure */
1440 pstream->idxFrames =
1441 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwLength * sizeof(AVIINDEXENTRY));
1442 if (pstream->idxFrames != NULL)
1443 pstream->nIdxFrames = asi->dwLength;
1445 if (asi->dwFormatChangeCount > 0) {
1446 /* pre-allocate mem for formatchange-index structure */
1447 pstream->idxFmtChanges =
1448 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1449 if (pstream->idxFmtChanges != NULL)
1450 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1453 /* These values will be computed */
1454 pstream->sInfo.dwLength = 0;
1455 pstream->sInfo.dwSuggestedBufferSize = 0;
1456 pstream->sInfo.dwFormatChangeCount = 0;
1457 pstream->sInfo.dwEditCount = 1;
1458 if (pstream->sInfo.dwSampleSize > 0)
1459 SetRectEmpty(&pstream->sInfo.rcFrame);
1462 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1465 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1467 /* pre-conditions */
1468 assert(This != NULL);
1470 This->dwCurrentFrame = (DWORD)-1;
1471 This->lLastFrame = -1;
1473 if (This->idxFrames != NULL) {
1474 GlobalFreePtr(This->idxFrames);
1475 This->idxFrames = NULL;
1476 This->nIdxFrames = 0;
1478 if (This->idxFmtChanges != NULL) {
1479 GlobalFreePtr(This->idxFmtChanges);
1480 This->idxFmtChanges = NULL;
1482 if (This->lpBuffer != NULL) {
1483 GlobalFreePtr(This->lpBuffer);
1484 This->lpBuffer = NULL;
1487 if (This->lpHandlerData != NULL) {
1488 GlobalFreePtr(This->lpHandlerData);
1489 This->lpHandlerData = NULL;
1490 This->cbHandlerData = 0;
1492 if (This->extra.lp != NULL) {
1493 GlobalFreePtr(This->extra.lp);
1494 This->extra.lp = NULL;
1497 if (This->lpFormat != NULL) {
1498 GlobalFreePtr(This->lpFormat);
1499 This->lpFormat = NULL;
1504 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1506 MainAVIHeader MainAVIHdr;
1511 IAVIStreamImpl *pStream;
1515 if (This->hmmio == NULL)
1516 return AVIERR_FILEOPEN;
1518 /* initialize stream ptr's */
1519 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1521 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1522 ckRIFF.fccType = formtypeAVI;
1523 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1524 ERR(": not an AVI!\n");
1525 return AVIERR_FILEREAD;
1528 /* get "LIST" "hdrl" */
1529 ckLIST1.fccType = listtypeAVIHEADER;
1530 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1534 /* get "avih" chunk */
1535 ck.ckid = ckidAVIMAINHDR;
1536 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1540 if (ck.cksize != sizeof(MainAVIHdr)) {
1541 ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize);
1542 return AVIERR_BADFORMAT;
1544 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1545 return AVIERR_FILEREAD;
1547 /* check for MAX_AVISTREAMS limit */
1548 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1549 WARN("file contains %lu streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1550 return AVIERR_UNSUPPORTED;
1553 /* adjust permissions if copyrighted material in file */
1554 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1555 This->uMode &= ~MMIO_RWMODE;
1556 This->uMode |= MMIO_READ;
1559 /* convert MainAVIHeader into AVIFILINFOW */
1560 memset(&This->fInfo, 0, sizeof(This->fInfo));
1561 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1562 This->fInfo.dwScale = 1000000;
1563 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1564 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1565 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1566 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1567 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1568 This->fInfo.dwSuggestedBufferSize = MainAVIHdr.dwSuggestedBufferSize;
1569 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1570 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1571 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1572 sizeof(This->fInfo.szFileType));
1574 /* go back to into header list */
1575 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1576 return AVIERR_FILEREAD;
1578 /* foreach stream exists a "LIST","strl" chunk */
1579 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1580 /* get next nested chunk in this "LIST","strl" */
1581 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1582 return AVIERR_FILEREAD;
1584 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1585 if (ckLIST2.ckid == FOURCC_LIST &&
1586 ckLIST2.fccType == listtypeSTREAMHEADER) {
1587 pStream = This->ppStreams[nStream] =
1588 (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
1589 if (pStream == NULL)
1590 return AVIERR_MEMORY;
1591 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1594 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1596 case ckidSTREAMHANDLERDATA:
1597 if (pStream->lpHandlerData != NULL)
1598 return AVIERR_BADFORMAT;
1599 pStream->lpHandlerData = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1601 if (pStream->lpHandlerData == NULL)
1602 return AVIERR_MEMORY;
1603 pStream->cbHandlerData = ck.cksize;
1605 if (mmioRead(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
1606 return AVIERR_FILEREAD;
1608 case ckidSTREAMFORMAT:
1609 if (pStream->lpFormat != NULL)
1610 return AVIERR_BADFORMAT;
1611 pStream->lpFormat = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1613 if (pStream->lpFormat == NULL)
1614 return AVIERR_MEMORY;
1615 pStream->cbFormat = ck.cksize;
1617 if (mmioRead(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
1618 return AVIERR_FILEREAD;
1620 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1621 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pStream->lpFormat;
1623 /* some corrections to the video format */
1624 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1625 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1626 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1627 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1628 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1629 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1630 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1631 lpbi->biCompression = BI_RLE8;
1633 if (lpbi->biCompression == BI_RGB &&
1634 (pStream->sInfo.fccHandler == 0 ||
1635 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1636 pStream->sInfo.fccHandler = comptypeDIB;
1638 /* init rcFrame if it's empty */
1639 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1640 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1643 case ckidSTREAMHEADER:
1645 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1647 AVIStreamHeader streamHdr;
1649 WCHAR streamNameFmt[25];
1653 if (ck.cksize > sizeof(streamHdr))
1654 n = sizeof(streamHdr);
1656 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1657 return AVIERR_FILEREAD;
1659 pStream->sInfo.fccType = streamHdr.fccType;
1660 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1661 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1662 pStream->sInfo.wPriority = streamHdr.wPriority;
1663 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1664 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1665 pStream->sInfo.dwScale = streamHdr.dwScale;
1666 pStream->sInfo.dwRate = streamHdr.dwRate;
1667 pStream->sInfo.dwStart = streamHdr.dwStart;
1668 pStream->sInfo.dwLength = streamHdr.dwLength;
1669 pStream->sInfo.dwSuggestedBufferSize =
1670 streamHdr.dwSuggestedBufferSize;
1671 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1672 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1673 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1674 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1675 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1676 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1677 pStream->sInfo.dwEditCount = 0;
1678 pStream->sInfo.dwFormatChangeCount = 0;
1680 /* generate description for stream like "filename.avi Type #n" */
1681 if (streamHdr.fccType == streamtypeVIDEO)
1682 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType));
1683 else if (streamHdr.fccType == streamtypeAUDIO)
1684 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType));
1686 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1688 /* get count of this streamtype up to this stream */
1690 for (n = nStream; 0 <= n; n--) {
1691 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1695 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1697 LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt));
1699 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1700 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1701 AVIFILE_BasenameW(This->szFileName), szType, count);
1704 case ckidSTREAMNAME:
1705 { /* streamname will be saved as ASCII string */
1706 LPSTR str = (LPSTR)LocalAlloc(LMEM_FIXED, ck.cksize);
1708 return AVIERR_MEMORY;
1710 if (mmioRead(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize)
1711 return AVIERR_FILEREAD;
1713 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1714 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1716 LocalFree((HLOCAL)str);
1719 case ckidAVIPADDING:
1720 case mmioFOURCC('p','a','d','d'):
1723 WARN(": found extra chunk 0x%08lX\n", ck.ckid);
1724 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1729 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1730 return AVIERR_FILEREAD;
1733 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1734 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1737 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1738 return AVIERR_FILEREAD;
1742 /* read any extra headers in "LIST","hdrl" */
1743 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1744 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1745 return AVIERR_FILEREAD;
1747 /* search "LIST","movi" chunk in "RIFF","AVI " */
1748 ckLIST1.fccType = listtypeAVIMOVIE;
1749 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1754 This->dwMoviChunkPos = ckLIST1.dwDataOffset - 2 * sizeof(DWORD);
1755 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1756 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1757 return AVIERR_FILEREAD;
1759 /* try to find an index */
1760 ck.ckid = ckidAVINEWINDEX;
1761 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1762 &ck, &ckRIFF, MMIO_FINDCHUNK);
1763 if (SUCCEEDED(hr) && ck.cksize > 0) {
1764 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1765 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1768 /* when we haven't found an index or it's bad, then build one
1769 * by parsing 'movi' chunk */
1770 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1771 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1772 This->ppStreams[nStream]->lLastFrame = -1;
1774 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1775 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1776 return AVIERR_FILEREAD;
1779 /* seek through the 'movi' list until end */
1780 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1781 if (ck.ckid != FOURCC_LIST) {
1782 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1783 nStream = StreamFromFOURCC(ck.ckid);
1785 if (nStream > This->fInfo.dwStreams)
1786 return AVIERR_BADFORMAT;
1788 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1789 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1791 nStream = StreamFromFOURCC(ck.ckid);
1792 WARN(": file seems to be truncated!\n");
1793 if (nStream <= This->fInfo.dwStreams &&
1794 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1795 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1796 if (ck.cksize != -1) {
1797 ck.cksize -= ck.dwDataOffset;
1798 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1800 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1801 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1809 /* find other chunks */
1810 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1815 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset)
1819 HRESULT hr = AVIERR_OK;
1820 BOOL bAbsolute = TRUE;
1822 lp = (AVIINDEXENTRY*)GlobalAllocPtr(GMEM_MOVEABLE,
1823 IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1825 return AVIERR_MEMORY;
1827 /* adjust limits for index tables, so that inserting will be faster */
1828 for (n = 0; n < This->fInfo.dwStreams; n++) {
1829 IAVIStreamImpl *pStream = This->ppStreams[n];
1831 pStream->lLastFrame = -1;
1833 if (pStream->idxFrames != NULL) {
1834 GlobalFreePtr(pStream->idxFrames);
1835 pStream->idxFrames = NULL;
1836 pStream->nIdxFrames = 0;
1839 if (pStream->sInfo.dwSampleSize != 0) {
1840 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1841 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1842 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1843 pStream->nIdxFrames =
1844 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1847 pStream->nIdxFrames = pStream->sInfo.dwLength;
1849 pStream->idxFrames =
1850 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1851 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1852 pStream->nIdxFrames = 0;
1853 return AVIERR_MEMORY;
1859 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1861 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1862 hr = AVIERR_FILEREAD;
1867 if (pos == (DWORD)-1)
1868 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1870 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1880 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
1881 LONG count, DWORD pos, BOOL *bAbsolute)
1884 return AVIERR_BADPARAM;
1886 for (; count > 0; count--, lp++) {
1887 WORD nStream = StreamFromFOURCC(lp->ckid);
1889 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1890 continue; /* skip these */
1892 if (nStream > This->fInfo.dwStreams)
1893 return AVIERR_BADFORMAT;
1895 if (*bAbsolute == TRUE && lp->dwChunkOffset < This->dwMoviChunkPos)
1899 lp->dwChunkOffset += sizeof(DWORD);
1901 lp->dwChunkOffset += pos;
1903 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1904 return AVIERR_MEMORY;
1910 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1911 LPVOID buffer, LONG size)
1913 /* pre-conditions */
1914 assert(This != NULL);
1915 assert(This->paf != NULL);
1916 assert(This->paf->hmmio != NULL);
1917 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
1918 assert(pos <= This->lLastFrame);
1920 /* should we read as much as block gives us? */
1921 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
1922 size = This->idxFrames[pos].dwChunkLength;
1924 /* read into out own buffer or given one? */
1925 if (buffer == NULL) {
1926 /* we also read the chunk */
1927 size += 2 * sizeof(DWORD);
1929 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
1930 if (This->lpBuffer == NULL || size < This->cbBuffer) {
1932 (LPDWORD)GlobalReAllocPtr(This->lpBuffer, max(size, This->sInfo.dwSuggestedBufferSize), GMEM_MOVEABLE);
1933 if (This->lpBuffer == NULL)
1934 return AVIERR_MEMORY;
1935 This->cbBuffer = max(size, This->sInfo.dwSuggestedBufferSize);
1938 /* now read the complete chunk into our buffer */
1939 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
1940 return AVIERR_FILEREAD;
1941 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
1942 return AVIERR_FILEREAD;
1944 /* check if it was the correct block which we have read */
1945 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
1946 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
1947 ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset);
1948 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1949 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
1950 This->idxFrames[pos].dwChunkLength);
1951 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1952 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
1953 return AVIERR_FILEREAD;
1956 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
1957 return AVIERR_FILEREAD;
1958 if (mmioRead(This->paf->hmmio, (HPSTR)buffer, size) != size)
1959 return AVIERR_FILEREAD;
1965 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
1970 /* pre-conditions */
1971 assert(This != NULL);
1972 assert(pos != NULL);
1973 assert(offset != NULL);
1974 assert(This->sInfo.dwSampleSize != 0);
1975 assert(*pos >= This->sInfo.dwStart);
1977 /* convert start sample to start bytes */
1978 (*offset) = (*pos) - This->sInfo.dwStart;
1979 (*offset) *= This->sInfo.dwSampleSize;
1981 /* convert bytes to block number */
1982 for (block = 0; block <= This->lLastFrame; block++) {
1983 if (This->idxFrames[block].dwChunkLength <= *offset)
1984 (*offset) -= This->idxFrames[block].dwChunkLength;
1992 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
1994 MainAVIHeader MainAVIHdr;
1995 IAVIStreamImpl* pStream;
2004 /* initialize some things */
2005 if (This->dwMoviChunkPos == 0)
2006 AVIFILE_ComputeMoviStart(This);
2008 AVIFILE_UpdateInfo(This);
2010 assert(This->fInfo.dwScale != 0);
2012 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2013 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2014 This->fInfo.dwScale);
2015 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2016 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2017 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2018 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2019 MainAVIHdr.dwInitialFrames = 0;
2020 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2021 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2022 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2023 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2024 for (nStream = 0; nStream < MainAVIHdr.dwStreams; nStream++) {
2025 pStream = This->ppStreams[nStream];
2027 if (MainAVIHdr.dwInitialFrames < pStream->sInfo.dwInitialFrames)
2028 MainAVIHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2031 /* now begin writing ... */
2032 mmioSeek(This->hmmio, 0, SEEK_SET);
2036 ckRIFF.fccType = formtypeAVI;
2037 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2038 return AVIERR_FILEWRITE;
2040 /* AVI headerlist */
2042 ckLIST1.fccType = listtypeAVIHEADER;
2043 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2044 return AVIERR_FILEWRITE;
2047 ck.ckid = ckidAVIMAINHDR;
2048 ck.cksize = sizeof(MainAVIHdr);
2050 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2051 return AVIERR_FILEWRITE;
2052 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2053 return AVIERR_FILEWRITE;
2054 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2055 return AVIERR_FILEWRITE;
2057 /* write the headers of each stream into a separate streamheader list */
2058 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2059 AVIStreamHeader strHdr;
2061 pStream = This->ppStreams[nStream];
2063 /* begin the new streamheader list */
2065 ckLIST2.fccType = listtypeSTREAMHEADER;
2066 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2067 return AVIERR_FILEWRITE;
2069 /* create an AVIStreamHeader from the AVSTREAMINFO */
2070 strHdr.fccType = pStream->sInfo.fccType;
2071 strHdr.fccHandler = pStream->sInfo.fccHandler;
2072 strHdr.dwFlags = pStream->sInfo.dwFlags;
2073 strHdr.wPriority = pStream->sInfo.wPriority;
2074 strHdr.wLanguage = pStream->sInfo.wLanguage;
2075 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2076 strHdr.dwScale = pStream->sInfo.dwScale;
2077 strHdr.dwRate = pStream->sInfo.dwRate;
2078 strHdr.dwStart = pStream->sInfo.dwStart;
2079 strHdr.dwLength = pStream->sInfo.dwLength;
2080 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2081 strHdr.dwQuality = pStream->sInfo.dwQuality;
2082 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2083 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2084 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2085 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2086 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2088 /* now write the AVIStreamHeader */
2089 ck.ckid = ckidSTREAMHEADER;
2090 ck.cksize = sizeof(strHdr);
2091 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2092 return AVIERR_FILEWRITE;
2093 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2094 return AVIERR_FILEWRITE;
2095 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2096 return AVIERR_FILEWRITE;
2098 /* ... the hopefully ever present streamformat ... */
2099 ck.ckid = ckidSTREAMFORMAT;
2100 ck.cksize = pStream->cbFormat;
2101 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2102 return AVIERR_FILEWRITE;
2103 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2104 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
2105 return AVIERR_FILEWRITE;
2107 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2108 return AVIERR_FILEWRITE;
2110 /* ... some optional existing handler data ... */
2111 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2112 ck.ckid = ckidSTREAMHANDLERDATA;
2113 ck.cksize = pStream->cbHandlerData;
2114 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2115 return AVIERR_FILEWRITE;
2116 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
2117 return AVIERR_FILEWRITE;
2118 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2119 return AVIERR_FILEWRITE;
2122 /* ... some optional additional extra chunk for this stream ... */
2123 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2124 /* the chunk header(s) are already in the strucuture */
2125 if (mmioWrite(This->hmmio, (HPSTR)pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2126 return AVIERR_FILEWRITE;
2129 /* ... an optional name for this stream ... */
2130 if (lstrlenW(pStream->sInfo.szName) > 0) {
2133 ck.ckid = ckidSTREAMNAME;
2134 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2135 if (ck.cksize & 1) /* align */
2137 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2138 return AVIERR_FILEWRITE;
2140 /* the streamname must be saved in ASCII not Unicode */
2141 str = (LPSTR)LocalAlloc(LPTR, ck.cksize);
2143 return AVIERR_MEMORY;
2144 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2145 ck.cksize, NULL, NULL);
2147 if (mmioWrite(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize) {
2148 LocalFree((HLOCAL)str);
2149 return AVIERR_FILEWRITE;
2152 LocalFree((HLOCAL)str);
2153 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2154 return AVIERR_FILEWRITE;
2157 /* close streamheader list for this stream */
2158 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2159 return AVIERR_FILEWRITE;
2160 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2162 /* close the aviheader list */
2163 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2164 return AVIERR_FILEWRITE;
2166 /* check for padding to pre-guessed 'movi'-chunk position */
2167 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2168 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2169 ck.ckid = ckidAVIPADDING;
2170 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2171 assert((LONG)ck.cksize >= 0);
2173 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2174 return AVIERR_FILEWRITE;
2175 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2176 return AVIERR_FILEWRITE;
2177 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2178 return AVIERR_FILEWRITE;
2181 /* now write the 'movi' chunk */
2182 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2184 ckLIST1.fccType = listtypeAVIMOVIE;
2185 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2186 return AVIERR_FILEWRITE;
2187 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2188 return AVIERR_FILEWRITE;
2189 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2190 return AVIERR_FILEWRITE;
2192 /* write 'idx1' chunk */
2193 hr = AVIFILE_SaveIndex(This);
2197 /* write optional extra file chunks */
2198 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2199 /* as for the streams, are the chunk header(s) in the structure */
2200 if (mmioWrite(This->hmmio, (HPSTR)This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2201 return AVIERR_FILEWRITE;
2204 /* close RIFF chunk */
2205 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2206 return AVIERR_FILEWRITE;
2208 /* add some JUNK at end for bad parsers */
2209 memset(&ckRIFF, 0, sizeof(ckRIFF));
2210 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2211 mmioFlush(This->hmmio, 0);
2216 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This)
2218 IAVIStreamImpl *pStream;
2224 ck.ckid = ckidAVINEWINDEX;
2226 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2227 return AVIERR_FILEWRITE;
2229 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2230 /* is interleaved -- write block of coresponding frames */
2231 LONG lInitialFrames = 0;
2235 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2238 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
2240 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2241 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2242 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2245 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2247 DWORD nFrame = lInitialFrames + i;
2249 assert(nFrame < This->nIdxRecords);
2251 idx.ckid = listtypeAVIRECORD;
2252 idx.dwFlags = AVIIF_LIST;
2253 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2254 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2255 - This->dwMoviChunkPos;
2256 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2257 return AVIERR_FILEWRITE;
2259 for (nStream = 0; nStream < This->fInfo.dwStreams; n++) {
2260 pStream = This->ppStreams[nStream];
2262 /* heave we reached start of this stream? */
2263 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2266 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2267 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2269 /* reached end of this stream? */
2270 if (pStream->lLastFrame <= nFrame)
2273 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2274 pStream->sInfo.dwFormatChangeCount != 0 &&
2275 pStream->idxFmtChanges != NULL) {
2278 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2279 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2280 idx.dwFlags = AVIIF_NOTIME;
2281 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2282 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2283 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2284 - This->dwMoviChunkPos;
2286 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2287 return AVIERR_FILEWRITE;
2291 } /* if have formatchanges */
2293 idx.ckid = pStream->idxFrames[nFrame].ckid;
2294 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2295 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2296 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2297 - This->dwMoviChunkPos;
2298 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2299 return AVIERR_FILEWRITE;
2303 /* not interleaved -- write index for each stream at once */
2304 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2305 pStream = This->ppStreams[nStream];
2307 if (pStream->lLastFrame == -1)
2308 pStream->lLastFrame = 0;
2310 for (n = 0; n < pStream->lLastFrame; n++) {
2311 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2312 (pStream->sInfo.dwFormatChangeCount != 0)) {
2315 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2316 if (pStream->idxFmtChanges[pos].ckid == n) {
2317 idx.dwFlags = AVIIF_NOTIME;
2318 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2319 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2321 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2322 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2323 return AVIERR_FILEWRITE;
2327 } /* if have formatchanges */
2329 idx.ckid = pStream->idxFrames[n].ckid;
2330 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2331 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2332 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2333 - This->dwMoviChunkPos;
2335 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2336 return AVIERR_FILEWRITE;
2339 } /* if not interleaved */
2341 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2342 return AVIERR_FILEWRITE;
2347 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2356 /* search the number of the specified stream */
2357 nStream = (ULONG)-1;
2358 for (i = 0; i < This->fInfo.dwStreams; i++) {
2359 assert(This->ppStreams[i] != NULL);
2361 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2375 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2379 /* pre-conditions */
2380 assert(This != NULL);
2382 This->fInfo.dwMaxBytesPerSec = 0;
2383 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2384 This->fInfo.dwSuggestedBufferSize = 0;
2385 This->fInfo.dwWidth = 0;
2386 This->fInfo.dwHeight = 0;
2387 This->fInfo.dwScale = 0;
2388 This->fInfo.dwRate = 0;
2389 This->fInfo.dwLength = 0;
2391 for (i = 0; i < This->fInfo.dwStreams; i++) {
2392 AVISTREAMINFOW *psi;
2395 /* pre-conditions */
2396 assert(This->ppStreams[i] != NULL);
2398 psi = &This->ppStreams[i]->sInfo;
2399 assert(psi->dwScale != 0);
2400 assert(psi->dwRate != 0);
2403 /* use first stream timings as base */
2404 This->fInfo.dwScale = psi->dwScale;
2405 This->fInfo.dwRate = psi->dwRate;
2406 This->fInfo.dwLength = psi->dwLength;
2408 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
2409 (PAVISTREAM)This->ppStreams[i],psi->dwLength);
2410 if (This->fInfo.dwLength < n)
2411 This->fInfo.dwLength = n;
2414 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2415 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2417 if (psi->dwSampleSize != 0) {
2418 /* fixed sample size -- exact computation */
2419 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2422 /* variable sample size -- only upper limit */
2423 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2424 psi->dwRate, psi->dwScale);
2426 /* update dimensions */
2427 n = psi->rcFrame.right - psi->rcFrame.left;
2428 if (This->fInfo.dwWidth < n)
2429 This->fInfo.dwWidth = n;
2430 n = psi->rcFrame.bottom - psi->rcFrame.top;
2431 if (This->fInfo.dwHeight < n)
2432 This->fInfo.dwHeight = n;
2437 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2438 FOURCC ckid, DWORD flags, LPVOID buffer,
2447 /* if no frame/block is already written, we must compute start of movi chunk */
2448 if (This->paf->dwMoviChunkPos == 0)
2449 AVIFILE_ComputeMoviStart(This->paf);
2451 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2452 return AVIERR_FILEWRITE;
2454 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2455 return AVIERR_FILEWRITE;
2456 if (buffer != NULL && size > 0) {
2457 if (mmioWrite(This->paf->hmmio, (HPSTR)buffer, size) != size)
2458 return AVIERR_FILEWRITE;
2460 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2461 return AVIERR_FILEWRITE;
2463 This->paf->fDirty = TRUE;
2464 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2466 return AVIFILE_AddFrame(This, ckid, size, ck.dwDataOffset, flags);