Implement VerSetConditionMask by forwarding to ntdll.
[wine/hacks.git] / dlls / avifil32 / avifile.c
bloba1afa0deeda37ab00cab22e13b0ca42592f56fa4
1 /*
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
20 /* TODO:
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 #include <assert.h>
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "winerror.h"
35 #include "windowsx.h"
36 #include "mmsystem.h"
37 #include "vfw.h"
39 #include "avifile_private.h"
40 #include "extrachunk.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
46 #ifndef IDX_PER_BLOCK
47 #define IDX_PER_BLOCK 2730
48 #endif
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,
66 IAVIFile_fnAddRef,
67 IAVIFile_fnRelease,
68 IAVIFile_fnInfo,
69 IAVIFile_fnGetStream,
70 IAVIFile_fnCreateStream,
71 IAVIFile_fnWriteData,
72 IAVIFile_fnReadData,
73 IAVIFile_fnEndRecord,
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,
94 IPersistFile_fnLoad,
95 IPersistFile_fnSave,
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,
118 IAVIStream_fnAddRef,
119 IAVIStream_fnRelease,
120 IAVIStream_fnCreate,
121 IAVIStream_fnInfo,
122 IAVIStream_fnFindSample,
123 IAVIStream_fnReadFormat,
124 IAVIStream_fnSetFormat,
125 IAVIStream_fnRead,
126 IAVIStream_fnWrite,
127 IAVIStream_fnDelete,
128 IAVIStream_fnReadData,
129 IAVIStream_fnWriteData,
130 IAVIStream_fnSetInfo
133 typedef struct _IAVIFileImpl IAVIFileImpl;
135 typedef struct _IPersistFileImpl {
136 /* IUnknown stuff */
137 ICOM_VFIELD(IPersistFile);
139 /* IPersistFile stuff */
140 IAVIFileImpl *paf;
141 } IPersistFileImpl;
143 typedef struct _IAVIStreamImpl {
144 /* IUnknown stuff */
145 ICOM_VFIELD(IAVIStream);
146 DWORD ref;
148 /* IAVIStream stuff */
149 IAVIFileImpl *paf;
150 DWORD nStream; /* the n-th stream in file */
151 AVISTREAMINFOW sInfo;
153 LPVOID lpFormat;
154 DWORD cbFormat;
156 LPVOID lpHandlerData;
157 DWORD cbHandlerData;
159 EXTRACHUNKS extra;
161 LPDWORD lpBuffer;
162 DWORD cbBuffer; /* size of lpBuffer */
163 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
165 LONG lLastFrame; /* 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 */
170 } IAVIStreamImpl;
172 struct _IAVIFileImpl {
173 /* IUnknown stuff */
174 ICOM_VFIELD(IAVIFile);
175 DWORD ref;
177 /* IAVIFile stuff... */
178 IPersistFileImpl iPersistFile;
180 AVIFILEINFOW fInfo;
181 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
183 EXTRACHUNKS fileextra;
185 DWORD dwMoviChunkPos; /* some stuff for saving ... */
186 DWORD dwIdxChunkPos;
187 DWORD dwNextFramePos;
189 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
190 DWORD nIdxRecords;
192 /* IPersistFile stuff ... */
193 HMMIO hmmio;
194 LPWSTR szFileName;
195 UINT uMode;
196 BOOL fDirty;
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,
214 LPLONG offset);
215 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
216 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This);
217 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fccType,
218 LONG lSkip);
219 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
220 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
221 FOURCC ckid, DWORD flags, LPVOID buffer,
222 LONG size);
224 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
226 IAVIFileImpl *pfile;
227 HRESULT hr;
229 assert(riid != NULL && ppv != NULL);
231 *ppv = NULL;
233 pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl));
234 if (pfile == NULL)
235 return AVIERR_MEMORY;
237 ICOM_VTBL(pfile) = &iavift;
238 ICOM_VTBL(&pfile->iPersistFile) = &ipersistft;
239 pfile->ref = 0;
240 pfile->iPersistFile.paf = pfile;
242 hr = IUnknown_QueryInterface((IUnknown*)pfile, riid, ppv);
243 if (FAILED(hr))
244 LocalFree((HLOCAL)pfile);
246 return hr;
249 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
250 LPVOID *obj)
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)) {
258 *obj = iface;
259 IAVIFile_AddRef(iface);
261 return S_OK;
262 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
263 *obj = &This->iPersistFile;
264 IAVIFile_AddRef(iface);
266 return S_OK;
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);
283 UINT i;
285 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
287 if (!--(This->ref)) {
288 if (This->fDirty) {
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 != NULL) {
322 mmioClose(This->hmmio, 0);
323 This->hmmio = NULL;
326 LocalFree((HLOCAL)This);
327 return 0;
329 return This->ref;
332 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
333 LONG size)
335 ICOM_THIS(IAVIFileImpl,iface);
337 TRACE("(%p,%p,%ld)\n",iface,afi,size);
339 if (afi == NULL)
340 return AVIERR_BADPARAM;
341 if (size < 0)
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;
350 return AVIERR_OK;
353 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
354 DWORD fccType, LONG lParam)
356 ICOM_THIS(IAVIFileImpl,iface);
358 ULONG nStream;
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);
373 return AVIERR_OK;
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);
385 DWORD n;
387 TRACE("(%p,%p,%p)\n", iface, avis, asi);
389 /* check parameters */
390 if (avis == NULL || asi == NULL)
391 return AVIERR_BADPARAM;
393 *avis = NULL;
395 /* Does the user have write permission? */
396 if ((This->uMode & MMIO_RWMODE) == 0)
397 return AVIERR_READONLY;
399 /* Can we add another stream? */
400 n = This->fInfo.dwStreams;
401 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
402 /* already reached max nr of streams
403 * or have already written frames to disk */
404 return AVIERR_UNSUPPORTED;
407 /* check AVISTREAMINFO for some really needed things */
408 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
409 return AVIERR_BADFORMAT;
411 /* now it seems to be save to add the stream */
412 assert(This->ppStreams[n] == NULL);
413 This->ppStreams[n] = (IAVIStreamImpl*)LocalAlloc(LPTR,
414 sizeof(IAVIStreamImpl));
415 if (This->ppStreams[n] == NULL)
416 return AVIERR_MEMORY;
418 /* initialize the new allocated stream */
419 AVIFILE_ConstructAVIStream(This, n, asi);
421 This->fInfo.dwStreams++;
422 This->fDirty = TRUE;
424 /* update our AVIFILEINFO structure */
425 AVIFILE_UpdateInfo(This);
427 /* return it */
428 *avis = (PAVISTREAM)This->ppStreams[n];
429 IAVIStream_AddRef(*avis);
431 return AVIERR_OK;
434 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
435 LPVOID lpData, LONG size)
437 ICOM_THIS(IAVIFileImpl,iface);
439 TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size);
441 /* check parameters */
442 if (lpData == NULL)
443 return AVIERR_BADPARAM;
444 if (size < 0)
445 return AVIERR_BADSIZE;
447 /* Do we have write permission? */
448 if ((This->uMode & MMIO_RWMODE) == 0)
449 return AVIERR_READONLY;
451 This->fDirty = TRUE;
453 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
456 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
457 LPVOID lpData, LONG *size)
459 ICOM_THIS(IAVIFileImpl,iface);
461 TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size);
463 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
466 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
468 ICOM_THIS(IAVIFileImpl,iface);
470 FIXME("(%p): stub\n",iface);
472 if ((This->uMode & MMIO_RWMODE) == 0)
473 return AVIERR_READONLY;
475 This->fDirty = TRUE;
477 /* FIXME: end record -- for interleaved files */
479 return E_FAIL;
482 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
483 LONG lParam)
485 ICOM_THIS(IAVIFileImpl,iface);
487 ULONG nStream;
489 TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam);
491 /* check parameter */
492 if (lParam < 0)
493 return AVIERR_BADPARAM;
495 /* Habe user write permissions? */
496 if ((This->uMode & MMIO_RWMODE) == 0)
497 return AVIERR_READONLY;
499 nStream = AVIFILE_SearchStream(This, fccType, lParam);
501 /* Does the requested stream exist? */
502 if (nStream < This->fInfo.dwStreams &&
503 This->ppStreams[nStream] != NULL) {
504 /* ... so delete it now */
505 LocalFree((HLOCAL)This->ppStreams[nStream]);
507 if (This->fInfo.dwStreams - nStream > 0)
508 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
509 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
511 This->ppStreams[This->fInfo.dwStreams] = NULL;
512 This->fInfo.dwStreams--;
513 This->fDirty = TRUE;
515 /* This->fInfo will be updated further when asked for */
516 return AVIERR_OK;
517 } else
518 return AVIERR_NODATA;
521 /***********************************************************************/
523 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
524 REFIID refiid, LPVOID *obj)
526 ICOM_THIS(IPersistFileImpl,iface);
528 assert(This->paf != NULL);
530 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
533 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
535 ICOM_THIS(IPersistFileImpl,iface);
537 assert(This->paf != NULL);
539 return IAVIFile_AddRef((PAVIFILE)This->paf);
542 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
544 ICOM_THIS(IPersistFileImpl,iface);
546 assert(This->paf != NULL);
548 return IAVIFile_Release((PAVIFILE)This->paf);
551 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
552 LPCLSID pClassID)
554 TRACE("(%p,%p)\n", iface, pClassID);
556 if (pClassID == NULL)
557 return AVIERR_BADPARAM;
559 memcpy(pClassID, &CLSID_AVIFile, sizeof(CLSID_AVIFile));
561 return AVIERR_OK;
564 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
566 ICOM_THIS(IPersistFileImpl,iface);
568 TRACE("(%p)\n", iface);
570 assert(This->paf != NULL);
572 return (This->paf->fDirty ? S_OK : S_FALSE);
575 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
576 LPCOLESTR pszFileName, DWORD dwMode)
578 ICOM_THIS(IPersistFileImpl,iface);
580 int len;
582 TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode);
584 /* check parameter */
585 if (pszFileName == NULL)
586 return AVIERR_BADPARAM;
588 assert(This->paf != NULL);
589 if (This->paf->hmmio != NULL)
590 return AVIERR_ERROR; /* No reuse of this object for another file! */
592 /* remeber mode and name */
593 This->paf->uMode = dwMode;
595 len = lstrlenW(pszFileName) + 1;
596 This->paf->szFileName = (LPWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR));
597 if (This->paf->szFileName == NULL)
598 return AVIERR_MEMORY;
599 lstrcpyW(This->paf->szFileName, pszFileName);
601 /* try to open the file */
602 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
603 MMIO_ALLOCBUF | dwMode);
604 if (This->paf->hmmio == NULL)
605 return AVIERR_FILEOPEN;
607 /* should we create a new file? */
608 if (dwMode & OF_CREATE) {
609 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
610 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
612 return AVIERR_OK;
613 } else
614 return AVIFILE_LoadFile(This->paf);
617 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
618 LPCOLESTR pszFileName,BOOL fRemember)
620 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
622 /* We write directly to disk, so nothing to do. */
624 return AVIERR_OK;
627 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
628 LPCOLESTR pszFileName)
630 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
632 /* We write directly to disk, so nothing to do. */
634 return AVIERR_OK;
637 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
638 LPOLESTR *ppszFileName)
640 ICOM_THIS(IPersistFileImpl,iface);
642 TRACE("(%p,%p)\n", iface, ppszFileName);
644 if (ppszFileName == NULL)
645 return AVIERR_BADPARAM;
647 *ppszFileName = NULL;
649 assert(This->paf != NULL);
651 if (This->paf->szFileName != NULL) {
652 int len = lstrlenW(This->paf->szFileName);
654 *ppszFileName = (LPOLESTR)GlobalAllocPtr(GHND, len * sizeof(WCHAR));
655 if (*ppszFileName == NULL)
656 return AVIERR_MEMORY;
658 memcpy(*ppszFileName, This->paf->szFileName, len * sizeof(WCHAR));
661 return AVIERR_OK;
664 /***********************************************************************/
666 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
667 REFIID refiid, LPVOID *obj)
669 ICOM_THIS(IAVIStreamImpl,iface);
671 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
673 if (IsEqualGUID(&IID_IUnknown, refiid) ||
674 IsEqualGUID(&IID_IAVIStream, refiid)) {
675 *obj = This;
676 IAVIStream_AddRef(iface);
678 return S_OK;
680 /* FIXME: IAVIStreaming interface */
682 return OLE_E_ENUM_NOMORE;
685 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
687 ICOM_THIS(IAVIStreamImpl,iface);
689 TRACE("(%p) -> %ld\n", iface, This->ref + 1);
691 /* also add ref to parent, so that it doesn't kill us */
692 if (This->paf != NULL)
693 IAVIFile_AddRef((PAVIFILE)This->paf);
695 return ++(This->ref);
698 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
700 ICOM_THIS(IAVIStreamImpl,iface);
702 TRACE("(%p) -> %ld\n", iface, This->ref - 1);
704 /* we belong to the AVIFile, which must free us! */
705 if (This->ref == 0) {
706 ERR(": already released!\n");
707 return 0;
710 if (This->paf != NULL)
711 IAVIFile_Release((PAVIFILE)This->paf);
713 return --This->ref;
716 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
717 LPARAM lParam2)
719 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
721 /* This IAVIStream interface needs an AVIFile */
722 return AVIERR_UNSUPPORTED;
725 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
726 LONG size)
728 ICOM_THIS(IAVIStreamImpl,iface);
730 TRACE("(%p,%p,%ld)\n", iface, psi, size);
732 if (psi == NULL)
733 return AVIERR_BADPARAM;
734 if (size < 0)
735 return AVIERR_BADSIZE;
737 memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo)));
739 if (size < sizeof(This->sInfo))
740 return AVIERR_BUFFERTOOSMALL;
741 return AVIERR_OK;
744 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
745 LONG flags)
747 ICOM_THIS(IAVIStreamImpl,iface);
749 LONG offset = 0;
751 TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
753 if (flags & FIND_FROM_START) {
754 pos = This->sInfo.dwStart;
755 flags &= ~(FIND_FROM_START|FIND_PREV);
756 flags |= FIND_NEXT;
759 if (This->sInfo.dwSampleSize != 0) {
760 /* convert samples into block number with offset */
761 AVIFILE_SamplesToBlock(This, &pos, &offset);
764 if (flags & FIND_TYPE) {
765 if (flags & FIND_KEY) {
766 while (0 <= pos && pos <= This->lLastFrame) {
767 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
768 goto RETURN_FOUND;
770 if (flags & FIND_NEXT)
771 pos++;
772 else
773 pos--;
775 } else if (flags & FIND_ANY) {
776 while (0 <= pos && pos <= This->lLastFrame) {
777 if (This->idxFrames[pos].dwChunkLength > 0)
778 goto RETURN_FOUND;
780 if (flags & FIND_NEXT)
781 pos++;
782 else
783 pos--;
786 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
787 This->sInfo.fccType == streamtypeVIDEO) {
788 UINT n;
790 if (flags & FIND_NEXT) {
791 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
792 if (This->idxFmtChanges[n].ckid >= pos)
793 goto RETURN_FOUND;
794 } else {
795 for (n = This->sInfo.dwFormatChangeCount; n >= 0; n--) {
796 if (This->idxFmtChanges[n].ckid <= pos)
797 goto RETURN_FOUND;
800 if (pos > This->sInfo.dwStart)
801 return 0; /* format changes always for first frame */
805 return -1;
808 if (flags & FIND_RET) {
809 RETURN_FOUND:
810 if (flags & FIND_LENGTH) {
811 /* logical size */
812 if (This->sInfo.dwSampleSize)
813 pos = This->sInfo.dwSampleSize;
814 else
815 pos = 1;
816 } else if (flags & FIND_OFFSET) {
817 /* physical position */
818 pos = This->idxFrames[pos].dwChunkOffset + offset * This->sInfo.dwSampleSize;
819 } else if (flags & FIND_SIZE) {
820 /* physical size */
821 pos = This->idxFrames[pos].dwChunkLength;
822 } else if (flags & FIND_INDEX) {
823 FIXME(": FIND_INDEX flag is not supported!");
825 pos = This->paf->dwIdxChunkPos;
826 } /* else logical position */
828 return pos;
831 return -1;
834 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
835 LPVOID format, LONG *formatsize)
837 ICOM_THIS(IAVIStreamImpl,iface);
839 TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
841 if (formatsize == NULL)
842 return AVIERR_BADPARAM;
844 /* only interested in needed buffersize? */
845 if (format == NULL || *formatsize <= 0) {
846 *formatsize = This->cbFormat;
848 return AVIERR_OK;
851 /* copy initial format (only as much as will fit) */
852 memcpy(format, This->lpFormat, min(*formatsize, This->cbFormat));
853 if (*formatsize < This->cbFormat) {
854 *formatsize = This->cbFormat;
855 return AVIERR_BUFFERTOOSMALL;
858 /* Could format change? When yes will it change? */
859 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
860 pos > This->sInfo.dwStart) {
861 LONG lLastFmt;
863 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
864 if (lLastFmt > 0) {
865 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt);
869 *formatsize = This->cbFormat;
870 return AVIERR_OK;
873 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
874 LPVOID format, LONG formatsize)
876 ICOM_THIS(IAVIStreamImpl,iface);
878 LPBITMAPINFOHEADER lpbiNew = (LPBITMAPINFOHEADER)format;
880 TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
882 /* check parameters */
883 if (format == NULL || formatsize <= 0)
884 return AVIERR_BADPARAM;
886 /* Do we have write permission? */
887 if ((This->paf->uMode & MMIO_RWMODE) == 0)
888 return AVIERR_READONLY;
890 /* can only set format before frame is written! */
891 if (This->lLastFrame > pos)
892 return AVIERR_UNSUPPORTED;
894 /* initial format or a formatchange? */
895 if (This->lpFormat == NULL) {
896 /* initial format */
897 if (This->paf->dwMoviChunkPos != 0)
898 return AVIERR_ERROR; /* user has used API in wrong sequnece! */
900 This->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, formatsize);
901 if (This->lpFormat == NULL)
902 return AVIERR_MEMORY;
903 This->cbFormat = formatsize;
905 memcpy(This->lpFormat, format, formatsize);
907 /* update some infos about stream */
908 if (This->sInfo.fccType == streamtypeVIDEO) {
909 LONG lDim;
911 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
912 if (lDim < lpbiNew->biWidth)
913 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
914 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
915 if (lDim < lpbiNew->biHeight)
916 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
917 } else if (This->sInfo.fccType == streamtypeAUDIO)
918 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
920 return AVIERR_OK;
921 } else {
922 MMCKINFO ck;
923 LPBITMAPINFOHEADER lpbiOld = (LPBITMAPINFOHEADER)This->lpFormat;
924 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
925 AVIPALCHANGE *lppc = NULL;
926 INT n;
928 /* pherhaps formatchange, check it ... */
929 if (This->cbFormat != formatsize)
930 return AVIERR_UNSUPPORTED;
932 /* no formatchange, only the initial one */
933 if (memcmp(This->lpFormat, format, formatsize) == 0)
934 return AVIERR_OK;
936 /* check that's only the palette, which changes */
937 if (lpbiOld->biSize != lpbiNew->biSize ||
938 lpbiOld->biWidth != lpbiNew->biWidth ||
939 lpbiOld->biHeight != lpbiNew->biHeight ||
940 lpbiOld->biPlanes != lpbiNew->biPlanes ||
941 lpbiOld->biBitCount != lpbiNew->biBitCount ||
942 lpbiOld->biCompression != lpbiNew->biCompression ||
943 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
944 return AVIERR_UNSUPPORTED;
946 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
948 /* simply say all colors have changed */
949 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
950 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
951 lppc = (AVIPALCHANGE*)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
952 if (lppc == NULL)
953 return AVIERR_MEMORY;
955 lppc->bFirstEntry = 0;
956 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
957 lppc->wFlags = 0;
958 for (n = 0; n < lpbiOld->biClrUsed; n++) {
959 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
960 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
961 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
962 lppc->peNew[n].peFlags = 0;
965 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
966 return AVIERR_FILEWRITE;
967 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
968 return AVIERR_FILEWRITE;
969 if (mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize)
970 return AVIERR_FILEWRITE;
971 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
972 return AVIERR_FILEWRITE;
973 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
975 GlobalFreePtr(lppc);
977 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
981 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
982 LONG samples, LPVOID buffer,
983 LONG buffersize, LPLONG bytesread,
984 LPLONG samplesread)
986 ICOM_THIS(IAVIStreamImpl,iface);
988 DWORD size;
989 HRESULT hr;
991 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
992 buffersize, bytesread, samplesread);
994 /* clear return parameters if given */
995 if (bytesread != NULL)
996 *bytesread = 0;
997 if (samplesread != NULL)
998 *samplesread = 0;
1000 /* check parameters */
1001 if (This->sInfo.dwStart > start)
1002 return AVIERR_NODATA; /* couldn't read before start of stream */
1003 if (This->sInfo.dwStart + This->sInfo.dwLength < start)
1004 return AVIERR_NODATA; /* start is past end of stream */
1006 /* should we read as much as possible? */
1007 if (samples == -1) {
1008 /* User should know how much we have read */
1009 if (bytesread == NULL && samplesread == NULL)
1010 return AVIERR_BADPARAM;
1012 if (This->sInfo.dwSampleSize != 0)
1013 samples = buffersize / This->sInfo.dwSampleSize;
1014 else
1015 samples = 1;
1018 /* limit to end of stream */
1019 if (This->sInfo.dwLength < samples)
1020 samples = This->sInfo.dwLength;
1021 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1022 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1024 /* nothing to read? Then leave ... */
1025 if (samples == 0)
1026 return AVIERR_OK;
1028 if (This->sInfo.dwSampleSize != 0) {
1029 /* fixed samplesize -- we can read over frame/block boundaries */
1030 LONG block = start;
1031 LONG offset = 0;
1033 /* convert start sample to block,offset pair */
1034 AVIFILE_SamplesToBlock(This, &block, &offset);
1036 /* convert samples to bytes */
1037 samples *= This->sInfo.dwSampleSize;
1039 while (samples > 0 && buffersize > 0) {
1040 if (block != This->dwCurrentFrame) {
1041 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1042 if (FAILED(hr))
1043 return hr;
1046 size = min((DWORD)samples, (DWORD)buffersize);
1047 size = min(size, This->cbBuffer - offset);
1048 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1050 block++;
1051 offset = 0;
1052 ((BYTE*)buffer) += size;
1053 samples -= size;
1054 buffersize -= size;
1056 /* fill out return parameters if given */
1057 if (bytesread != NULL)
1058 *bytesread += size;
1059 if (samplesread != NULL)
1060 *samplesread += size / This->sInfo.dwSampleSize;
1063 if (samples == 0)
1064 return AVIERR_OK;
1065 else
1066 return AVIERR_BUFFERTOOSMALL;
1067 } else {
1068 /* variable samplesize -- we can only read one full frame/block */
1069 if (samples > 1)
1070 samples = 1;
1072 assert(start <= This->lLastFrame);
1073 size = This->idxFrames[start].dwChunkLength;
1074 if (buffer != NULL && buffersize >= size) {
1075 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1076 if (FAILED(hr))
1077 return hr;
1078 } else if (buffer != NULL)
1079 return AVIERR_BUFFERTOOSMALL;
1081 /* fill out return parameters if given */
1082 if (bytesread != NULL)
1083 *bytesread = size;
1084 if (samplesread != NULL)
1085 *samplesread = samples;
1087 return AVIERR_OK;
1091 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
1092 LONG samples, LPVOID buffer,
1093 LONG buffersize, DWORD flags,
1094 LPLONG sampwritten,
1095 LPLONG byteswritten)
1097 ICOM_THIS(IAVIStreamImpl,iface);
1099 FOURCC ckid;
1100 HRESULT hr;
1102 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
1103 buffer, buffersize, flags, sampwritten, byteswritten);
1105 /* clear return parameters if given */
1106 if (sampwritten != NULL)
1107 *sampwritten = 0;
1108 if (byteswritten != NULL)
1109 *byteswritten = 0;
1111 /* check parameters */
1112 if (buffer == NULL && (buffersize > 0 || samples > 0))
1113 return AVIERR_BADPARAM;
1115 /* Have we write permission? */
1116 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1117 return AVIERR_READONLY;
1119 switch (This->sInfo.fccType) {
1120 case streamtypeAUDIO:
1121 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1122 break;
1123 default:
1124 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1125 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1126 else
1127 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1128 break;
1131 /* append to end of stream? */
1132 if (start == -1) {
1133 if (This->lLastFrame == -1)
1134 start = This->sInfo.dwStart;
1135 else
1136 start = This->sInfo.dwLength;
1137 } else if (This->lLastFrame == -1)
1138 This->sInfo.dwStart = start;
1140 if (This->sInfo.dwSampleSize != 0) {
1141 /* fixed sample size -- audio like */
1142 if (samples * This->sInfo.dwSampleSize != buffersize)
1143 return AVIERR_BADPARAM;
1145 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1146 if (This->sInfo.dwLength != start)
1147 return AVIERR_UNSUPPORTED;
1149 /* Convert position to frame/block */
1150 start = This->lLastFrame + 1;
1152 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1153 FIXME(": not interleaved, could collect audio data!\n");
1155 } else {
1156 /* variable sample size -- video like */
1157 if (samples > 1)
1158 return AVIERR_UNSUPPORTED;
1160 /* must we fill up with empty frames? */
1161 if (This->lLastFrame != -1) {
1162 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1164 while (start > This->lLastFrame + 1) {
1165 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1166 if (FAILED(hr))
1167 return hr;
1172 /* write the block now */
1173 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1174 if (SUCCEEDED(hr)) {
1175 /* fill out return parameters if given */
1176 if (sampwritten != NULL)
1177 *sampwritten = samples;
1178 if (byteswritten != NULL)
1179 *byteswritten = buffersize;
1182 return hr;
1185 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
1186 LONG samples)
1188 ICOM_THIS(IAVIStreamImpl,iface);
1190 FIXME("(%p,%ld,%ld): stub\n", iface, start, samples);
1192 /* check parameters */
1193 if (start < 0 || samples < 0)
1194 return AVIERR_BADPARAM;
1196 /* Delete before start of stream? */
1197 if (start + samples < This->sInfo.dwStart)
1198 return AVIERR_OK;
1200 /* Delete after end of stream? */
1201 if (start > This->sInfo.dwLength)
1202 return AVIERR_OK;
1204 /* For the rest we need write permissions */
1205 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1206 return AVIERR_READONLY;
1208 /* 1. overwrite the data with JUNK
1210 * if ISINTERLEAVED {
1211 * 2. concat all neighboured JUNK-blocks in this record to one
1212 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1213 * to start of this record, repeat this.
1214 * } else {
1215 * 2. concat all neighboured JUNK-blocks.
1216 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1217 * start of this block.
1221 return AVIERR_UNSUPPORTED;
1224 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
1225 LPVOID lp, LPLONG lpread)
1227 ICOM_THIS(IAVIStreamImpl,iface);
1229 TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
1231 if (fcc == ckidSTREAMHANDLERDATA) {
1232 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1233 if (lp == NULL || *lpread <= 0) {
1234 *lpread = This->cbHandlerData;
1235 return AVIERR_OK;
1238 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1239 if (*lpread < This->cbHandlerData)
1240 return AVIERR_BUFFERTOOSMALL;
1241 return AVIERR_OK;
1242 } else
1243 return AVIERR_NODATA;
1244 } else
1245 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1248 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
1249 LPVOID lp, LONG size)
1251 ICOM_THIS(IAVIStreamImpl,iface);
1253 TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
1255 /* check parameters */
1256 if (lp == NULL)
1257 return AVIERR_BADPARAM;
1258 if (size <= 0)
1259 return AVIERR_BADSIZE;
1261 /* need write permission */
1262 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1263 return AVIERR_READONLY;
1265 /* already written something to this file? */
1266 if (This->paf->dwMoviChunkPos != 0) {
1267 /* the data will be inserted before the 'movi' chunk, so check for
1268 * enough space */
1269 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1271 /* ckid,size => 2 * sizeof(DWORD) */
1272 dwPos += 2 * sizeof(DWORD) + size;
1273 if (size >= This->paf->dwMoviChunkPos)
1274 return AVIERR_UNSUPPORTED; /* not enough space left */
1277 This->paf->fDirty = TRUE;
1279 if (fcc == ckidSTREAMHANDLERDATA) {
1280 if (This->lpHandlerData != NULL) {
1281 FIXME(": handler data already set -- overwirte?\n");
1282 return AVIERR_UNSUPPORTED;
1285 This->lpHandlerData = GlobalAllocPtr(GMEM_MOVEABLE, size);
1286 if (This->lpHandlerData == NULL)
1287 return AVIERR_MEMORY;
1288 This->cbHandlerData = size;
1289 memcpy(This->lpHandlerData, lp, size);
1291 return AVIERR_OK;
1292 } else
1293 return WriteExtraChunk(&This->extra, fcc, lp, size);
1296 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
1297 LPAVISTREAMINFOW info, LONG infolen)
1299 FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
1301 return E_FAIL;
1304 /***********************************************************************/
1306 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1308 /* pre-conditions */
1309 assert(This != NULL);
1311 switch (TWOCCFromFOURCC(ckid)) {
1312 case cktypeDIBbits:
1313 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1314 flags |= AVIIF_KEYFRAME;
1315 break;
1316 case cktypeDIBcompressed:
1317 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1318 flags &= ~AVIIF_KEYFRAME;
1319 break;
1320 case cktypePALchange:
1321 if (This->sInfo.fccType != streamtypeVIDEO) {
1322 ERR(": found palette change in non-video stream!\n");
1323 return AVIERR_BADFORMAT;
1325 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1326 This->sInfo.dwFormatChangeCount++;
1328 if (This->idxFmtChanges == NULL || This->sInfo.dwFormatChangeCount < This->nIdxFmtChanges) {
1329 UINT n = This->sInfo.dwFormatChangeCount;
1331 This->nIdxFmtChanges += 16;
1332 This->idxFmtChanges = GlobalReAllocPtr(This->idxFmtChanges, This->nIdxFmtChanges * sizeof(AVIINDEXENTRY), GHND);
1333 if (This->idxFmtChanges == NULL)
1334 return AVIERR_MEMORY;
1336 This->idxFmtChanges[n].ckid = This->lLastFrame;
1337 This->idxFmtChanges[n].dwFlags = 0;
1338 This->idxFmtChanges[n].dwChunkOffset = offset;
1339 This->idxFmtChanges[n].dwChunkLength = size;
1341 return AVIERR_OK;
1343 break;
1344 case cktypeWAVEbytes:
1345 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1346 flags |= AVIIF_KEYFRAME;
1347 break;
1348 default:
1349 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1350 break;
1353 /* first frame is alwasy a keyframe */
1354 if (This->lLastFrame == -1)
1355 flags |= AVIIF_KEYFRAME;
1357 if (This->sInfo.dwSuggestedBufferSize < size)
1358 This->sInfo.dwSuggestedBufferSize = size;
1360 /* get memory for index */
1361 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1362 This->nIdxFrames += 512;
1363 This->idxFrames = GlobalReAllocPtr(This->idxFrames, This->nIdxFrames * sizeof(AVIINDEXENTRY), GHND);
1364 if (This->idxFrames == NULL)
1365 return AVIERR_MEMORY;
1368 This->lLastFrame++;
1369 This->idxFrames[This->lLastFrame].ckid = ckid;
1370 This->idxFrames[This->lLastFrame].dwFlags = flags;
1371 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1372 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1374 /* update AVISTREAMINFO structure if neccessary */
1375 if (This->sInfo.dwLength < This->lLastFrame)
1376 This->sInfo.dwLength = This->lLastFrame;
1378 return AVIERR_OK;
1381 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1383 DWORD dwPos;
1384 DWORD nStream;
1386 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1387 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1389 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1390 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1392 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1393 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1394 dwPos += ((pStream->cbFormat + 1) & ~1);
1395 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1396 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1);
1397 if (lstrlenW(pStream->sInfo.szName) > 0)
1398 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1);
1401 if (This->dwMoviChunkPos == 0) {
1402 This->dwNextFramePos = dwPos;
1404 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1405 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1406 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1408 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1411 return dwPos;
1414 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, LPAVISTREAMINFOW asi)
1416 IAVIStreamImpl *pstream;
1418 /* pre-conditions */
1419 assert(paf != NULL);
1420 assert(nr < MAX_AVISTREAMS);
1421 assert(paf->ppStreams[nr] != NULL);
1423 pstream = paf->ppStreams[nr];
1425 ICOM_VTBL(pstream) = &iavist;
1426 pstream->ref = 0;
1427 pstream->paf = paf;
1428 pstream->nStream = nr;
1429 pstream->dwCurrentFrame = -1;
1430 pstream->lLastFrame = -1;
1432 if (asi != NULL) {
1433 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1435 if (asi->dwLength > 0) {
1436 /* pre-allocate mem for frame-index structure */
1437 pstream->idxFrames =
1438 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwLength * sizeof(AVIINDEXENTRY));
1439 if (pstream->idxFrames != NULL)
1440 pstream->nIdxFrames = asi->dwLength;
1442 if (asi->dwFormatChangeCount > 0) {
1443 /* pre-allocate mem for formatchange-index structure */
1444 pstream->idxFmtChanges =
1445 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1446 if (pstream->idxFmtChanges != NULL)
1447 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1450 /* These values will be computed */
1451 pstream->sInfo.dwLength = 0;
1452 pstream->sInfo.dwSuggestedBufferSize = 0;
1453 pstream->sInfo.dwFormatChangeCount = 0;
1454 pstream->sInfo.dwEditCount = 1;
1455 if (pstream->sInfo.dwSampleSize > 0)
1456 SetRectEmpty(&pstream->sInfo.rcFrame);
1459 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1462 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1464 /* pre-conditions */
1465 assert(This != NULL);
1467 This->dwCurrentFrame = -1;
1468 This->lLastFrame = -1;
1469 This->paf = NULL;
1470 if (This->idxFrames != NULL) {
1471 GlobalFreePtr(This->idxFrames);
1472 This->idxFrames = NULL;
1473 This->nIdxFrames = 0;
1475 if (This->idxFmtChanges != NULL) {
1476 GlobalFreePtr(This->idxFmtChanges);
1477 This->idxFmtChanges = NULL;
1479 if (This->lpBuffer != NULL) {
1480 GlobalFreePtr(This->lpBuffer);
1481 This->lpBuffer = NULL;
1482 This->cbBuffer = 0;
1484 if (This->lpHandlerData != NULL) {
1485 GlobalFreePtr(This->lpHandlerData);
1486 This->lpHandlerData = NULL;
1487 This->cbHandlerData = 0;
1489 if (This->extra.lp != NULL) {
1490 GlobalFreePtr(This->extra.lp);
1491 This->extra.lp = NULL;
1492 This->extra.cb = 0;
1494 if (This->lpFormat != NULL) {
1495 GlobalFreePtr(This->lpFormat);
1496 This->lpFormat = NULL;
1497 This->cbFormat = 0;
1501 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1503 MainAVIHeader MainAVIHdr;
1504 MMCKINFO ckRIFF;
1505 MMCKINFO ckLIST1;
1506 MMCKINFO ckLIST2;
1507 MMCKINFO ck;
1508 IAVIStreamImpl *pStream;
1509 DWORD nStream;
1510 HRESULT hr;
1512 if (This->hmmio == NULL)
1513 return AVIERR_FILEOPEN;
1515 /* initialize stream ptr's */
1516 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1518 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1519 ckRIFF.fccType = formtypeAVI;
1520 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1521 ERR(": not an AVI!\n");
1522 return AVIERR_FILEREAD;
1525 /* get "LIST" "hdrl" */
1526 ckLIST1.fccType = listtypeAVIHEADER;
1527 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1528 if (FAILED(hr))
1529 return hr;
1531 /* get "avih" chunk */
1532 ck.ckid = ckidAVIMAINHDR;
1533 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1534 if (FAILED(hr))
1535 return hr;
1537 if (ck.cksize != sizeof(MainAVIHdr)) {
1538 ERR(": invalid size of %ld for MainAVIHeader!\n", ck.cksize);
1539 return AVIERR_BADFORMAT;
1541 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1542 return AVIERR_FILEREAD;
1544 /* adjust permissions if copyrighted material in file */
1545 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1546 This->uMode &= ~MMIO_RWMODE;
1547 This->uMode |= MMIO_READ;
1550 /* convert MainAVIHeader into AVIFILINFOW */
1551 memset(&This->fInfo, 0, sizeof(This->fInfo));
1552 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1553 This->fInfo.dwScale = 1000000;
1554 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1555 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1556 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1557 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1558 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1559 This->fInfo.dwSuggestedBufferSize = MainAVIHdr.dwSuggestedBufferSize;
1560 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1561 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1562 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1563 sizeof(This->fInfo.szFileType));
1565 /* go back to into header list */
1566 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1567 return AVIERR_FILEREAD;
1569 /* foreach stream exists a "LIST","strl" chunk */
1570 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1571 /* get next nested chunk in this "LIST","strl" */
1572 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1573 return AVIERR_FILEREAD;
1575 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1576 if (ckLIST2.ckid == FOURCC_LIST &&
1577 ckLIST2.fccType == listtypeSTREAMHEADER) {
1578 pStream = This->ppStreams[nStream] =
1579 (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
1580 if (pStream == NULL)
1581 return AVIERR_MEMORY;
1582 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1584 ck.ckid = 0;
1585 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1586 switch (ck.ckid) {
1587 case ckidSTREAMHANDLERDATA:
1588 if (pStream->lpHandlerData != NULL)
1589 return AVIERR_BADFORMAT;
1590 pStream->lpHandlerData = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1591 ck.cksize);
1592 if (pStream->lpHandlerData == NULL)
1593 return AVIERR_MEMORY;
1594 pStream->cbHandlerData = ck.cksize;
1596 if (mmioRead(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
1597 return AVIERR_FILEREAD;
1598 break;
1599 case ckidSTREAMFORMAT:
1600 if (pStream->lpFormat != NULL)
1601 return AVIERR_BADFORMAT;
1602 pStream->lpFormat = GlobalAllocPtr(GMEM_DDESHARE|GMEM_MOVEABLE,
1603 ck.cksize);
1604 if (pStream->lpFormat == NULL)
1605 return AVIERR_MEMORY;
1606 pStream->cbFormat = ck.cksize;
1608 if (mmioRead(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
1609 return AVIERR_FILEREAD;
1611 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1612 LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pStream->lpFormat;
1614 /* some corrections to the video format */
1615 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1616 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1617 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1618 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1619 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1620 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1621 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1622 lpbi->biCompression = BI_RLE8;
1624 if (lpbi->biCompression == BI_RGB &&
1625 (pStream->sInfo.fccHandler == 0 ||
1626 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1627 pStream->sInfo.fccHandler = comptypeDIB;
1629 /* init rcFrame if it's empty */
1630 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1631 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1633 break;
1634 case ckidSTREAMHEADER:
1636 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1638 AVIStreamHeader streamHdr;
1639 WCHAR szType[25];
1640 WCHAR streamNameFmt[25];
1641 UINT count;
1642 LONG n = ck.cksize;
1644 if (ck.cksize > sizeof(streamHdr))
1645 n = sizeof(streamHdr);
1647 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1648 return AVIERR_FILEREAD;
1650 pStream->sInfo.fccType = streamHdr.fccType;
1651 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1652 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1653 pStream->sInfo.wPriority = streamHdr.wPriority;
1654 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1655 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1656 pStream->sInfo.dwScale = streamHdr.dwScale;
1657 pStream->sInfo.dwRate = streamHdr.dwRate;
1658 pStream->sInfo.dwStart = streamHdr.dwStart;
1659 pStream->sInfo.dwLength = streamHdr.dwLength;
1660 pStream->sInfo.dwSuggestedBufferSize =
1661 streamHdr.dwSuggestedBufferSize;
1662 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1663 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1664 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1665 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1666 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1667 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1668 pStream->sInfo.dwEditCount = 0;
1669 pStream->sInfo.dwFormatChangeCount = 0;
1671 /* generate description for stream like "filename.avi Type #n" */
1672 if (streamHdr.fccType == streamtypeVIDEO)
1673 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType));
1674 else if (streamHdr.fccType == streamtypeAUDIO)
1675 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType));
1676 else
1677 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1679 /* get count of this streamtype up to this stream */
1680 count = 0;
1681 for (n = nStream; 0 <= n; n--) {
1682 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1683 count++;
1686 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1688 LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt));
1690 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1691 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1692 AVIFILE_BasenameW(This->szFileName), szType, count);
1694 break;
1695 case ckidSTREAMNAME:
1696 { /* streamname will be saved as ASCII string */
1697 LPSTR str = (LPSTR)LocalAlloc(LMEM_FIXED, ck.cksize);
1698 if (str == NULL)
1699 return AVIERR_MEMORY;
1701 if (mmioRead(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize)
1702 return AVIERR_FILEREAD;
1704 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1705 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1707 LocalFree((HLOCAL)str);
1709 break;
1710 case ckidAVIPADDING:
1711 case mmioFOURCC('p','a','d','d'):
1712 break;
1713 default:
1714 WARN(": found extra chunk 0x%08lX\n", ck.ckid);
1715 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1716 if (FAILED(hr))
1717 return hr;
1720 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1721 return AVIERR_FILEREAD;
1723 } else {
1724 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1725 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1726 if (FAILED(hr))
1727 return hr;
1728 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1729 return AVIERR_FILEREAD;
1733 /* read any extra headers in "LIST","hdrl" */
1734 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1735 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1736 return AVIERR_FILEREAD;
1738 /* search "LIST","movi" chunk in "RIFF","AVI " */
1739 ckLIST1.fccType = listtypeAVIMOVIE;
1740 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1741 MMIO_FINDLIST);
1742 if (FAILED(hr))
1743 return hr;
1745 This->dwMoviChunkPos = ckLIST1.dwDataOffset - 2 * sizeof(DWORD);
1746 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1747 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1748 return AVIERR_FILEREAD;
1750 /* try to find an index */
1751 ck.ckid = ckidAVINEWINDEX;
1752 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1753 &ck, &ckRIFF, MMIO_FINDCHUNK);
1754 if (SUCCEEDED(hr) && ck.cksize > 0) {
1755 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1756 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1759 /* when we haven't found an index or it's bad, then build one
1760 * by parsing 'movi' chunk */
1761 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1762 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1763 This->ppStreams[nStream]->lLastFrame = -1;
1765 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1766 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1767 return AVIERR_FILEREAD;
1770 /* seek through the 'movi' list until end */
1771 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1772 if (ck.ckid != FOURCC_LIST) {
1773 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1774 nStream = StreamFromFOURCC(ck.ckid);
1776 if (nStream > This->fInfo.dwStreams)
1777 return AVIERR_BADFORMAT;
1779 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1780 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1781 } else {
1782 nStream = StreamFromFOURCC(ck.ckid);
1783 WARN(": file seems to be truncated!\n");
1784 if (nStream <= This->fInfo.dwStreams &&
1785 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1786 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1787 if (ck.cksize != -1) {
1788 ck.cksize -= ck.dwDataOffset;
1789 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1791 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1792 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1800 /* find other chunks */
1801 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1803 return AVIERR_OK;
1806 static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset)
1808 AVIINDEXENTRY *lp;
1809 DWORD pos, n;
1810 HRESULT hr = AVIERR_OK;
1811 BOOL bAbsolute = TRUE;
1813 lp = (AVIINDEXENTRY*)GlobalAllocPtr(GMEM_MOVEABLE,
1814 IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1815 if (lp == NULL)
1816 return AVIERR_MEMORY;
1818 /* adjust limits for index tables, so that inserting will be faster */
1819 for (n = 0; n < This->fInfo.dwStreams; n++) {
1820 IAVIStreamImpl *pStream = This->ppStreams[n];
1822 pStream->lLastFrame = -1;
1824 if (pStream->idxFrames != NULL) {
1825 GlobalFreePtr(pStream->idxFrames);
1826 pStream->idxFrames = NULL;
1827 pStream->nIdxFrames = 0;
1830 if (pStream->sInfo.dwSampleSize != 0) {
1831 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1832 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1833 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1834 pStream->nIdxFrames =
1835 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1837 } else
1838 pStream->nIdxFrames = pStream->sInfo.dwLength;
1840 pStream->idxFrames =
1841 (AVIINDEXENTRY*)GlobalAllocPtr(GHND, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1842 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1843 pStream->nIdxFrames = 0;
1844 return AVIERR_MEMORY;
1848 pos = (DWORD)-1;
1849 while (size != 0) {
1850 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1852 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1853 hr = AVIERR_FILEREAD;
1854 break;
1856 size -= read;
1858 if (pos == (DWORD)-1)
1859 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1861 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1862 pos, &bAbsolute);
1865 if (lp != NULL)
1866 GlobalFreePtr(lp);
1868 return hr;
1871 static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
1872 LONG count, DWORD pos, BOOL *bAbsolute)
1874 if (lp == NULL)
1875 return AVIERR_BADPARAM;
1877 for (; count > 0; count--, lp++) {
1878 WORD nStream = StreamFromFOURCC(lp->ckid);
1880 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1881 continue; /* skip these */
1883 if (nStream > This->fInfo.dwStreams)
1884 return AVIERR_BADFORMAT;
1886 if (*bAbsolute == TRUE && lp->dwChunkOffset < This->dwMoviChunkPos)
1887 *bAbsolute = FALSE;
1889 if (*bAbsolute)
1890 lp->dwChunkOffset += sizeof(DWORD);
1891 else
1892 lp->dwChunkOffset += pos;
1894 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1895 return AVIERR_MEMORY;
1898 return AVIERR_OK;
1901 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1902 LPVOID buffer, LONG size)
1904 /* pre-conditions */
1905 assert(This != NULL);
1906 assert(This->paf != NULL);
1907 assert(This->paf->hmmio != NULL);
1908 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
1909 assert(pos <= This->lLastFrame);
1911 /* should we read as much as block gives us? */
1912 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
1913 size = This->idxFrames[pos].dwChunkLength;
1915 /* read into out own buffer or given one? */
1916 if (buffer == NULL) {
1917 /* we also read the chunk */
1918 size += 2 * sizeof(DWORD);
1920 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
1921 if (This->lpBuffer == NULL || size < This->cbBuffer) {
1922 This->lpBuffer =
1923 (LPDWORD)GlobalReAllocPtr(This->lpBuffer, max(size, This->sInfo.dwSuggestedBufferSize), GMEM_MOVEABLE);
1924 if (This->lpBuffer == NULL)
1925 return AVIERR_MEMORY;
1926 This->cbBuffer = max(size, This->sInfo.dwSuggestedBufferSize);
1929 /* now read the complete chunk into our buffer */
1930 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
1931 return AVIERR_FILEREAD;
1932 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
1933 return AVIERR_FILEREAD;
1935 /* check if it was the correct block which we have read */
1936 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
1937 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
1938 ERR(": block %ld not found at 0x%08lX\n", pos, This->idxFrames[pos].dwChunkOffset);
1939 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1940 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
1941 This->idxFrames[pos].dwChunkLength);
1942 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1943 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
1944 return AVIERR_FILEREAD;
1946 } else {
1947 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
1948 return AVIERR_FILEREAD;
1949 if (mmioRead(This->paf->hmmio, (HPSTR)buffer, size) != size)
1950 return AVIERR_FILEREAD;
1953 return AVIERR_OK;
1956 static void AVIFILE_SamplesToBlock(IAVIStreamImpl *This, LPLONG pos,
1957 LPLONG offset)
1959 DWORD block;
1961 /* pre-conditions */
1962 assert(This != NULL);
1963 assert(pos != NULL);
1964 assert(offset != NULL);
1965 assert(This->sInfo.dwSampleSize != 0);
1966 assert(*pos >= This->sInfo.dwStart);
1968 /* convert start sample to start bytes */
1969 (*offset) = (*pos) - This->sInfo.dwStart;
1970 (*offset) *= This->sInfo.dwSampleSize;
1972 /* convert bytes to block number */
1973 for (block = 0; block <= This->lLastFrame; block++) {
1974 if (This->idxFrames[block].dwChunkLength <= *offset)
1975 (*offset) -= This->idxFrames[block].dwChunkLength;
1976 else
1977 break;
1980 *pos = block;
1983 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
1985 MainAVIHeader MainAVIHdr;
1986 IAVIStreamImpl* pStream;
1987 MMCKINFO ckRIFF;
1988 MMCKINFO ckLIST1;
1989 MMCKINFO ckLIST2;
1990 MMCKINFO ck;
1991 DWORD nStream;
1992 DWORD dwPos;
1993 HRESULT hr;
1995 /* initialize some things */
1996 if (This->dwMoviChunkPos == 0)
1997 AVIFILE_ComputeMoviStart(This);
1999 AVIFILE_UpdateInfo(This);
2001 assert(This->fInfo.dwScale != 0);
2003 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2004 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2005 This->fInfo.dwScale);
2006 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2007 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2008 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2009 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2010 MainAVIHdr.dwInitialFrames = 0;
2011 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2012 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2013 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2014 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2015 for (nStream = 0; nStream < MainAVIHdr.dwStreams; nStream++) {
2016 pStream = This->ppStreams[nStream];
2018 if (MainAVIHdr.dwInitialFrames < pStream->sInfo.dwInitialFrames)
2019 MainAVIHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2022 /* now begin writing ... */
2023 mmioSeek(This->hmmio, 0, SEEK_SET);
2025 /* RIFF chunk */
2026 ckRIFF.cksize = 0;
2027 ckRIFF.fccType = formtypeAVI;
2028 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2029 return AVIERR_FILEWRITE;
2031 /* AVI headerlist */
2032 ckLIST1.cksize = 0;
2033 ckLIST1.fccType = listtypeAVIHEADER;
2034 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2035 return AVIERR_FILEWRITE;
2037 /* MainAVIHeader */
2038 ck.ckid = ckidAVIMAINHDR;
2039 ck.cksize = sizeof(MainAVIHdr);
2040 ck.fccType = 0;
2041 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2042 return AVIERR_FILEWRITE;
2043 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2044 return AVIERR_FILEWRITE;
2045 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2046 return AVIERR_FILEWRITE;
2048 /* write the headers of each stream into a seperate streamheader list */
2049 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2050 AVIStreamHeader strHdr;
2052 pStream = This->ppStreams[nStream];
2054 /* begin the new streamheader list */
2055 ckLIST2.cksize = 0;
2056 ckLIST2.fccType = listtypeSTREAMHEADER;
2057 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2058 return AVIERR_FILEWRITE;
2060 /* create an AVIStreamHeader from the AVSTREAMINFO */
2061 strHdr.fccType = pStream->sInfo.fccType;
2062 strHdr.fccHandler = pStream->sInfo.fccHandler;
2063 strHdr.dwFlags = pStream->sInfo.dwFlags;
2064 strHdr.wPriority = pStream->sInfo.wPriority;
2065 strHdr.wLanguage = pStream->sInfo.wLanguage;
2066 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2067 strHdr.dwScale = pStream->sInfo.dwScale;
2068 strHdr.dwRate = pStream->sInfo.dwRate;
2069 strHdr.dwStart = pStream->sInfo.dwStart;
2070 strHdr.dwLength = pStream->sInfo.dwLength;
2071 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2072 strHdr.dwQuality = pStream->sInfo.dwQuality;
2073 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2074 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2075 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2076 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2077 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2079 /* now write the AVIStreamHeader */
2080 ck.ckid = ckidSTREAMHEADER;
2081 ck.cksize = sizeof(strHdr);
2082 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2083 return AVIERR_FILEWRITE;
2084 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2085 return AVIERR_FILEWRITE;
2086 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2087 return AVIERR_FILEWRITE;
2089 /* ... the hopefull ever present streamformat ... */
2090 ck.ckid = ckidSTREAMFORMAT;
2091 ck.cksize = pStream->cbFormat;
2092 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2093 return AVIERR_FILEWRITE;
2094 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2095 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
2096 return AVIERR_FILEWRITE;
2098 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2099 return AVIERR_FILEWRITE;
2101 /* ... some optional existing handler data ... */
2102 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2103 ck.ckid = ckidSTREAMHANDLERDATA;
2104 ck.cksize = pStream->cbHandlerData;
2105 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2106 return AVIERR_FILEWRITE;
2107 if (mmioWrite(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
2108 return AVIERR_FILEWRITE;
2109 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2110 return AVIERR_FILEWRITE;
2113 /* ... some optional additional extra chunk for this stream ... */
2114 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2115 /* the chunk header(s) are already in the strucuture */
2116 if (mmioWrite(This->hmmio, (HPSTR)pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2117 return AVIERR_FILEWRITE;
2120 /* ... an optional name for this stream ... */
2121 if (lstrlenW(pStream->sInfo.szName) > 0) {
2122 LPSTR str;
2124 ck.ckid = ckidSTREAMNAME;
2125 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2126 if (ck.cksize & 1) /* align */
2127 ck.cksize++;
2128 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2129 return AVIERR_FILEWRITE;
2131 /* the streamname must be saved in ASCII not Unicode */
2132 str = (LPSTR)LocalAlloc(LPTR, ck.cksize);
2133 if (str == NULL)
2134 return AVIERR_MEMORY;
2135 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2136 ck.cksize, NULL, NULL);
2138 if (mmioWrite(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize) {
2139 LocalFree((HLOCAL)str);
2140 return AVIERR_FILEWRITE;
2143 LocalFree((HLOCAL)str);
2144 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2145 return AVIERR_FILEWRITE;
2148 /* close streamheader list for this stream */
2149 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2150 return AVIERR_FILEWRITE;
2151 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2153 /* close the aviheader list */
2154 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2155 return AVIERR_FILEWRITE;
2157 /* check for padding to pre-guessed 'movi'-chunk position */
2158 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2159 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2160 ck.ckid = ckidAVIPADDING;
2161 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2162 assert((LONG)ck.cksize >= 0);
2164 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2165 return AVIERR_FILEWRITE;
2166 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2167 return AVIERR_FILEWRITE;
2168 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2169 return AVIERR_FILEWRITE;
2172 /* now write the 'movi' chunk */
2173 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2174 ckLIST1.cksize = 0;
2175 ckLIST1.fccType = listtypeAVIMOVIE;
2176 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2177 return AVIERR_FILEWRITE;
2178 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2179 return AVIERR_FILEWRITE;
2180 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2181 return AVIERR_FILEWRITE;
2183 /* write 'idx1' chunk */
2184 hr = AVIFILE_SaveIndex(This);
2185 if (FAILED(hr))
2186 return hr;
2188 /* write optional extra file chunks */
2189 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2190 /* as for the streams, are the chunk header(s) in the structure */
2191 if (mmioWrite(This->hmmio, (HPSTR)This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2192 return AVIERR_FILEWRITE;
2195 /* close RIFF chunk */
2196 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2197 return AVIERR_FILEWRITE;
2199 /* add some JUNK at end for bad parsers */
2200 memset(&ckRIFF, 0, sizeof(ckRIFF));
2201 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2202 mmioFlush(This->hmmio, 0);
2204 return AVIERR_OK;
2207 static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This)
2209 IAVIStreamImpl *pStream;
2210 AVIINDEXENTRY idx;
2211 MMCKINFO ck;
2212 DWORD nStream;
2213 LONG n;
2215 ck.ckid = ckidAVINEWINDEX;
2216 ck.cksize = 0;
2217 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2218 return AVIERR_FILEWRITE;
2220 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2221 /* is interleaved -- write block of coresponding frames */
2222 LONG lInitialFrames = 0;
2223 LONG stepsize;
2224 LONG i;
2226 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2227 stepsize = 1;
2228 else
2229 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
2231 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2232 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2233 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2236 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2237 i += stepsize) {
2238 DWORD nFrame = lInitialFrames + i;
2240 assert(nFrame < This->nIdxRecords);
2242 idx.ckid = listtypeAVIRECORD;
2243 idx.dwFlags = AVIIF_LIST;
2244 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2245 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2246 - This->dwMoviChunkPos;
2247 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2248 return AVIERR_FILEWRITE;
2250 for (nStream = 0; nStream < This->fInfo.dwStreams; n++) {
2251 pStream = This->ppStreams[nStream];
2253 /* heave we reached start of this stream? */
2254 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2255 continue;
2257 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2258 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2260 /* reached end of this stream? */
2261 if (pStream->lLastFrame <= nFrame)
2262 continue;
2264 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2265 pStream->sInfo.dwFormatChangeCount != 0 &&
2266 pStream->idxFmtChanges != NULL) {
2267 DWORD pos;
2269 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2270 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2271 idx.dwFlags = AVIIF_NOTIME;
2272 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2273 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2274 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2275 - This->dwMoviChunkPos;
2277 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2278 return AVIERR_FILEWRITE;
2279 break;
2282 } /* if have formatchanges */
2284 idx.ckid = pStream->idxFrames[nFrame].ckid;
2285 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2286 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2287 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2288 - This->dwMoviChunkPos;
2289 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2290 return AVIERR_FILEWRITE;
2293 } else {
2294 /* not interleaved -- write index for each stream at once */
2295 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2296 pStream = This->ppStreams[nStream];
2298 if (pStream->lLastFrame == -1)
2299 pStream->lLastFrame = 0;
2301 for (n = 0; n < pStream->lLastFrame; n++) {
2302 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2303 (pStream->sInfo.dwFormatChangeCount != 0)) {
2304 DWORD pos;
2306 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2307 if (pStream->idxFmtChanges[pos].ckid == n) {
2308 idx.dwFlags = AVIIF_NOTIME;
2309 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2310 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2311 idx.dwChunkOffset =
2312 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2313 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2314 return AVIERR_FILEWRITE;
2315 break;
2318 } /* if have formatchanges */
2320 idx.ckid = pStream->idxFrames[n].ckid;
2321 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2322 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2323 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2324 - This->dwMoviChunkPos;
2326 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2327 return AVIERR_FILEWRITE;
2330 } /* if not interleaved */
2332 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2333 return AVIERR_FILEWRITE;
2335 return AVIERR_OK;
2338 static ULONG AVIFILE_SearchStream(IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2340 UINT i;
2341 UINT nStream;
2343 /* pre-condition */
2344 assert(lSkip >= 0);
2346 if (fcc != 0) {
2347 /* search the number of the specified stream */
2348 nStream = (ULONG)-1;
2349 for (i = 0; i < This->fInfo.dwStreams; i++) {
2350 assert(This->ppStreams[i] != NULL);
2352 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2353 if (lSkip == 0) {
2354 nStream = i;
2355 break;
2356 } else
2357 lSkip--;
2360 } else
2361 nStream = lSkip;
2363 return nStream;
2366 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2368 UINT i;
2370 /* pre-conditions */
2371 assert(This != NULL);
2373 This->fInfo.dwMaxBytesPerSec = 0;
2374 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2375 This->fInfo.dwSuggestedBufferSize = 0;
2376 This->fInfo.dwWidth = 0;
2377 This->fInfo.dwHeight = 0;
2378 This->fInfo.dwScale = 0;
2379 This->fInfo.dwRate = 0;
2380 This->fInfo.dwLength = 0;
2382 for (i = 0; i < This->fInfo.dwStreams; i++) {
2383 AVISTREAMINFOW *psi;
2384 DWORD n;
2386 /* pre-conditions */
2387 assert(This->ppStreams[i] != NULL);
2389 psi = &This->ppStreams[i]->sInfo;
2390 assert(psi->dwScale != 0);
2391 assert(psi->dwRate != 0);
2393 if (i == 0) {
2394 /* use first stream timings as base */
2395 This->fInfo.dwScale = psi->dwScale;
2396 This->fInfo.dwRate = psi->dwRate;
2397 This->fInfo.dwLength = psi->dwLength;
2398 } else {
2399 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
2400 (PAVISTREAM)This->ppStreams[i],psi->dwLength);
2401 if (This->fInfo.dwLength < n)
2402 This->fInfo.dwLength = n;
2405 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2406 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2408 if (psi->dwSampleSize != 0) {
2409 /* fixed sample size -- exact computation */
2410 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2411 psi->dwScale);
2412 } else {
2413 /* variable sample size -- only upper limit */
2414 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2415 psi->dwRate, psi->dwScale);
2417 /* update dimensions */
2418 n = psi->rcFrame.right - psi->rcFrame.left;
2419 if (This->fInfo.dwWidth < n)
2420 This->fInfo.dwWidth = n;
2421 n = psi->rcFrame.bottom - psi->rcFrame.top;
2422 if (This->fInfo.dwHeight < n)
2423 This->fInfo.dwHeight = n;
2428 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2429 FOURCC ckid, DWORD flags, LPVOID buffer,
2430 LONG size)
2432 MMCKINFO ck;
2434 ck.ckid = ckid;
2435 ck.cksize = size;
2436 ck.fccType = 0;
2438 /* if no frame/block is already written, we must compute start of movi chunk */
2439 if (This->paf->dwMoviChunkPos == 0)
2440 AVIFILE_ComputeMoviStart(This->paf);
2442 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2443 return AVIERR_FILEWRITE;
2445 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2446 return AVIERR_FILEWRITE;
2447 if (buffer != NULL && size > 0) {
2448 if (mmioWrite(This->paf->hmmio, (HPSTR)buffer, size) != size)
2449 return AVIERR_FILEWRITE;
2451 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2452 return AVIERR_FILEWRITE;
2454 This->paf->fDirty = TRUE;
2455 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2457 return AVIFILE_AddFrame(This, ckid, size, ck.dwDataOffset, flags);