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.
39 #include "avifile_private.h"
40 #include "extrachunk.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
47 #define IDX_PER_BLOCK 2730
50 /***********************************************************************/
52 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
53 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
54 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
55 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
56 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
57 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
58 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
59 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
60 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
61 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
63 struct ICOM_VTABLE(IAVIFile) iavift = {
64 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
65 IAVIFile_fnQueryInterface,
70 IAVIFile_fnCreateStream,
74 IAVIFile_fnDeleteStream
77 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
78 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
79 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface);
80 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
81 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
82 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
83 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
84 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
85 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
87 struct ICOM_VTABLE(IPersistFile) ipersistft = {
88 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
89 IPersistFile_fnQueryInterface,
90 IPersistFile_fnAddRef,
91 IPersistFile_fnRelease,
92 IPersistFile_fnGetClassID,
93 IPersistFile_fnIsDirty,
96 IPersistFile_fnSaveCompleted,
97 IPersistFile_fnGetCurFile
100 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
101 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
102 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface);
103 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
104 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
105 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
106 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
107 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
108 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
109 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
110 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
111 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
112 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
113 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
115 struct ICOM_VTABLE(IAVIStream) iavist = {
116 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
117 IAVIStream_fnQueryInterface,
119 IAVIStream_fnRelease,
122 IAVIStream_fnFindSample,
123 IAVIStream_fnReadFormat,
124 IAVIStream_fnSetFormat,
128 IAVIStream_fnReadData,
129 IAVIStream_fnWriteData,
133 typedef struct _IAVIFileImpl IAVIFileImpl;
135 typedef struct _IPersistFileImpl {
137 ICOM_VFIELD(IPersistFile);
139 /* IPersistFile stuff */
143 typedef struct _IAVIStreamImpl {
145 ICOM_VFIELD(IAVIStream);
148 /* IAVIStream stuff */
150 DWORD nStream; /* the n-th stream in file */
151 AVISTREAMINFOW sInfo;
156 LPVOID lpHandlerData;
162 DWORD cbBuffer; /* size of lpBuffer */
163 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
165 DWORD dwLastFrame; /* last correct index in idxFrames */
166 AVIINDEXENTRY *idxFrames;
167 DWORD nIdxFrames; /* upper index limit of idxFrames */
168 AVIINDEXENTRY *idxFmtChanges;
169 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
172 struct _IAVIFileImpl {
174 ICOM_VFIELD(IAVIFile);
177 /* IAVIFile stuff... */
178 IPersistFileImpl iPersistFile;
181 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
183 EXTRACHUNKS fileextra;
185 DWORD dwMoviChunkPos; /* some stuff for saving ... */
187 DWORD dwNextFramePos;
189 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
192 /* IPersistFile stuff ... */
199 /***********************************************************************/
201 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
202 DWORD offset, DWORD flags);
203 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
204 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
205 LPAVISTREAMINFOW asi);
206 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
207 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
208 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset);
209 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
210 LONG count, DWORD pos, BOOL *bAbsolute);
211 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
212 LPVOID buffer, LONG size);
213 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
215 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
216 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This);
217 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fccType,
219 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
220 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
221 FOURCC ckid, DWORD flags, LPVOID buffer,
224 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
229 assert(riid != NULL && ppv != NULL);
233 pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl));
235 return AVIERR_MEMORY;
237 ICOM_VTBL(pfile) = &iavift;
238 ICOM_VTBL(&pfile->iPersistFile) = &ipersistft;
240 pfile->iPersistFile.paf = pfile;
242 hr = IUnknown_QueryInterface((IUnknown*)pfile, riid, ppv);
244 LocalFree((HLOCAL)pfile);
249 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
252 ICOM_THIS(IAVIFileImpl,iface);
254 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
256 if (IsEqualGUID(&IID_IUnknown, refiid) ||
257 IsEqualGUID(&IID_IAVIFile, refiid)) {
259 IAVIFile_AddRef(iface);
262 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
263 *obj = &This->iPersistFile;
264 IAVIFile_AddRef(iface);
269 return OLE_E_ENUM_NOMORE;
272 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
274 ICOM_THIS(IAVIFileImpl,iface);
276 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
277 return ++(This->ref);
280 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
282 ICOM_THIS(IAVIFileImpl,iface);
285 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
287 if (!--(This->ref)) {
289 /* need to write headers to file */
290 AVIFILE_SaveFile(This);
293 for (i = 0; i < This->fInfo.dwStreams; i++) {
294 if (This->ppStreams[i] != NULL) {
295 if (This->ppStreams[i]->ref != 0) {
296 ERR(": someone has still a reference to stream %u (%p)!\n",
297 i, This->ppStreams[i]);
299 AVIFILE_DestructAVIStream(This->ppStreams[i]);
300 LocalFree((HLOCAL)This->ppStreams[i]);
301 This->ppStreams[i] = NULL;
305 if (This->idxRecords != NULL) {
306 GlobalFreePtr(This->idxRecords);
307 This->idxRecords = NULL;
308 This->nIdxRecords = 0;
311 if (This->fileextra.lp != NULL) {
312 GlobalFreePtr(This->fileextra.lp);
313 This->fileextra.lp = NULL;
314 This->fileextra.cb = 0;
317 if (This->szFileName != NULL) {
318 LocalFree((HLOCAL)This->szFileName);
319 This->szFileName = NULL;
321 if (This->hmmio != (HMMIO)NULL) {
322 mmioClose(This->hmmio, 0);
323 This->hmmio = (HMMIO)NULL;
326 LocalFree((HLOCAL)This);
332 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
335 ICOM_THIS(IAVIFileImpl,iface);
337 TRACE("(%p,%p,%ld)\n",iface,afi,size);
340 return AVIERR_BADPARAM;
342 return AVIERR_BADSIZE;
344 AVIFILE_UpdateInfo(This);
346 memcpy(afi, &This->fInfo, min(size, sizeof(This->fInfo)));
348 if (size < sizeof(This->fInfo))
349 return AVIERR_BUFFERTOOSMALL;
353 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
354 DWORD fccType, LONG lParam)
356 ICOM_THIS(IAVIFileImpl,iface);
360 TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam);
362 if (avis == NULL || lParam < 0)
363 return AVIERR_BADPARAM;
365 nStream = AVIFILE_SearchStream(This, fccType, lParam);
367 /* Does the requested stream exist? */
368 if (nStream < This->fInfo.dwStreams &&
369 This->ppStreams[nStream] != NULL) {
370 *avis = (PAVISTREAM)This->ppStreams[nStream];
371 IAVIStream_AddRef(*avis);
376 /* Sorry, but the specified stream doesn't exist */
377 return AVIERR_NODATA;
380 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
381 LPAVISTREAMINFOW asi)
383 ICOM_THIS(IAVIFileImpl,iface);
387 TRACE("(%p,%p,%p)\n", iface, avis, asi);
389 /* check parameters */
390 if (avis == NULL || asi == NULL)
391 return AVIERR_BADPARAM;
393 /* Does the user have write permission? */
394 if ((This->uMode & MMIO_RWMODE) == 0)
395 return AVIERR_READONLY;
397 /* Can we add another stream? */
398 n = This->fInfo.dwStreams;
399 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
400 /* already reached max nr of streams
401 * or have already written frames to disk */
402 return AVIERR_UNSUPPORTED;
405 /* check AVISTREAMINFO for some really needed things */
406 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
407 return AVIERR_BADFORMAT;
409 /* now it seems to be save to add the stream */
410 assert(This->ppStreams[n] == NULL);
411 This->ppStreams[n] = (IAVIStreamImpl*)LocalAlloc(LPTR,
412 sizeof(IAVIStreamImpl));
413 if (This->ppStreams[n] == NULL)
414 return AVIERR_MEMORY;
416 /* initialize the new allocated stream */
417 AVIFILE_ConstructAVIStream(This, n, asi);
419 This->fInfo.dwStreams++;
422 /* update our AVIFILEINFO structure */
423 AVIFILE_UpdateInfo(This);
428 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
429 LPVOID lpData, LONG size)
431 ICOM_THIS(IAVIFileImpl,iface);
433 TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
435 /* check parameters */
437 return AVIERR_BADPARAM;
439 return AVIERR_BADSIZE;
441 /* Do we have write permission? */
442 if ((This->uMode & MMIO_RWMODE) == 0)
443 return AVIERR_READONLY;
447 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
450 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
451 LPVOID lpData, LONG *size)
453 ICOM_THIS(IAVIFileImpl,iface);
455 TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
457 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
460 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
462 ICOM_THIS(IAVIFileImpl,iface);
464 FIXME("(%p): stub\n",iface);
466 if ((This->uMode & MMIO_RWMODE) == 0)
467 return AVIERR_READONLY;
471 /* FIXME: end record -- for interleaved files */
476 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
479 ICOM_THIS(IAVIFileImpl,iface);
483 TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
485 /* check parameter */
487 return AVIERR_BADPARAM;
489 /* Habe user write permissions? */
490 if ((This->uMode & MMIO_RWMODE) == 0)
491 return AVIERR_READONLY;
493 nStream = AVIFILE_SearchStream(This, fccType, lParam);
495 /* Does the requested stream exist? */
496 if (nStream < This->fInfo.dwStreams &&
497 This->ppStreams[nStream] != NULL) {
498 /* ... so delete it now */
499 LocalFree((HLOCAL)This->ppStreams[nStream]);
501 if (This->fInfo.dwStreams - nStream > 0)
502 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
503 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
505 This->ppStreams[This->fInfo.dwStreams] = NULL;
506 This->fInfo.dwStreams--;
509 /* This->fInfo will be updated further when asked for */
512 return AVIERR_NODATA;
515 /***********************************************************************/
517 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
518 REFIID refiid, LPVOID *obj)
520 ICOM_THIS(IPersistFileImpl,iface);
522 assert(This->paf != NULL);
524 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
527 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
529 ICOM_THIS(IPersistFileImpl,iface);
531 assert(This->paf != NULL);
533 return IAVIFile_AddRef((PAVIFILE)This->paf);
536 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
538 ICOM_THIS(IPersistFileImpl,iface);
540 assert(This->paf != NULL);
542 return IAVIFile_Release((PAVIFILE)This->paf);
545 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
548 TRACE("(%p,%p)\n", iface, pClassID);
550 if (pClassID == NULL)
551 return AVIERR_BADPARAM;
553 memcpy(pClassID, &CLSID_AVIFile, sizeof(CLSID_AVIFile));
558 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
560 ICOM_THIS(IPersistFileImpl,iface);
562 TRACE("(%p)\n", iface);
564 assert(This->paf != NULL);
566 return (This->paf->fDirty ? S_OK : S_FALSE);
569 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
570 LPCOLESTR pszFileName, DWORD dwMode)
572 ICOM_THIS(IPersistFileImpl,iface);
576 TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
578 /* check parameter */
579 if (pszFileName == NULL)
580 return AVIERR_BADPARAM;
582 assert(This->paf != NULL);
583 if (This->paf->hmmio != (HMMIO)NULL)
584 return AVIERR_ERROR; /* No reuse of this object for another file! */
586 /* remeber mode and name */
587 This->paf->uMode = dwMode;
589 len = lstrlenW(pszFileName) + 1;
590 This->paf->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR));
591 if (This->paf->szFileName == NULL)
592 return AVIERR_MEMORY;
593 lstrcpyW(This->paf->szFileName, pszFileName);
595 /* try to open the file */
596 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
597 MMIO_ALLOCBUF | dwMode);
598 if (This->paf->hmmio == (HMMIO)NULL)
599 return AVIERR_FILEOPEN;
601 /* should we create a new file? */
602 if (dwMode & OF_CREATE) {
603 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
604 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
608 return AVIFILE_LoadFile(This->paf);
611 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
612 LPCOLESTR pszFileName,BOOL fRemember)
614 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
616 /* We write directly to disk, so nothing to do. */
621 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
622 LPCOLESTR pszFileName)
624 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
626 /* We write directly to disk, so nothing to do. */
631 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
632 LPOLESTR *ppszFileName)
634 ICOM_THIS(IPersistFileImpl,iface);
636 TRACE("(%p,%p)\n", iface, ppszFileName);
638 if (ppszFileName == NULL)
639 return AVIERR_BADPARAM;
641 *ppszFileName = NULL;
643 assert(This->paf != NULL);
645 if (This->paf->szFileName != NULL) {
646 int len = lstrlenW(This->paf->szFileName);
648 *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR));
649 if (*ppszFileName == NULL)
650 return AVIERR_MEMORY;
652 memcpy(*ppszFileName, This->paf->szFileName, len * sizeof(WCHAR));
658 /***********************************************************************/
660 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
661 REFIID refiid, LPVOID *obj)
663 ICOM_THIS(IAVIStreamImpl,iface);
665 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
667 if (IsEqualGUID(&IID_IUnknown, refiid) ||
668 IsEqualGUID(&IID_IAVIStream, refiid)) {
670 IAVIStream_AddRef(iface);
674 /* FIXME: IAVIStreaming interface */
676 return OLE_E_ENUM_NOMORE;
679 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
681 ICOM_THIS(IAVIStreamImpl,iface);
683 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
685 /* also add ref to parent, so that it doesn't kill us */
686 if (This->paf != NULL)
687 IAVIFile_AddRef((PAVIFILE)This->paf);
689 return ++(This->ref);
692 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
694 ICOM_THIS(IAVIStreamImpl,iface);
696 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
698 /* we belong to the AVIFile, which must free us! */
699 if (This->ref == 0) {
700 ERR(": already released!\n");
704 if (This->paf != NULL)
705 IAVIFile_Release((PAVIFILE)This->paf);
710 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
713 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
715 /* This IAVIStream interface needs an AVIFile */
716 return AVIERR_UNSUPPORTED;
719 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
722 ICOM_THIS(IAVIStreamImpl,iface);
724 TRACE("(%p,%p,%ld)\n", iface, psi, size);
727 return AVIERR_BADPARAM;
729 return AVIERR_BADSIZE;
731 memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo)));
733 if (size < sizeof(This->sInfo))
734 return AVIERR_BUFFERTOOSMALL;
738 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
741 ICOM_THIS(IAVIStreamImpl,iface);
745 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
747 if (flags & FIND_FROM_START) {
748 pos = This->sInfo.dwStart;
749 flags &= ~(FIND_FROM_START|FIND_PREV);
753 if (This->sInfo.dwSampleSize != 0) {
754 /* convert samples into block number with offset */
755 AVIFILE_SamplesToBlock(This, &pos, &offset);
758 if (flags & FIND_TYPE) {
759 if (flags & FIND_KEY) {
760 while (0 <= pos && pos <= This->dwLastFrame) {
761 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
764 if (flags & FIND_NEXT)
769 } else if (flags & FIND_ANY) {
770 while (0 <= pos && pos <= This->dwLastFrame) {
771 if (This->idxFrames[pos].dwChunkLength > 0)
774 if (flags & FIND_NEXT)
780 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
781 This->sInfo.fccType == streamtypeVIDEO) {
784 if (flags & FIND_NEXT) {
785 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
786 if (This->idxFmtChanges[n].ckid >= pos)
789 for (n = This->sInfo.dwFormatChangeCount; n >= 0; n--) {
790 if (This->idxFmtChanges[n].ckid <= pos)
794 if (pos > This->sInfo.dwStart)
795 return 0; /* format changes always for first frame */
802 if (flags & FIND_RET) {
804 if (flags & FIND_LENGTH) {
806 if (This->sInfo.dwSampleSize)
807 pos = This->sInfo.dwSampleSize;
810 } else if (flags & FIND_OFFSET) {
811 /* physical position */
812 pos = This->idxFrames[pos].dwChunkOffset + offset * This->sInfo.dwSampleSize;
813 } else if (flags & FIND_SIZE) {
815 pos = This->idxFrames[pos].dwChunkLength;
816 } else if (flags & FIND_INDEX) {
817 FIXME(": FIND_INDEX flag is not supported!");
819 pos = This->paf->dwIdxChunkPos;
820 } /* else logical position */
828 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
829 LPVOID format, LONG *formatsize)
831 ICOM_THIS(IAVIStreamImpl,iface);
833 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
835 if (formatsize == NULL)
836 return AVIERR_BADPARAM;
838 /* only interested in needed buffersize? */
839 if (format == NULL || *formatsize <= 0) {
840 *formatsize = This->cbFormat;
845 /* copy initial format (only as much as will fit) */
846 memcpy(format, This->lpFormat, min(*formatsize, This->cbFormat));
847 if (*formatsize < This->cbFormat) {
848 *formatsize = This->cbFormat;
849 return AVIERR_BUFFERTOOSMALL;
852 /* Could format change? When yes will it change? */
853 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
854 pos > This->sInfo.dwStart) {
857 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
859 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt);
863 *formatsize = This->cbFormat;
867 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
868 LPVOID format, LONG formatsize)
870 ICOM_THIS(IAVIStreamImpl,iface);
872 LPBITMAPINFOHEADER lpbiNew = (LPBITMAPINFOHEADER)format;
874 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
876 /* check parameters */
877 if (format == NULL || formatsize <= 0)
878 return AVIERR_BADPARAM;
880 /* Do we have write permission? */
881 if ((This->paf->uMode & MMIO_RWMODE) == 0)
882 return AVIERR_READONLY;
884 /* can only set format before frame is written! */
885 if (This->dwLastFrame > pos)
886 return AVIERR_UNSUPPORTED;
888 /* initial format or a formatchange? */
889 if (This->lpFormat != NULL) {
891 if (This->paf->dwMoviChunkPos != 0)
892 return AVIERR_ERROR; /* user has used API in wrong sequnece! */
894 This->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, formatsize);
895 if (This->lpFormat == NULL)
896 return AVIERR_MEMORY;
897 This->cbFormat = formatsize;
899 memcpy(This->lpFormat, format, formatsize);
901 /* update some infos about stream */
902 if (This->sInfo.fccType == streamtypeVIDEO) {
905 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
906 if (lDim < lpbiNew->biWidth)
907 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
908 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
909 if (lDim < lpbiNew->biHeight)
910 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
911 } else if (This->sInfo.fccType == streamtypeAUDIO)
912 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
917 LPBITMAPINFOHEADER lpbiOld = (LPBITMAPINFOHEADER)This->lpFormat;
918 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
919 AVIPALCHANGE *lppc = NULL;
922 /* pherhaps formatchange, check it ... */
923 if (This->cbFormat != formatsize)
924 return AVIERR_UNSUPPORTED;
926 /* no formatchange, only the initial one */
927 if (memcmp(This->lpFormat, format, formatsize) == 0)
930 /* check that's only the palette, which changes */
931 if (lpbiOld->biSize != lpbiNew->biSize ||
932 lpbiOld->biWidth != lpbiNew->biWidth ||
933 lpbiOld->biHeight != lpbiNew->biHeight ||
934 lpbiOld->biPlanes != lpbiNew->biPlanes ||
935 lpbiOld->biBitCount != lpbiNew->biBitCount ||
936 lpbiOld->biCompression != lpbiNew->biCompression ||
937 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
938 return AVIERR_UNSUPPORTED;
940 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
942 /* simply say all colors have changed */
943 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
944 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
945 lppc = (AVIPALCHANGE*)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
947 return AVIERR_MEMORY;
949 lppc->bFirstEntry = 0;
950 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
952 for (n = 0; n < lpbiOld->biClrUsed; n++) {
953 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
954 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
955 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
956 lppc->peNew[n].peFlags = 0;
959 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
960 return AVIERR_FILEWRITE;
961 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
962 return AVIERR_FILEWRITE;
963 if (mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize)
964 return AVIERR_FILEWRITE;
965 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
966 return AVIERR_FILEWRITE;
967 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
971 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
975 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
976 LONG samples, LPVOID buffer,
977 LONG buffersize, LPLONG bytesread,
980 ICOM_THIS(IAVIStreamImpl,iface);
985 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
986 buffersize, bytesread, samplesread);
988 /* clear return parameters if given */
989 if (bytesread != NULL)
991 if (samplesread != NULL)
994 /* check parameters */
995 if (This->sInfo.dwStart > start)
996 return AVIERR_NODATA; /* couldn't read before start of stream */
997 if (This->sInfo.dwStart + This->sInfo.dwLength < start)
998 return AVIERR_NODATA; /* start is past end of stream */
1000 /* should we read as much as possible? */
1001 if (samples == -1) {
1002 /* User should know how much we have read */
1003 if (bytesread == NULL && samplesread == NULL)
1004 return AVIERR_BADPARAM;
1006 if (This->sInfo.dwSampleSize != 0)
1007 samples = buffersize / This->sInfo.dwSampleSize;
1012 /* limit to end of stream */
1013 if (This->sInfo.dwLength < samples)
1014 samples = This->sInfo.dwLength;
1015 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1016 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1018 /* nothing to read? Then leave ... */
1022 if (This->sInfo.dwSampleSize != 0) {
1023 /* fixed samplesize -- we can read over frame/block boundaries */
1027 /* convert start sample to block,offset pair */
1028 AVIFILE_SamplesToBlock(This, &block, &offset);
1030 /* convert samples to bytes */
1031 samples *= This->sInfo.dwSampleSize;
1033 while (samples > 0 && buffersize > 0) {
1034 if (block != This->dwCurrentFrame) {
1035 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1040 size = min((DWORD)samples, (DWORD)buffersize);
1041 size = min(size, This->cbBuffer - offset);
1042 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1046 ((BYTE*)buffer) += size;
1050 /* fill out return parameters if given */
1051 if (bytesread != NULL)
1053 if (samplesread != NULL)
1054 *samplesread += size / This->sInfo.dwSampleSize;
1060 return AVIERR_BUFFERTOOSMALL;
1062 /* variable samplesize -- we can only read one full frame/block */
1066 assert(start <= This->dwLastFrame);
1067 size = This->idxFrames[start].dwChunkLength;
1068 if (buffer != NULL && buffersize >= size) {
1069 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1072 } else if (buffer != NULL)
1073 return AVIERR_BUFFERTOOSMALL;
1075 /* fill out return parameters if given */
1076 if (bytesread != NULL)
1078 if (samplesread != NULL)
1079 *samplesread = samples;
1085 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
1086 LONG samples, LPVOID buffer,
1087 LONG buffersize, DWORD flags,
1089 LPLONG byteswritten)
1091 ICOM_THIS(IAVIStreamImpl,iface);
1096 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
1097 buffer, buffersize, flags, sampwritten, byteswritten);
1099 /* clear return parameters if given */
1100 if (sampwritten != NULL)
1102 if (byteswritten != NULL)
1105 /* check parameters */
1106 if (buffer == NULL && (buffersize > 0 || samples > 0))
1107 return AVIERR_BADPARAM;
1109 /* Have we write permission? */
1110 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1111 return AVIERR_READONLY;
1113 switch (This->sInfo.fccType) {
1114 case streamtypeAUDIO:
1115 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1118 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1119 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1121 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1125 /* append to end of stream? */
1127 if (This->dwLastFrame == -1)
1128 start = This->sInfo.dwStart;
1130 start = This->sInfo.dwLength;
1133 if (This->sInfo.dwSampleSize != 0) {
1134 /* fixed sample size -- audio like */
1135 if (samples * This->sInfo.dwSampleSize != buffersize)
1136 return AVIERR_BADPARAM;
1138 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1139 if (This->sInfo.dwLength != start)
1140 return AVIERR_UNSUPPORTED;
1142 /* Convert position to frame/block */
1143 start = This->dwLastFrame + 1;
1145 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1146 FIXME(": not interleaved, could collect audio data!\n");
1149 /* variable sample size -- video like */
1151 return AVIERR_UNSUPPORTED;
1153 /* must we fill up with empty frames? */
1154 if (This->dwLastFrame != -1) {
1155 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1157 while (start > This->dwLastFrame + 1) {
1158 hr = AVIFILE_WriteBlock(This, This->dwLastFrame + 1, ckid2, 0, NULL, 0);
1165 /* write the block now */
1166 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1167 if (SUCCEEDED(hr)) {
1168 /* fill out return parameters if given */
1169 if (sampwritten != NULL)
1170 *sampwritten = samples;
1171 if (byteswritten != NULL)
1172 *byteswritten = buffersize;
1178 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
1181 ICOM_THIS(IAVIStreamImpl,iface);
1183 FIXME("(%p,%ld,%ld): stub\n", iface, start, samples);
1185 /* check parameters */
1186 if (start < 0 || samples < 0)
1187 return AVIERR_BADPARAM;
1189 /* Delete before start of stream? */
1190 if (start + samples < This->sInfo.dwStart)
1193 /* Delete after end of stream? */
1194 if (start > This->sInfo.dwLength)
1197 /* For the rest we need write permissions */
1198 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1199 return AVIERR_READONLY;
1201 /* 1. overwrite the data with JUNK
1203 * if ISINTERLEAVED {
1204 * 2. concat all neighboured JUNK-blocks in this record to one
1205 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1206 * to start of this record, repeat this.
1208 * 2. concat all neighboured JUNK-blocks.
1209 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1210 * start of this block.
1214 return AVIERR_UNSUPPORTED;
1217 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
1218 LPVOID lp, LPLONG lpread)
1220 ICOM_THIS(IAVIStreamImpl,iface);
1222 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
1224 if (fcc == ckidSTREAMHANDLERDATA) {
1225 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1226 if (lp == NULL || *lpread <= 0) {
1227 *lpread = This->cbHandlerData;
1231 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1232 if (*lpread < This->cbHandlerData)
1233 return AVIERR_BUFFERTOOSMALL;
1236 return AVIERR_NODATA;
1238 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1241 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
1242 LPVOID lp, LONG size)
1244 ICOM_THIS(IAVIStreamImpl,iface);
1246 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
1248 /* check parameters */
1250 return AVIERR_BADPARAM;
1252 return AVIERR_BADSIZE;
1254 /* need write permission */
1255 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1256 return AVIERR_READONLY;
1258 /* already written something to this file? */
1259 if (This->paf->dwMoviChunkPos != 0) {
1260 /* the data will be inserted before the 'movi' chunk, so check for
1262 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1264 /* ckid,size => 2 * sizeof(DWORD) */
1265 dwPos += 2 * sizeof(DWORD) + size;
1266 if (size >= This->paf->dwMoviChunkPos)
1267 return AVIERR_UNSUPPORTED; /* not enough space left */
1270 This->paf->fDirty = TRUE;
1272 if (fcc == ckidSTREAMHANDLERDATA) {
1273 if (This->lpHandlerData != NULL) {
1274 FIXME(": handler data already set -- overwirte?\n");
1275 return AVIERR_UNSUPPORTED;
1278 This->lpHandlerData = GlobalAllocPtr(GMEM_MOVEABLE, size);
1279 if (This->lpHandlerData == NULL)
1280 return AVIERR_MEMORY;
1281 This->cbHandlerData = size;
1282 memcpy(This->lpHandlerData, lp, size);
1286 return WriteExtraChunk(&This->extra, fcc, lp, size);
1289 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
1290 LPAVISTREAMINFOW info, LONG infolen)
1292 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
1297 /***********************************************************************/
1299 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1301 switch (TWOCCFromFOURCC(ckid)) {
1303 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1304 flags |= AVIIF_KEYFRAME;
1306 case cktypeDIBcompressed:
1307 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1308 flags &= ~AVIIF_KEYFRAME;
1310 case cktypePALchange:
1311 if (This->sInfo.fccType != streamtypeVIDEO) {
1312 ERR(": found palette change in non-video stream!\n");
1313 return AVIERR_BADFORMAT;
1315 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1316 This->sInfo.dwFormatChangeCount++;
1318 if (This->idxFmtChanges == NULL || This->sInfo.dwFormatChangeCount < This->nIdxFmtChanges) {
1319 UINT n = This->sInfo.dwFormatChangeCount;
1321 This->nIdxFmtChanges += 16;
1322 This->idxFmtChanges = GlobalReAllocPtr(This->idxFmtChanges, This->nIdxFmtChanges * sizeof(AVIINDEXENTRY), GHND);
1323 if (This->idxFmtChanges == NULL)
1324 return AVIERR_MEMORY;
1326 This->idxFmtChanges[n].ckid = This->dwLastFrame;
1327 This->idxFmtChanges[n].dwFlags = 0;
1328 This->idxFmtChanges[n].dwChunkOffset = offset;
1329 This->idxFmtChanges[n].dwChunkLength = size;
1334 case cktypeWAVEbytes:
1335 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1336 flags |= AVIIF_KEYFRAME;
1339 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1343 /* first frame is alwasy a keyframe */
1344 if (This->dwLastFrame == -1)
1345 flags |= AVIIF_KEYFRAME;
1347 if (This->sInfo.dwSuggestedBufferSize < size)
1348 This->sInfo.dwSuggestedBufferSize = size;
1350 /* get memory for index */
1351 if (This->idxFrames == NULL || This->dwLastFrame + 1 >= This->nIdxFrames) {
1352 This->nIdxFrames += 512;
1353 This->idxFrames = GlobalReAllocPtr(This->idxFrames, This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND);
1354 if (This->idxFrames == NULL)
1355 return AVIERR_MEMORY;
1358 This->dwLastFrame++;
1359 This->idxFrames[This->dwLastFrame].ckid = ckid;
1360 This->idxFrames[This->dwLastFrame].dwFlags = flags;
1361 This->idxFrames[This->dwLastFrame].dwChunkOffset = offset;
1362 This->idxFrames[This->dwLastFrame].dwChunkLength = size;
1367 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1372 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1373 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1375 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1376 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1378 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1379 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1380 dwPos += ((pStream->cbFormat + 1) & ~1);
1381 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1382 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1);
1383 if (lstrlenW(pStream->sInfo.szName) > 0)
1384 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1);
1387 if (This->dwMoviChunkPos == 0) {
1388 This->dwNextFramePos = dwPos;
1390 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1391 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1392 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1394 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1400 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, LPAVISTREAMINFOW asi)
1402 IAVIStreamImpl *pstream;
1404 /* pre-conditions */
1405 assert(paf != NULL);
1406 assert(nr < MAX_AVISTREAMS);
1407 assert(paf->ppStreams[nr] != NULL);
1409 pstream = paf->ppStreams[nr];
1411 ICOM_VTBL(pstream) = &iavist;
1414 pstream->nStream = nr;
1415 pstream->dwCurrentFrame = -1;
1416 pstream->dwLastFrame = -1;
1419 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1421 if (asi->dwLength > 0) {
1422 /* pre-allocate mem for frame-index structure */
1423 pstream->idxFrames =
1424 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwLength * sizeof(AVIINDEXENTRY));
1425 if (pstream->idxFrames != NULL)
1426 pstream->nIdxFrames = asi->dwLength;
1428 if (asi->dwFormatChangeCount > 0) {
1429 /* pre-allocate mem for formatchange-index structure */
1430 pstream->idxFmtChanges =
1431 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1432 if (pstream->idxFmtChanges != NULL)
1433 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1436 /* These values will be computed */
1437 pstream->sInfo.dwLength = 0;
1438 pstream->sInfo.dwSuggestedBufferSize = 0;
1439 pstream->sInfo.dwFormatChangeCount = 0;
1440 pstream->sInfo.dwEditCount = 1;
1441 if (pstream->sInfo.dwSampleSize > 0)
1442 SetRectEmpty(&pstream->sInfo.rcFrame);
1445 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1448 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1450 /* pre-conditions */
1451 assert(This != NULL);
1453 This->dwCurrentFrame = -1;
1454 This->dwLastFrame = -1;
1456 if (This->idxFrames != NULL) {
1457 GlobalFreePtr(This->idxFrames);
1458 This->idxFrames = NULL;
1459 This->nIdxFrames = 0;
1461 if (This->idxFmtChanges != NULL) {
1462 GlobalFreePtr(This->idxFmtChanges);
1463 This->idxFmtChanges = NULL;
1465 if (This->lpBuffer != NULL) {
1466 GlobalFreePtr(This->lpBuffer);
1467 This->lpBuffer = NULL;
1470 if (This->lpHandlerData != NULL) {
1471 GlobalFreePtr(This->lpHandlerData);
1472 This->lpHandlerData = NULL;
1473 This->cbHandlerData = 0;
1475 if (This->extra.lp != NULL) {
1476 GlobalFreePtr(This->extra.lp);
1477 This->extra.lp = NULL;
1480 if (This->lpFormat != NULL) {
1481 GlobalFreePtr(This->lpFormat);
1482 This->lpFormat = NULL;
1487 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1489 MainAVIHeader MainAVIHdr;
1494 IAVIStreamImpl *pStream;
1498 if (This->hmmio == (HMMIO)NULL)
1499 return AVIERR_FILEOPEN;
1501 /* initialize stream ptr's */
1502 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1504 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1505 ckRIFF.fccType = formtypeAVI;
1506 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1507 ERR(": not an AVI!\n");
1508 return AVIERR_FILEREAD;
1511 /* get "LIST" "hdrl" */
1512 ckLIST1.fccType = listtypeAVIHEADER;
1513 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1517 /* get "avih" chunk */
1518 ck.ckid = ckidAVIMAINHDR;
1519 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1523 if (ck.cksize != sizeof(MainAVIHdr)) {
1524 ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize);
1525 return AVIERR_BADFORMAT;
1527 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1528 return AVIERR_FILEREAD;
1530 /* adjust permissions if copyrighted material in file */
1531 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1532 This->uMode &= ~MMIO_RWMODE;
1533 This->uMode |= MMIO_READ;
1536 /* convert MainAVIHeader into AVIFILINFOW */
1537 memset(&This->fInfo, 0, sizeof(This->fInfo));
1538 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1539 This->fInfo.dwScale = 1000000;
1540 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1541 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1542 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1543 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1544 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1545 This->fInfo.dwSuggestedBufferSize = MainAVIHdr.dwSuggestedBufferSize;
1546 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1547 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1548 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1549 sizeof(This->fInfo.szFileType));
1551 /* go back to into header list */
1552 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1553 return AVIERR_FILEREAD;
1555 /* foreach stream exists a "LIST","strl" chunk */
1556 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1557 /* get next nested chunk in this "LIST","strl" */
1558 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1559 return AVIERR_FILEREAD;
1561 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1562 if (ckLIST2.ckid == FOURCC_LIST &&
1563 ckLIST2.fccType == listtypeSTREAMHEADER) {
1564 pStream = This->ppStreams[nStream] =
1565 (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
1566 if (pStream == NULL)
1567 return AVIERR_MEMORY;
1568 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1571 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1573 case ckidSTREAMHANDLERDATA:
1574 if (pStream->lpHandlerData != NULL)
1575 return AVIERR_BADFORMAT;
1576 pStream->lpHandlerData = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1578 if (pStream->lpHandlerData == NULL)
1579 return AVIERR_MEMORY;
1580 pStream->cbHandlerData = ck.cksize;
1582 if (mmioRead(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
1583 return AVIERR_FILEREAD;
1585 case ckidSTREAMFORMAT:
1586 if (pStream->lpFormat != NULL)
1587 return AVIERR_BADFORMAT;
1588 pStream->lpFormat = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1590 if (pStream->lpFormat == NULL)
1591 return AVIERR_MEMORY;
1592 pStream->cbFormat = ck.cksize;
1594 if (mmioRead(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
1595 return AVIERR_FILEREAD;
1597 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1598 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pStream->lpFormat;
1600 /* some corrections to the video format */
1601 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1602 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1603 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1604 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1605 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1606 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1607 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1608 lpbi->biCompression = BI_RLE8;
1610 if (lpbi->biCompression == BI_RGB &&
1611 (pStream->sInfo.fccHandler == 0 ||
1612 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1613 pStream->sInfo.fccHandler = comptypeDIB;
1615 /* init rcFrame if it's empty */
1616 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1617 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1620 case ckidSTREAMHEADER:
1622 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1624 AVIStreamHeader streamHdr;
1626 WCHAR streamNameFmt[25];
1630 if (ck.cksize > sizeof(streamHdr))
1631 n = sizeof(streamHdr);
1633 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1634 return AVIERR_FILEREAD;
1636 pStream->sInfo.fccType = streamHdr.fccType;
1637 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1638 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1639 pStream->sInfo.wPriority = streamHdr.wPriority;
1640 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1641 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1642 pStream->sInfo.dwScale = streamHdr.dwScale;
1643 pStream->sInfo.dwRate = streamHdr.dwRate;
1644 pStream->sInfo.dwStart = streamHdr.dwStart;
1645 pStream->sInfo.dwLength = streamHdr.dwLength;
1646 pStream->sInfo.dwSuggestedBufferSize =
1647 streamHdr.dwSuggestedBufferSize;
1648 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1649 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1650 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1651 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1652 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1653 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1654 pStream->sInfo.dwEditCount = 0;
1655 pStream->sInfo.dwFormatChangeCount = 0;
1657 /* generate description for stream like "filename.avi Type #n" */
1658 if (streamHdr.fccType == streamtypeVIDEO)
1659 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType));
1660 else if (streamHdr.fccType == streamtypeAUDIO)
1661 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType));
1663 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1665 /* get count of this streamtype up to this stream */
1667 for (n = nStream; 0 <= n; n--) {
1668 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1672 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1674 LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt));
1676 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1677 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1678 AVIFILE_BasenameW(This->szFileName), szType, count);
1681 case ckidSTREAMNAME:
1682 { /* streamname will be saved as ASCII string */
1683 LPSTR str = (LPSTR)LocalAlloc(LMEM_FIXED, ck.cksize);
1685 return AVIERR_MEMORY;
1687 if (mmioRead(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize)
1688 return AVIERR_FILEREAD;
1690 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1691 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1693 LocalFree((HLOCAL)str);
1696 case ckidAVIPADDING:
1697 case mmioFOURCC('p','a','d','d'):
1700 WARN(": found extra chunk 0x%08lX\n", ck.ckid);
1701 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1706 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1707 return AVIERR_FILEREAD;
1710 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1711 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1714 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1715 return AVIERR_FILEREAD;
1719 /* read any extra headers in "LIST","hdrl" */
1720 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1721 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1722 return AVIERR_FILEREAD;
1724 /* search "LIST","movi" chunk in "RIFF","AVI " */
1725 ckLIST1.fccType = listtypeAVIMOVIE;
1726 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1731 This->dwMoviChunkPos = ckLIST1.dwDataOffset - 2 * sizeof(DWORD);
1732 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1733 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1734 return AVIERR_FILEREAD;
1736 /* try to find an index */
1737 ck.ckid = ckidAVINEWINDEX;
1738 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1739 &ck, &ckRIFF, MMIO_FINDCHUNK);
1740 if (SUCCEEDED(hr) && ck.cksize > 0) {
1741 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1742 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1745 /* when we haven't found an index or it's bad, then build one
1746 * by parsing 'movi' chunk */
1747 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1748 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1749 This->ppStreams[nStream]->dwLastFrame = -1;
1751 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1752 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1753 return AVIERR_FILEREAD;
1756 /* seek through the 'movi' list until end */
1757 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1758 if (ck.ckid != FOURCC_LIST) {
1759 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1760 nStream = StreamFromFOURCC(ck.ckid);
1762 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1763 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1765 nStream = StreamFromFOURCC(ck.ckid);
1766 WARN(": file seems to be truncated!\n");
1767 if (nStream <= This->fInfo.dwStreams &&
1768 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1769 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1770 if (ck.cksize != -1) {
1771 ck.cksize -= ck.dwDataOffset;
1772 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1774 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1775 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1783 /* find other chunks */
1784 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1789 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset)
1793 HRESULT hr = AVIERR_OK;
1794 BOOL bAbsolute = TRUE;
1796 lp = (AVIINDEXENTRY*)GlobalAllocPtr(GMEM_MOVEABLE,
1797 IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1799 return AVIERR_MEMORY;
1801 /* adjust limits for index tables, so that inserting will be faster */
1802 for (n = 0; n < This->fInfo.dwStreams; n++) {
1803 IAVIStreamImpl *pStream = This->ppStreams[n];
1805 pStream->dwLastFrame = -1;
1807 if (pStream->idxFrames != NULL) {
1808 GlobalFreePtr(pStream->idxFrames);
1809 pStream->idxFrames = NULL;
1810 pStream->nIdxFrames = 0;
1813 if (pStream->sInfo.dwSampleSize != 0) {
1814 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1815 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1816 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1817 pStream->nIdxFrames =
1818 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1821 pStream->nIdxFrames = pStream->sInfo.dwLength;
1823 pStream->idxFrames =
1824 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1825 if (pStream->idxFrames == NULL) {
1826 pStream->nIdxFrames = 0;
1827 return AVIERR_MEMORY;
1833 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1835 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1836 hr = AVIERR_FILEREAD;
1841 if (pos == (DWORD)-1)
1842 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1844 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1854 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
1855 LONG count, DWORD pos, BOOL *bAbsolute)
1858 return AVIERR_BADPARAM;
1860 for (; count > 0; count--, lp++) {
1861 WORD nStream = StreamFromFOURCC(lp->ckid);
1863 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1864 continue; /* skip these */
1866 if (nStream > This->fInfo.dwStreams)
1867 return AVIERR_BADFORMAT;
1869 if (*bAbsolute == TRUE && lp->dwChunkOffset < This->dwMoviChunkPos)
1873 lp->dwChunkOffset += sizeof(DWORD);
1875 lp->dwChunkOffset += pos;
1877 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1878 return AVIERR_MEMORY;
1884 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1885 LPVOID buffer, LONG size)
1887 /* pre-conditions */
1888 assert(This != NULL);
1889 assert(This->paf != NULL);
1890 assert(This->paf->hmmio != (HMMIO)NULL);
1891 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
1892 assert(pos <= This->dwLastFrame);
1894 /* should we read as much as block gives us? */
1895 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
1896 size = This->idxFrames[pos].dwChunkLength;
1898 /* read into out own buffer or given one? */
1899 if (buffer == NULL) {
1900 /* we also read the chunk */
1901 size += 2 * sizeof(DWORD);
1903 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
1904 if (This->lpBuffer == NULL || size < This->cbBuffer) {
1906 (LPDWORD)GlobalReAllocPtr(This->lpBuffer, max(size, This->sInfo.dwSuggestedBufferSize), GMEM_MOVEABLE);
1907 if (This->lpBuffer == NULL)
1908 return AVIERR_MEMORY;
1909 This->cbBuffer = max(size, This->sInfo.dwSuggestedBufferSize);
1912 /* now read the complete chunk into our buffer */
1913 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
1914 return AVIERR_FILEREAD;
1915 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
1916 return AVIERR_FILEREAD;
1918 /* check if it was the correct block which we have read */
1919 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
1920 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
1921 ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset);
1922 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1923 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
1924 This->idxFrames[pos].dwChunkLength);
1925 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1926 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
1927 return AVIERR_FILEREAD;
1930 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
1931 return AVIERR_FILEREAD;
1932 if (mmioRead(This->paf->hmmio, (HPSTR)buffer, size) != size)
1933 return AVIERR_FILEREAD;
1939 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
1944 /* pre-conditions */
1945 assert(This != NULL);
1946 assert(pos != NULL);
1947 assert(offset != NULL);
1948 assert(This->sInfo.dwSampleSize != 0);
1949 assert(*pos >= This->sInfo.dwStart);
1951 /* convert start sample to start bytes */
1952 (*offset) = (*pos) - This->sInfo.dwStart;
1953 (*offset) *= This->sInfo.dwSampleSize;
1955 /* convert bytes to block number */
1956 for (block = 0; block <= This->dwLastFrame; block++) {
1957 if (This->idxFrames[block].dwChunkLength <= *offset)
1958 (*offset) -= This->idxFrames[block].dwChunkLength;
1966 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
1968 MainAVIHeader MainAVIHdr;
1969 IAVIStreamImpl* pStream;
1978 /* initialize some things */
1979 if (This->dwMoviChunkPos == 0)
1980 AVIFILE_ComputeMoviStart(This);
1982 AVIFILE_UpdateInfo(This);
1984 assert(This->fInfo.dwScale != 0);
1986 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
1987 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
1988 This->fInfo.dwScale);
1989 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
1990 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
1991 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
1992 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
1993 MainAVIHdr.dwInitialFrames = 0;
1994 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
1995 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
1996 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
1997 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
1998 for (nStream = 0; nStream < MainAVIHdr.dwStreams; nStream++) {
1999 pStream = This->ppStreams[nStream];
2001 if (MainAVIHdr.dwInitialFrames < pStream->sInfo.dwInitialFrames)
2002 MainAVIHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2005 /* now begin writing ... */
2006 mmioSeek(This->hmmio, 0, SEEK_SET);
2010 ckRIFF.fccType = formtypeAVI;
2011 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2012 return AVIERR_FILEWRITE;
2014 /* AVI headerlist */
2016 ckLIST1.fccType = listtypeAVIHEADER;
2017 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2018 return AVIERR_FILEWRITE;
2021 ck.ckid = ckidAVIMAINHDR;
2022 ck.cksize = sizeof(MainAVIHdr);
2024 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2025 return AVIERR_FILEWRITE;
2026 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2027 return AVIERR_FILEWRITE;
2028 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2029 return AVIERR_FILEWRITE;
2031 /* write the headers of each stream into a seperate streamheader list */
2032 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2033 AVIStreamHeader strHdr;
2035 pStream = This->ppStreams[nStream];
2037 /* begin the new streamheader list */
2039 ckLIST2.fccType = listtypeSTREAMHEADER;
2040 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2041 return AVIERR_FILEWRITE;
2043 /* create an AVIStreamHeader from the AVSTREAMINFO */
2044 strHdr.fccType = pStream->sInfo.fccType;
2045 strHdr.fccHandler = pStream->sInfo.fccHandler;
2046 strHdr.dwFlags = pStream->sInfo.dwFlags;
2047 strHdr.wPriority = pStream->sInfo.wPriority;
2048 strHdr.wLanguage = pStream->sInfo.wLanguage;
2049 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2050 strHdr.dwScale = pStream->sInfo.dwScale;
2051 strHdr.dwRate = pStream->sInfo.dwRate;
2052 strHdr.dwLength = pStream->sInfo.dwLength;
2053 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2054 strHdr.dwQuality = pStream->sInfo.dwQuality;
2055 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2056 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2057 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2058 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2059 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2061 /* now write the AVIStreamHeader */
2062 ck.ckid = ckidSTREAMHEADER;
2063 ck.cksize = sizeof(strHdr);
2064 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2065 return AVIERR_FILEWRITE;
2066 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2067 return AVIERR_FILEWRITE;
2068 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2069 return AVIERR_FILEWRITE;
2071 /* ... the hopefull ever present streamformat ... */
2072 ck.ckid = ckidSTREAMFORMAT;
2073 ck.cksize = pStream->cbFormat;
2074 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2075 return AVIERR_FILEWRITE;
2076 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2077 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
2078 return AVIERR_FILEWRITE;
2080 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2081 return AVIERR_FILEWRITE;
2083 /* ... some optional existing handler data ... */
2084 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2085 ck.ckid = ckidSTREAMHANDLERDATA;
2086 ck.cksize = pStream->cbHandlerData;
2087 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2088 return AVIERR_FILEWRITE;
2089 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
2090 return AVIERR_FILEWRITE;
2091 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2092 return AVIERR_FILEWRITE;
2095 /* ... some optional additional extra chunk for this stream ... */
2096 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2097 /* the chunk header(s) are already in the strucuture */
2098 if (mmioWrite(This->hmmio, (HPSTR)pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2099 return AVIERR_FILEWRITE;
2102 /* ... an optional name for this stream ... */
2103 if (lstrlenW(pStream->sInfo.szName) > 0) {
2106 ck.ckid = ckidSTREAMNAME;
2107 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2108 if (ck.cksize & 1) /* align */
2110 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2111 return AVIERR_FILEWRITE;
2113 /* the streamname must be saved in ASCII not Unicode */
2114 str = (LPSTR)LocalAlloc(LPTR, ck.cksize);
2116 return AVIERR_MEMORY;
2117 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2118 ck.cksize, NULL, NULL);
2120 if (mmioWrite(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize) {
2121 LocalFree((HLOCAL)str);
2122 return AVIERR_FILEWRITE;
2125 LocalFree((HLOCAL)str);
2126 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2127 return AVIERR_FILEWRITE;
2130 /* close streamheader list for this stream */
2131 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2132 return AVIERR_FILEWRITE;
2133 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2135 /* close the aviheader list */
2136 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2137 return AVIERR_FILEWRITE;
2139 /* check for padding to pre-guessed 'movi'-chunk position */
2140 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2141 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2142 ck.ckid = ckidAVIPADDING;
2143 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2144 assert((LONG)ck.cksize >= 0);
2146 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2147 return AVIERR_FILEWRITE;
2148 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2149 return AVIERR_FILEWRITE;
2150 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2151 return AVIERR_FILEWRITE;
2154 /* now write the 'movi' chunk */
2155 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2157 ckLIST1.fccType = listtypeAVIMOVIE;
2158 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2159 return AVIERR_FILEWRITE;
2160 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2161 return AVIERR_FILEWRITE;
2162 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2163 return AVIERR_FILEWRITE;
2165 /* write 'idx1' chunk */
2166 hr = AVIFILE_SaveIndex(This);
2170 /* write optional extra file chunks */
2171 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2172 /* as for the streams, are the chunk header(s) in the structure */
2173 if (mmioWrite(This->hmmio, (HPSTR)This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2174 return AVIERR_FILEWRITE;
2177 /* close RIFF chunk */
2178 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2179 return AVIERR_FILEWRITE;
2181 /* add some JUNK at end for bad parsers */
2182 memset(&ckRIFF, 0, sizeof(ckRIFF));
2183 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2184 mmioFlush(This->hmmio, 0);
2189 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This)
2191 IAVIStreamImpl *pStream;
2197 ck.ckid = ckidAVINEWINDEX;
2199 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2200 return AVIERR_FILEWRITE;
2202 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2203 /* is interleaved -- write block of coresponding frames */
2204 LONG lInitialFrames = 0;
2208 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2211 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
2213 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2214 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2215 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2218 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2220 DWORD nFrame = lInitialFrames + i;
2222 assert(nFrame < This->nIdxRecords);
2224 idx.ckid = listtypeAVIRECORD;
2225 idx.dwFlags = AVIIF_LIST;
2226 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2227 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2228 - This->dwMoviChunkPos;
2229 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2230 return AVIERR_FILEWRITE;
2232 for (nStream = 0; nStream < This->fInfo.dwStreams; n++) {
2233 pStream = This->ppStreams[nStream];
2235 /* heave we reached start of this stream? */
2236 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2239 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2240 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2242 /* reached end of this stream? */
2243 if (pStream->dwLastFrame <= nFrame)
2246 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2247 pStream->sInfo.dwFormatChangeCount != 0 &&
2248 pStream->idxFmtChanges != NULL) {
2251 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2252 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2253 idx.dwFlags = AVIIF_NOTIME;
2254 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2255 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2256 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2257 - This->dwMoviChunkPos;
2259 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2260 return AVIERR_FILEWRITE;
2264 } /* if have formatchanges */
2266 idx.ckid = pStream->idxFrames[nFrame].ckid;
2267 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2268 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2269 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2270 - This->dwMoviChunkPos;
2271 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2272 return AVIERR_FILEWRITE;
2276 /* not interleaved -- write index for each stream at once */
2277 for (nStream = 0; nStream < This->fInfo.dwStreams; n++) {
2278 pStream = This->ppStreams[nStream];
2280 for (n = 0; n < pStream->dwLastFrame; n++) {
2281 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2282 (pStream->sInfo.dwFormatChangeCount != 0)) {
2285 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2286 if (pStream->idxFmtChanges[pos].ckid == n) {
2287 idx.dwFlags = AVIIF_NOTIME;
2288 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2289 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2291 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2292 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2293 return AVIERR_FILEWRITE;
2297 } /* if have formatchanges */
2299 idx.ckid = pStream->idxFrames[n].ckid;
2300 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2301 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2302 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2303 - This->dwMoviChunkPos;
2305 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2306 return AVIERR_FILEWRITE;
2309 } /* if not interleaved */
2311 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2312 return AVIERR_FILEWRITE;
2317 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2326 /* search the number of the specified stream */
2327 nStream = (ULONG)-1;
2328 for (i = 0; i < This->fInfo.dwStreams; i++) {
2329 assert(This->ppStreams[i] != NULL);
2331 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2345 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2349 /* pre-conditions */
2350 assert(This != NULL);
2352 This->fInfo.dwMaxBytesPerSec = 0;
2353 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2354 This->fInfo.dwSuggestedBufferSize = 0;
2355 This->fInfo.dwWidth = 0;
2356 This->fInfo.dwHeight = 0;
2357 This->fInfo.dwScale = 0;
2358 This->fInfo.dwRate = 0;
2359 This->fInfo.dwLength = 0;
2361 for (i = 0; i < This->fInfo.dwStreams; i++) {
2362 AVISTREAMINFOW *psi;
2365 /* pre-conditions */
2366 assert(This->ppStreams[i] != NULL);
2368 psi = &This->ppStreams[i]->sInfo;
2369 assert(psi->dwScale != 0);
2370 assert(psi->dwRate != 0);
2373 /* use first stream timings as base */
2374 This->fInfo.dwScale = psi->dwScale;
2375 This->fInfo.dwRate = psi->dwRate;
2376 This->fInfo.dwLength = psi->dwLength;
2378 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
2379 (PAVISTREAM)This->ppStreams[i],psi->dwLength);
2380 if (This->fInfo.dwLength < n)
2381 This->fInfo.dwLength = n;
2384 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2385 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2387 if (psi->dwSampleSize != 0) {
2388 /* fixed sample size -- exact computation */
2389 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2392 /* variable sample size -- only upper limit */
2393 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2394 psi->dwRate, psi->dwScale);
2396 /* update dimensions */
2397 n = psi->rcFrame.right - psi->rcFrame.left;
2398 if (This->fInfo.dwWidth < n)
2399 This->fInfo.dwWidth = n;
2400 n = psi->rcFrame.bottom - psi->rcFrame.top;
2401 if (This->fInfo.dwHeight < n)
2402 This->fInfo.dwHeight = n;
2407 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2408 FOURCC ckid, DWORD flags, LPVOID buffer,
2417 /* if no frame/block is already written, we must compute start of movi chunk */
2418 if (This->paf->dwMoviChunkPos == 0)
2419 AVIFILE_ComputeMoviStart(This->paf);
2421 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2422 return AVIERR_FILEWRITE;
2424 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2425 return AVIERR_FILEWRITE;
2426 if (buffer != NULL && size > 0) {
2427 if (mmioWrite(This->paf->hmmio, (HPSTR)buffer, size) != size)
2428 return AVIERR_FILEWRITE;
2430 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2431 return AVIERR_FILEWRITE;
2433 This->paf->fDirty = TRUE;
2434 This->paf->dwNextFramePos = ck.dwDataOffset + ck.cksize;
2436 return AVIFILE_AddFrame(This, ckid, size, ck.dwDataOffset, flags);