advapi32/tests: Changed an invalid comment.
[wine.git] / dlls / avifil32 / avifile.c
blob6295c7b2fb9a6fda62203aecb701f7c2e04a336b
1 /*
2 * Copyright 1999 Marcus Meissner
3 * Copyright 2002-2003 Michael Günnewig
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 /* TODO:
21 * - IAVIStreaming interface is missing for the IAVIStreamImpl
22 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
23 * - IAVIStream_fnReadFormat: formatchanges aren't read in.
24 * - IAVIStream_fnDelete: a stub.
25 * - IAVIStream_fnSetInfo: a stub.
26 * - make thread safe
28 * KNOWN Bugs:
29 * - native version can hangup when reading a file generated with this DLL.
30 * When index is missing it works, but index seems to be okay.
33 #define COBJMACROS
34 #include <assert.h>
35 #include <stdarg.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winnls.h"
42 #include "winerror.h"
43 #include "mmsystem.h"
44 #include "vfw.h"
46 #include "avifile_private.h"
47 #include "extrachunk.h"
49 #include "wine/unicode.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
54 #ifndef IDX_PER_BLOCK
55 #define IDX_PER_BLOCK 2730
56 #endif
58 typedef struct _IAVIFileImpl IAVIFileImpl;
60 typedef struct _IAVIStreamImpl {
61 IAVIStream IAVIStream_iface;
62 LONG ref;
64 IAVIFileImpl *paf;
65 DWORD nStream; /* the n-th stream in file */
66 AVISTREAMINFOW sInfo;
68 LPVOID lpFormat;
69 DWORD cbFormat;
71 LPVOID lpHandlerData;
72 DWORD cbHandlerData;
74 EXTRACHUNKS extra;
76 LPDWORD lpBuffer;
77 DWORD cbBuffer; /* size of lpBuffer */
78 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
80 LONG lLastFrame; /* last correct index in idxFrames */
81 AVIINDEXENTRY *idxFrames;
82 DWORD nIdxFrames; /* upper index limit of idxFrames */
83 AVIINDEXENTRY *idxFmtChanges;
84 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
85 } IAVIStreamImpl;
87 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
89 return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
92 struct _IAVIFileImpl {
93 IUnknown IUnknown_inner;
94 IAVIFile IAVIFile_iface;
95 IPersistFile IPersistFile_iface;
96 IUnknown *outer_unk;
97 LONG ref;
99 AVIFILEINFOW fInfo;
100 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
102 EXTRACHUNKS fileextra;
104 DWORD dwMoviChunkPos; /* some stuff for saving ... */
105 DWORD dwIdxChunkPos;
106 DWORD dwNextFramePos;
107 DWORD dwInitialFrames;
109 MMCKINFO ckLastRecord;
110 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
111 DWORD nIdxRecords; /* current fill level */
112 DWORD cbIdxRecords; /* size of idxRecords */
114 /* IPersistFile stuff ... */
115 HMMIO hmmio;
116 LPWSTR szFileName;
117 UINT uMode;
118 BOOL fDirty;
121 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
123 return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
126 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
128 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
131 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
133 return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
136 /***********************************************************************/
138 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
139 DWORD offset, DWORD flags);
140 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
141 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
142 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
143 const AVISTREAMINFOW *asi);
144 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
145 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
146 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
147 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
148 LONG count, DWORD pos, BOOL *bAbsolute);
149 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
150 LPVOID buffer, DWORD size);
151 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
152 LPLONG offset);
153 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
154 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
155 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
156 LONG lSkip);
157 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
158 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
159 FOURCC ckid, DWORD flags, LPCVOID buffer,
160 LONG size);
162 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
164 IAVIFileImpl *This = impl_from_IUnknown(iface);
166 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
168 if (!ppv) {
169 WARN("invalid parameter\n");
170 return E_INVALIDARG;
172 *ppv = NULL;
174 if (IsEqualIID(riid, &IID_IUnknown))
175 *ppv = &This->IUnknown_inner;
176 else if (IsEqualIID(riid, &IID_IAVIFile))
177 *ppv = &This->IAVIFile_iface;
178 else if (IsEqualGUID(riid, &IID_IPersistFile))
179 *ppv = &This->IPersistFile_iface;
180 else {
181 WARN("unknown IID %s\n", debugstr_guid(riid));
182 return E_NOINTERFACE;
185 /* Violation of the COM aggregation ref counting rule */
186 IUnknown_AddRef(&This->IUnknown_inner);
187 return S_OK;
190 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
192 IAVIFileImpl *This = impl_from_IUnknown(iface);
193 ULONG ref = InterlockedIncrement(&This->ref);
195 TRACE("(%p) ref=%d\n", This, ref);
197 return ref;
200 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
202 IAVIFileImpl *This = impl_from_IUnknown(iface);
203 ULONG ref = InterlockedDecrement(&This->ref);
204 UINT i;
206 TRACE("(%p) ref=%d\n", This, ref);
208 if (!ref) {
209 if (This->fDirty)
210 AVIFILE_SaveFile(This);
212 for (i = 0; i < This->fInfo.dwStreams; i++) {
213 if (This->ppStreams[i] != NULL) {
214 if (This->ppStreams[i]->ref != 0)
215 ERR(": someone has still %u reference to stream %u (%p)!\n",
216 This->ppStreams[i]->ref, i, This->ppStreams[i]);
217 AVIFILE_DestructAVIStream(This->ppStreams[i]);
218 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
219 This->ppStreams[i] = NULL;
223 if (This->idxRecords != NULL) {
224 HeapFree(GetProcessHeap(), 0, This->idxRecords);
225 This->idxRecords = NULL;
226 This->nIdxRecords = 0;
229 if (This->fileextra.lp != NULL) {
230 HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
231 This->fileextra.lp = NULL;
232 This->fileextra.cb = 0;
235 HeapFree(GetProcessHeap(), 0, This->szFileName);
236 This->szFileName = NULL;
238 if (This->hmmio != NULL) {
239 mmioClose(This->hmmio, 0);
240 This->hmmio = NULL;
243 HeapFree(GetProcessHeap(), 0, This);
245 return ref;
248 static const IUnknownVtbl unk_vtbl =
250 IUnknown_fnQueryInterface,
251 IUnknown_fnAddRef,
252 IUnknown_fnRelease
255 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
257 IAVIFileImpl *This = impl_from_IAVIFile(iface);
259 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
262 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
264 IAVIFileImpl *This = impl_from_IAVIFile(iface);
266 return IUnknown_AddRef(This->outer_unk);
269 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
271 IAVIFileImpl *This = impl_from_IAVIFile(iface);
273 return IUnknown_Release(This->outer_unk);
276 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
278 IAVIFileImpl *This = impl_from_IAVIFile(iface);
280 TRACE("(%p,%p,%d)\n",iface,afi,size);
282 if (afi == NULL)
283 return AVIERR_BADPARAM;
284 if (size < 0)
285 return AVIERR_BADSIZE;
287 AVIFILE_UpdateInfo(This);
289 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
291 if ((DWORD)size < sizeof(This->fInfo))
292 return AVIERR_BUFFERTOOSMALL;
293 return AVIERR_OK;
296 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
297 LONG lParam)
299 IAVIFileImpl *This = impl_from_IAVIFile(iface);
300 ULONG nStream;
302 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
304 if (avis == NULL || lParam < 0)
305 return AVIERR_BADPARAM;
307 nStream = AVIFILE_SearchStream(This, fccType, lParam);
309 /* Does the requested stream exist? */
310 if (nStream < This->fInfo.dwStreams &&
311 This->ppStreams[nStream] != NULL) {
312 *avis = &This->ppStreams[nStream]->IAVIStream_iface;
313 IAVIStream_AddRef(*avis);
315 return AVIERR_OK;
318 /* Sorry, but the specified stream doesn't exist */
319 return AVIERR_NODATA;
322 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
323 AVISTREAMINFOW *asi)
325 IAVIFileImpl *This = impl_from_IAVIFile(iface);
326 DWORD n;
328 TRACE("(%p,%p,%p)\n", iface, avis, asi);
330 /* check parameters */
331 if (avis == NULL || asi == NULL)
332 return AVIERR_BADPARAM;
334 *avis = NULL;
336 /* Does the user have write permission? */
337 if ((This->uMode & MMIO_RWMODE) == 0)
338 return AVIERR_READONLY;
340 /* Can we add another stream? */
341 n = This->fInfo.dwStreams;
342 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
343 /* already reached max nr of streams
344 * or have already written frames to disk */
345 return AVIERR_UNSUPPORTED;
348 /* check AVISTREAMINFO for some really needed things */
349 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
350 return AVIERR_BADFORMAT;
352 /* now it seems to be save to add the stream */
353 assert(This->ppStreams[n] == NULL);
354 This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
355 sizeof(IAVIStreamImpl));
356 if (This->ppStreams[n] == NULL)
357 return AVIERR_MEMORY;
359 /* initialize the new allocated stream */
360 AVIFILE_ConstructAVIStream(This, n, asi);
362 This->fInfo.dwStreams++;
363 This->fDirty = TRUE;
365 /* update our AVIFILEINFO structure */
366 AVIFILE_UpdateInfo(This);
368 /* return it */
369 *avis = &This->ppStreams[n]->IAVIStream_iface;
370 IAVIStream_AddRef(*avis);
372 return AVIERR_OK;
375 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
377 IAVIFileImpl *This = impl_from_IAVIFile(iface);
379 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
381 /* check parameters */
382 if (lpData == NULL)
383 return AVIERR_BADPARAM;
384 if (size < 0)
385 return AVIERR_BADSIZE;
387 /* Do we have write permission? */
388 if ((This->uMode & MMIO_RWMODE) == 0)
389 return AVIERR_READONLY;
391 This->fDirty = TRUE;
393 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
396 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
398 IAVIFileImpl *This = impl_from_IAVIFile(iface);
400 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
402 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
405 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
407 IAVIFileImpl *This = impl_from_IAVIFile(iface);
409 TRACE("(%p)\n",iface);
411 if ((This->uMode & MMIO_RWMODE) == 0)
412 return AVIERR_READONLY;
414 This->fDirty = TRUE;
416 /* no frames written to any stream? -- compute start of 'movi'-chunk */
417 if (This->dwMoviChunkPos == 0)
418 AVIFILE_ComputeMoviStart(This);
420 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
422 /* already written frames to any stream, ... */
423 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
424 /* close last record */
425 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
426 return AVIERR_FILEWRITE;
428 AVIFILE_AddRecord(This);
430 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
431 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
434 /* write out a new record into file, but don't close it */
435 This->ckLastRecord.cksize = 0;
436 This->ckLastRecord.fccType = listtypeAVIRECORD;
437 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
438 return AVIERR_FILEWRITE;
439 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
440 return AVIERR_FILEWRITE;
441 This->dwNextFramePos += 3 * sizeof(DWORD);
443 return AVIERR_OK;
446 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
448 IAVIFileImpl *This = impl_from_IAVIFile(iface);
449 ULONG nStream;
451 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
453 /* check parameter */
454 if (lParam < 0)
455 return AVIERR_BADPARAM;
457 /* Have user write permissions? */
458 if ((This->uMode & MMIO_RWMODE) == 0)
459 return AVIERR_READONLY;
461 nStream = AVIFILE_SearchStream(This, fccType, lParam);
463 /* Does the requested stream exist? */
464 if (nStream < This->fInfo.dwStreams &&
465 This->ppStreams[nStream] != NULL) {
466 /* ... so delete it now */
467 HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
469 if (This->fInfo.dwStreams - nStream > 0)
470 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
471 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
473 This->ppStreams[This->fInfo.dwStreams] = NULL;
474 This->fInfo.dwStreams--;
475 This->fDirty = TRUE;
477 /* This->fInfo will be updated further when asked for */
478 return AVIERR_OK;
479 } else
480 return AVIERR_NODATA;
483 static const struct IAVIFileVtbl avif_vt = {
484 IAVIFile_fnQueryInterface,
485 IAVIFile_fnAddRef,
486 IAVIFile_fnRelease,
487 IAVIFile_fnInfo,
488 IAVIFile_fnGetStream,
489 IAVIFile_fnCreateStream,
490 IAVIFile_fnWriteData,
491 IAVIFile_fnReadData,
492 IAVIFile_fnEndRecord,
493 IAVIFile_fnDeleteStream
497 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
499 IAVIFileImpl *This = impl_from_IPersistFile(iface);
501 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
504 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
506 IAVIFileImpl *This = impl_from_IPersistFile(iface);
508 return IUnknown_AddRef(This->outer_unk);
511 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
513 IAVIFileImpl *This = impl_from_IPersistFile(iface);
515 return IUnknown_Release(This->outer_unk);
518 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
520 TRACE("(%p,%p)\n", iface, pClassID);
522 if (pClassID == NULL)
523 return AVIERR_BADPARAM;
525 *pClassID = CLSID_AVIFile;
527 return AVIERR_OK;
530 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
532 IAVIFileImpl *This = impl_from_IPersistFile(iface);
534 TRACE("(%p)\n", iface);
536 return (This->fDirty ? S_OK : S_FALSE);
539 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
541 IAVIFileImpl *This = impl_from_IPersistFile(iface);
542 int len;
544 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
546 /* check parameter */
547 if (pszFileName == NULL)
548 return AVIERR_BADPARAM;
550 if (This->hmmio != NULL)
551 return AVIERR_ERROR; /* No reuse of this object for another file! */
553 /* remember mode and name */
554 This->uMode = dwMode;
556 len = lstrlenW(pszFileName) + 1;
557 This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
558 if (This->szFileName == NULL)
559 return AVIERR_MEMORY;
560 lstrcpyW(This->szFileName, pszFileName);
562 /* try to open the file */
563 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
564 if (This->hmmio == NULL) {
565 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
566 LPSTR szFileName;
568 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
569 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
570 if (szFileName == NULL)
571 return AVIERR_MEMORY;
573 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
575 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
576 HeapFree(GetProcessHeap(), 0, szFileName);
577 if (This->hmmio == NULL)
578 return AVIERR_FILEOPEN;
581 /* should we create a new file? */
582 if (dwMode & OF_CREATE) {
583 memset(& This->fInfo, 0, sizeof(This->fInfo));
584 This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
586 return AVIERR_OK;
587 } else
588 return AVIFILE_LoadFile(This);
591 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
592 BOOL fRemember)
594 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
596 /* We write directly to disk, so nothing to do. */
598 return AVIERR_OK;
601 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
603 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
605 /* We write directly to disk, so nothing to do. */
607 return AVIERR_OK;
610 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
612 IAVIFileImpl *This = impl_from_IPersistFile(iface);
614 TRACE("(%p,%p)\n", iface, ppszFileName);
616 if (ppszFileName == NULL)
617 return AVIERR_BADPARAM;
619 *ppszFileName = NULL;
621 if (This->szFileName != NULL) {
622 int len = lstrlenW(This->szFileName) + 1;
624 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
625 if (*ppszFileName == NULL)
626 return AVIERR_MEMORY;
628 strcpyW(*ppszFileName, This->szFileName);
631 return AVIERR_OK;
634 static const struct IPersistFileVtbl pf_vt = {
635 IPersistFile_fnQueryInterface,
636 IPersistFile_fnAddRef,
637 IPersistFile_fnRelease,
638 IPersistFile_fnGetClassID,
639 IPersistFile_fnIsDirty,
640 IPersistFile_fnLoad,
641 IPersistFile_fnSave,
642 IPersistFile_fnSaveCompleted,
643 IPersistFile_fnGetCurFile
646 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
648 IAVIFileImpl *obj;
649 HRESULT hr;
651 *ppv = NULL;
652 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
653 if (!obj)
654 return AVIERR_MEMORY;
656 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
657 obj->IAVIFile_iface.lpVtbl = &avif_vt;
658 obj->IPersistFile_iface.lpVtbl = &pf_vt;
659 obj->ref = 1;
660 if (pUnkOuter)
661 obj->outer_unk = pUnkOuter;
662 else
663 obj->outer_unk = &obj->IUnknown_inner;
665 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
666 IUnknown_Release(&obj->IUnknown_inner);
668 return hr;
672 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
674 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
676 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
678 if (!ppv) {
679 WARN("invalid parameter\n");
680 return E_INVALIDARG;
682 *ppv = NULL;
684 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
685 *ppv = iface;
686 IAVIStream_AddRef(iface);
688 return S_OK;
690 /* FIXME: IAVIStreaming interface */
692 return E_NOINTERFACE;
695 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
697 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
698 ULONG ref = InterlockedIncrement(&This->ref);
700 TRACE("(%p) ref=%d\n", This, ref);
702 /* also add ref to parent, so that it doesn't kill us */
703 if (This->paf != NULL)
704 IAVIFile_AddRef(&This->paf->IAVIFile_iface);
706 return ref;
709 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
711 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
712 ULONG ref = InterlockedDecrement(&This->ref);
714 TRACE("(%p) ref=%d\n", This, ref);
716 if (This->paf != NULL)
717 IAVIFile_Release(&This->paf->IAVIFile_iface);
719 return ref;
722 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
724 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
726 /* This IAVIStream interface needs an AVIFile */
727 return AVIERR_UNSUPPORTED;
730 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
732 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
734 TRACE("(%p,%p,%d)\n", iface, psi, size);
736 if (psi == NULL)
737 return AVIERR_BADPARAM;
738 if (size < 0)
739 return AVIERR_BADSIZE;
741 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
743 if ((DWORD)size < sizeof(This->sInfo))
744 return AVIERR_BUFFERTOOSMALL;
745 return AVIERR_OK;
748 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
750 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
751 LONG offset = 0;
753 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
755 if (flags & FIND_FROM_START) {
756 pos = This->sInfo.dwStart;
757 flags &= ~(FIND_FROM_START|FIND_PREV);
758 flags |= FIND_NEXT;
761 if (This->sInfo.dwSampleSize != 0) {
762 /* convert samples into block number with offset */
763 AVIFILE_SamplesToBlock(This, &pos, &offset);
766 if (flags & FIND_TYPE) {
767 if (flags & FIND_KEY) {
768 while (0 <= pos && pos <= This->lLastFrame) {
769 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
770 goto RETURN_FOUND;
772 if (flags & FIND_NEXT)
773 pos++;
774 else
775 pos--;
777 } else if (flags & FIND_ANY) {
778 while (0 <= pos && pos <= This->lLastFrame) {
779 if (This->idxFrames[pos].dwChunkLength > 0)
780 goto RETURN_FOUND;
782 if (flags & FIND_NEXT)
783 pos++;
784 else
785 pos--;
788 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
789 This->sInfo.fccType == streamtypeVIDEO) {
790 if (flags & FIND_NEXT) {
791 ULONG n;
793 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
794 if (This->idxFmtChanges[n].ckid >= pos) {
795 pos = This->idxFmtChanges[n].ckid;
796 goto RETURN_FOUND;
798 } else {
799 LONG n;
801 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
802 if (This->idxFmtChanges[n].ckid <= pos) {
803 pos = This->idxFmtChanges[n].ckid;
804 goto RETURN_FOUND;
808 if (pos > (LONG)This->sInfo.dwStart)
809 return 0; /* format changes always for first frame */
813 return -1;
816 RETURN_FOUND:
817 if (pos < (LONG)This->sInfo.dwStart)
818 return -1;
820 switch (flags & FIND_RET) {
821 case FIND_LENGTH:
822 /* physical size */
823 pos = This->idxFrames[pos].dwChunkLength;
824 break;
825 case FIND_OFFSET:
826 /* physical position */
827 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
828 + offset * This->sInfo.dwSampleSize;
829 break;
830 case FIND_SIZE:
831 /* logical size */
832 if (This->sInfo.dwSampleSize)
833 pos = This->sInfo.dwSampleSize;
834 else
835 pos = 1;
836 break;
837 case FIND_INDEX:
838 FIXME(": FIND_INDEX flag is not supported!\n");
839 /* This is an index in the index-table on disc. */
840 break;
841 }; /* else logical position */
843 return pos;
846 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
847 LONG *formatsize)
849 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
851 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
853 if (formatsize == NULL)
854 return AVIERR_BADPARAM;
856 /* only interested in needed buffersize? */
857 if (format == NULL || *formatsize <= 0) {
858 *formatsize = This->cbFormat;
860 return AVIERR_OK;
863 /* copy initial format (only as much as will fit) */
864 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
865 if (*(DWORD*)formatsize < This->cbFormat) {
866 *formatsize = This->cbFormat;
867 return AVIERR_BUFFERTOOSMALL;
870 /* Could format change? When yes will it change? */
871 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
872 pos > This->sInfo.dwStart) {
873 LONG lLastFmt;
875 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
876 if (lLastFmt > 0) {
877 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
881 *formatsize = This->cbFormat;
882 return AVIERR_OK;
885 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
886 LONG formatsize)
888 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
889 BITMAPINFOHEADER *lpbiNew = format;
891 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
893 /* check parameters */
894 if (format == NULL || formatsize <= 0)
895 return AVIERR_BADPARAM;
897 /* Do we have write permission? */
898 if ((This->paf->uMode & MMIO_RWMODE) == 0)
899 return AVIERR_READONLY;
901 /* can only set format before frame is written! */
902 if (This->lLastFrame > pos)
903 return AVIERR_UNSUPPORTED;
905 /* initial format or a formatchange? */
906 if (This->lpFormat == NULL) {
907 /* initial format */
908 if (This->paf->dwMoviChunkPos != 0)
909 return AVIERR_ERROR; /* user has used API in wrong sequence! */
911 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
912 if (This->lpFormat == NULL)
913 return AVIERR_MEMORY;
914 This->cbFormat = formatsize;
916 memcpy(This->lpFormat, format, formatsize);
918 /* update some infos about stream */
919 if (This->sInfo.fccType == streamtypeVIDEO) {
920 LONG lDim;
922 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
923 if (lDim < lpbiNew->biWidth)
924 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
925 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
926 if (lDim < lpbiNew->biHeight)
927 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
928 } else if (This->sInfo.fccType == streamtypeAUDIO)
929 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
931 return AVIERR_OK;
932 } else {
933 MMCKINFO ck;
934 LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
935 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
936 AVIPALCHANGE *lppc = NULL;
937 UINT n;
939 /* perhaps format change, check it ... */
940 if (This->cbFormat != formatsize)
941 return AVIERR_UNSUPPORTED;
943 /* no format change, only the initial one */
944 if (memcmp(This->lpFormat, format, formatsize) == 0)
945 return AVIERR_OK;
947 /* check that's only the palette, which changes */
948 if (lpbiOld->biSize != lpbiNew->biSize ||
949 lpbiOld->biWidth != lpbiNew->biWidth ||
950 lpbiOld->biHeight != lpbiNew->biHeight ||
951 lpbiOld->biPlanes != lpbiNew->biPlanes ||
952 lpbiOld->biBitCount != lpbiNew->biBitCount ||
953 lpbiOld->biCompression != lpbiNew->biCompression ||
954 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
955 return AVIERR_UNSUPPORTED;
957 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
959 /* simply say all colors have changed */
960 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
961 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
962 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
963 if (lppc == NULL)
964 return AVIERR_MEMORY;
966 lppc->bFirstEntry = 0;
967 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
968 lppc->wFlags = 0;
969 for (n = 0; n < lpbiOld->biClrUsed; n++) {
970 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
971 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
972 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
973 lppc->peNew[n].peFlags = 0;
976 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
977 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
978 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
979 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
981 HeapFree(GetProcessHeap(), 0, lppc);
982 return AVIERR_FILEWRITE;
985 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
987 HeapFree(GetProcessHeap(), 0, lppc);
989 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
993 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
994 LONG buffersize, LONG *bytesread, LONG *samplesread)
996 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
997 DWORD size;
998 HRESULT hr;
1000 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
1001 buffersize, bytesread, samplesread);
1003 /* clear return parameters if given */
1004 if (bytesread != NULL)
1005 *bytesread = 0;
1006 if (samplesread != NULL)
1007 *samplesread = 0;
1009 /* check parameters */
1010 if ((LONG)This->sInfo.dwStart > start)
1011 return AVIERR_NODATA; /* couldn't read before start of stream */
1012 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1013 return AVIERR_NODATA; /* start is past end of stream */
1015 /* should we read as much as possible? */
1016 if (samples == -1) {
1017 /* User should know how much we have read */
1018 if (bytesread == NULL && samplesread == NULL)
1019 return AVIERR_BADPARAM;
1021 if (This->sInfo.dwSampleSize != 0)
1022 samples = buffersize / This->sInfo.dwSampleSize;
1023 else
1024 samples = 1;
1027 /* limit to end of stream */
1028 if ((LONG)This->sInfo.dwLength < samples)
1029 samples = This->sInfo.dwLength;
1030 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1031 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1033 /* nothing to read? Then leave ... */
1034 if (samples == 0)
1035 return AVIERR_OK;
1037 if (This->sInfo.dwSampleSize != 0) {
1038 /* fixed samplesize -- we can read over frame/block boundaries */
1039 LONG block = start;
1040 LONG offset = 0;
1042 if (!buffer)
1044 if (bytesread)
1045 *bytesread = samples*This->sInfo.dwSampleSize;
1046 if (samplesread)
1047 *samplesread = samples;
1048 return AVIERR_OK;
1051 /* convert start sample to block,offset pair */
1052 AVIFILE_SamplesToBlock(This, &block, &offset);
1054 /* convert samples to bytes */
1055 samples *= This->sInfo.dwSampleSize;
1057 while (samples > 0 && buffersize > 0) {
1058 LONG blocksize;
1059 if (block != This->dwCurrentFrame) {
1060 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1061 if (FAILED(hr))
1062 return hr;
1065 size = min((DWORD)samples, (DWORD)buffersize);
1066 blocksize = This->lpBuffer[1];
1067 TRACE("blocksize = %u\n",blocksize);
1068 size = min(size, blocksize - offset);
1069 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1071 block++;
1072 offset = 0;
1073 buffer = ((LPBYTE)buffer)+size;
1074 samples -= size;
1075 buffersize -= size;
1077 /* fill out return parameters if given */
1078 if (bytesread != NULL)
1079 *bytesread += size;
1080 if (samplesread != NULL)
1081 *samplesread += size / This->sInfo.dwSampleSize;
1084 if (samples == 0)
1085 return AVIERR_OK;
1086 else
1087 return AVIERR_BUFFERTOOSMALL;
1088 } else {
1089 /* variable samplesize -- we can only read one full frame/block */
1090 if (samples > 1)
1091 samples = 1;
1093 assert(start <= This->lLastFrame);
1094 size = This->idxFrames[start].dwChunkLength;
1095 if (buffer != NULL && buffersize >= size) {
1096 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1097 if (FAILED(hr))
1098 return hr;
1099 } else if (buffer != NULL)
1100 return AVIERR_BUFFERTOOSMALL;
1102 /* fill out return parameters if given */
1103 if (bytesread != NULL)
1104 *bytesread = size;
1105 if (samplesread != NULL)
1106 *samplesread = samples;
1108 return AVIERR_OK;
1112 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
1113 LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1115 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1116 FOURCC ckid;
1117 HRESULT hr;
1119 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1120 buffer, buffersize, flags, sampwritten, byteswritten);
1122 /* clear return parameters if given */
1123 if (sampwritten != NULL)
1124 *sampwritten = 0;
1125 if (byteswritten != NULL)
1126 *byteswritten = 0;
1128 /* check parameters */
1129 if (buffer == NULL && (buffersize > 0 || samples > 0))
1130 return AVIERR_BADPARAM;
1132 /* Have we write permission? */
1133 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1134 return AVIERR_READONLY;
1136 switch (This->sInfo.fccType) {
1137 case streamtypeAUDIO:
1138 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1139 break;
1140 default:
1141 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1142 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1143 else
1144 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1145 break;
1148 /* append to end of stream? */
1149 if (start == -1) {
1150 if (This->lLastFrame == -1)
1151 start = This->sInfo.dwStart;
1152 else
1153 start = This->sInfo.dwLength;
1154 } else if (This->lLastFrame == -1)
1155 This->sInfo.dwStart = start;
1157 if (This->sInfo.dwSampleSize != 0) {
1158 /* fixed sample size -- audio like */
1159 if (samples * This->sInfo.dwSampleSize != buffersize)
1160 return AVIERR_BADPARAM;
1162 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1163 if (This->sInfo.dwLength != start)
1164 return AVIERR_UNSUPPORTED;
1166 /* Convert position to frame/block */
1167 start = This->lLastFrame + 1;
1169 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1170 FIXME(": not interleaved, could collect audio data!\n");
1172 } else {
1173 /* variable sample size -- video like */
1174 if (samples > 1)
1175 return AVIERR_UNSUPPORTED;
1177 /* must we fill up with empty frames? */
1178 if (This->lLastFrame != -1) {
1179 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1181 while (start > This->lLastFrame + 1) {
1182 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1183 if (FAILED(hr))
1184 return hr;
1189 /* write the block now */
1190 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1191 if (SUCCEEDED(hr)) {
1192 /* fill out return parameters if given */
1193 if (sampwritten != NULL)
1194 *sampwritten = samples;
1195 if (byteswritten != NULL)
1196 *byteswritten = buffersize;
1199 return hr;
1202 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1204 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1206 FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1208 /* check parameters */
1209 if (start < 0 || samples < 0)
1210 return AVIERR_BADPARAM;
1212 /* Delete before start of stream? */
1213 if (start + samples < This->sInfo.dwStart)
1214 return AVIERR_OK;
1216 /* Delete after end of stream? */
1217 if (start > This->sInfo.dwLength)
1218 return AVIERR_OK;
1220 /* For the rest we need write permissions */
1221 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1222 return AVIERR_READONLY;
1224 /* 1. overwrite the data with JUNK
1226 * if ISINTERLEAVED {
1227 * 2. concat all neighboured JUNK-blocks in this record to one
1228 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1229 * to start of this record, repeat this.
1230 * } else {
1231 * 2. concat all neighboured JUNK-blocks.
1232 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1233 * start of this block.
1237 return AVIERR_UNSUPPORTED;
1240 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1242 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1244 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1246 if (fcc == ckidSTREAMHANDLERDATA) {
1247 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1248 if (lp == NULL || *lpread <= 0) {
1249 *lpread = This->cbHandlerData;
1250 return AVIERR_OK;
1253 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1254 if (*lpread < This->cbHandlerData)
1255 return AVIERR_BUFFERTOOSMALL;
1256 return AVIERR_OK;
1257 } else
1258 return AVIERR_NODATA;
1259 } else
1260 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1263 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1265 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1267 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1269 /* check parameters */
1270 if (lp == NULL)
1271 return AVIERR_BADPARAM;
1272 if (size <= 0)
1273 return AVIERR_BADSIZE;
1275 /* need write permission */
1276 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1277 return AVIERR_READONLY;
1279 /* already written something to this file? */
1280 if (This->paf->dwMoviChunkPos != 0) {
1281 /* the data will be inserted before the 'movi' chunk, so check for
1282 * enough space */
1283 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1285 /* ckid,size => 2 * sizeof(DWORD) */
1286 dwPos += 2 * sizeof(DWORD) + size;
1287 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1288 return AVIERR_UNSUPPORTED; /* not enough space left */
1291 This->paf->fDirty = TRUE;
1293 if (fcc == ckidSTREAMHANDLERDATA) {
1294 if (This->lpHandlerData != NULL) {
1295 FIXME(": handler data already set -- overwrite?\n");
1296 return AVIERR_UNSUPPORTED;
1299 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1300 if (This->lpHandlerData == NULL)
1301 return AVIERR_MEMORY;
1302 This->cbHandlerData = size;
1303 memcpy(This->lpHandlerData, lp, size);
1305 return AVIERR_OK;
1306 } else
1307 return WriteExtraChunk(&This->extra, fcc, lp, size);
1310 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1312 FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1314 return E_FAIL;
1317 static const struct IAVIStreamVtbl avist_vt = {
1318 IAVIStream_fnQueryInterface,
1319 IAVIStream_fnAddRef,
1320 IAVIStream_fnRelease,
1321 IAVIStream_fnCreate,
1322 IAVIStream_fnInfo,
1323 IAVIStream_fnFindSample,
1324 IAVIStream_fnReadFormat,
1325 IAVIStream_fnSetFormat,
1326 IAVIStream_fnRead,
1327 IAVIStream_fnWrite,
1328 IAVIStream_fnDelete,
1329 IAVIStream_fnReadData,
1330 IAVIStream_fnWriteData,
1331 IAVIStream_fnSetInfo
1335 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1337 UINT n;
1339 /* pre-conditions */
1340 assert(This != NULL);
1342 switch (TWOCCFromFOURCC(ckid)) {
1343 case cktypeDIBbits:
1344 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1345 flags |= AVIIF_KEYFRAME;
1346 break;
1347 case cktypeDIBcompressed:
1348 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1349 flags &= ~AVIIF_KEYFRAME;
1350 break;
1351 case cktypePALchange:
1352 if (This->sInfo.fccType != streamtypeVIDEO) {
1353 ERR(": found palette change in non-video stream!\n");
1354 return AVIERR_BADFORMAT;
1357 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1358 DWORD new_count = This->nIdxFmtChanges + 16;
1359 void *new_buffer;
1361 if (This->idxFmtChanges == NULL) {
1362 This->idxFmtChanges =
1363 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
1364 if (!This->idxFmtChanges) return AVIERR_MEMORY;
1365 } else {
1366 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
1367 new_count * sizeof(AVIINDEXENTRY));
1368 if (!new_buffer) return AVIERR_MEMORY;
1369 This->idxFmtChanges = new_buffer;
1371 This->nIdxFmtChanges = new_count;
1374 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1375 n = ++This->sInfo.dwFormatChangeCount;
1376 This->idxFmtChanges[n].ckid = This->lLastFrame;
1377 This->idxFmtChanges[n].dwFlags = 0;
1378 This->idxFmtChanges[n].dwChunkOffset = offset;
1379 This->idxFmtChanges[n].dwChunkLength = size;
1381 return AVIERR_OK;
1382 case cktypeWAVEbytes:
1383 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1384 flags |= AVIIF_KEYFRAME;
1385 break;
1386 default:
1387 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1388 break;
1391 /* first frame is always a keyframe */
1392 if (This->lLastFrame == -1)
1393 flags |= AVIIF_KEYFRAME;
1395 if (This->sInfo.dwSuggestedBufferSize < size)
1396 This->sInfo.dwSuggestedBufferSize = size;
1398 /* get memory for index */
1399 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1400 This->nIdxFrames += 512;
1401 if (This->idxFrames == NULL)
1402 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1403 else
1404 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1405 This->nIdxFrames * sizeof(AVIINDEXENTRY));
1406 if (This->idxFrames == NULL)
1407 return AVIERR_MEMORY;
1410 This->lLastFrame++;
1411 This->idxFrames[This->lLastFrame].ckid = ckid;
1412 This->idxFrames[This->lLastFrame].dwFlags = flags;
1413 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1414 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1416 /* update AVISTREAMINFO structure if necessary */
1417 if (This->sInfo.dwLength <= This->lLastFrame)
1418 This->sInfo.dwLength = This->lLastFrame + 1;
1420 return AVIERR_OK;
1423 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1425 /* pre-conditions */
1426 assert(This != NULL && This->ppStreams[0] != NULL);
1428 if (This->idxRecords == NULL || This->cbIdxRecords == 0) {
1429 This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY);
1430 This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords);
1431 if (This->idxRecords == NULL)
1432 return AVIERR_MEMORY;
1435 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1437 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1438 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1439 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1440 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1441 This->idxRecords[This->nIdxRecords].dwChunkLength =
1442 This->ckLastRecord.cksize;
1443 This->nIdxRecords++;
1445 return AVIERR_OK;
1448 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1450 DWORD dwPos;
1451 DWORD nStream;
1453 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1454 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1456 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1457 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1459 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1460 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1461 dwPos += ((pStream->cbFormat + 1) & ~1U);
1462 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1463 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1464 if (lstrlenW(pStream->sInfo.szName) > 0)
1465 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1468 if (This->dwMoviChunkPos == 0) {
1469 This->dwNextFramePos = dwPos;
1471 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1472 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1473 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1475 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1478 return dwPos;
1481 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1483 IAVIStreamImpl *pstream;
1485 /* pre-conditions */
1486 assert(paf != NULL);
1487 assert(nr < MAX_AVISTREAMS);
1488 assert(paf->ppStreams[nr] != NULL);
1490 pstream = paf->ppStreams[nr];
1492 pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1493 pstream->ref = 0;
1494 pstream->paf = paf;
1495 pstream->nStream = nr;
1496 pstream->dwCurrentFrame = (DWORD)-1;
1497 pstream->lLastFrame = -1;
1499 if (asi != NULL) {
1500 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1502 if (asi->dwLength > 0) {
1503 /* pre-allocate mem for frame-index structure */
1504 pstream->idxFrames =
1505 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1506 if (pstream->idxFrames != NULL)
1507 pstream->nIdxFrames = asi->dwLength;
1509 if (asi->dwFormatChangeCount > 0) {
1510 /* pre-allocate mem for formatchange-index structure */
1511 pstream->idxFmtChanges =
1512 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1513 if (pstream->idxFmtChanges != NULL)
1514 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1517 /* These values will be computed */
1518 pstream->sInfo.dwLength = 0;
1519 pstream->sInfo.dwSuggestedBufferSize = 0;
1520 pstream->sInfo.dwFormatChangeCount = 0;
1521 pstream->sInfo.dwEditCount = 1;
1522 if (pstream->sInfo.dwSampleSize > 0)
1523 SetRectEmpty(&pstream->sInfo.rcFrame);
1526 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1529 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1531 /* pre-conditions */
1532 assert(This != NULL);
1534 This->dwCurrentFrame = (DWORD)-1;
1535 This->lLastFrame = -1;
1536 This->paf = NULL;
1537 if (This->idxFrames != NULL) {
1538 HeapFree(GetProcessHeap(), 0, This->idxFrames);
1539 This->idxFrames = NULL;
1540 This->nIdxFrames = 0;
1542 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
1543 This->idxFmtChanges = NULL;
1544 if (This->lpBuffer != NULL) {
1545 HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1546 This->lpBuffer = NULL;
1547 This->cbBuffer = 0;
1549 if (This->lpHandlerData != NULL) {
1550 HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1551 This->lpHandlerData = NULL;
1552 This->cbHandlerData = 0;
1554 if (This->extra.lp != NULL) {
1555 HeapFree(GetProcessHeap(), 0, This->extra.lp);
1556 This->extra.lp = NULL;
1557 This->extra.cb = 0;
1559 if (This->lpFormat != NULL) {
1560 HeapFree(GetProcessHeap(), 0, This->lpFormat);
1561 This->lpFormat = NULL;
1562 This->cbFormat = 0;
1566 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1568 MainAVIHeader MainAVIHdr;
1569 MMCKINFO ckRIFF;
1570 MMCKINFO ckLIST1;
1571 MMCKINFO ckLIST2;
1572 MMCKINFO ck;
1573 IAVIStreamImpl *pStream;
1574 DWORD nStream;
1575 HRESULT hr;
1577 if (This->hmmio == NULL)
1578 return AVIERR_FILEOPEN;
1580 /* initialize stream ptr's */
1581 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1583 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1584 ckRIFF.fccType = formtypeAVI;
1585 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1586 ERR(": not an AVI!\n");
1587 return AVIERR_FILEREAD;
1590 /* get "LIST" "hdrl" */
1591 ckLIST1.fccType = listtypeAVIHEADER;
1592 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1593 if (FAILED(hr))
1594 return hr;
1596 /* get "avih" chunk */
1597 ck.ckid = ckidAVIMAINHDR;
1598 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1599 if (FAILED(hr))
1600 return hr;
1602 if (ck.cksize != sizeof(MainAVIHdr)) {
1603 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1604 return AVIERR_BADFORMAT;
1606 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1607 return AVIERR_FILEREAD;
1609 /* check for MAX_AVISTREAMS limit */
1610 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1611 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1612 return AVIERR_UNSUPPORTED;
1615 /* adjust permissions if copyrighted material in file */
1616 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1617 This->uMode &= ~MMIO_RWMODE;
1618 This->uMode |= MMIO_READ;
1621 /* convert MainAVIHeader into AVIFILINFOW */
1622 memset(&This->fInfo, 0, sizeof(This->fInfo));
1623 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1624 This->fInfo.dwScale = 1000000;
1625 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1626 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1627 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1628 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1629 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1630 This->fInfo.dwSuggestedBufferSize = 0;
1631 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1632 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1633 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1634 sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
1636 /* go back to into header list */
1637 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1638 return AVIERR_FILEREAD;
1640 /* foreach stream exists a "LIST","strl" chunk */
1641 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1642 /* get next nested chunk in this "LIST","strl" */
1643 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1644 return AVIERR_FILEREAD;
1646 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1647 if (ckLIST2.ckid == FOURCC_LIST &&
1648 ckLIST2.fccType == listtypeSTREAMHEADER) {
1649 pStream = This->ppStreams[nStream] =
1650 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1651 if (pStream == NULL)
1652 return AVIERR_MEMORY;
1653 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1655 ck.ckid = 0;
1656 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1657 switch (ck.ckid) {
1658 case ckidSTREAMHANDLERDATA:
1659 if (pStream->lpHandlerData != NULL)
1660 return AVIERR_BADFORMAT;
1661 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1662 if (pStream->lpHandlerData == NULL)
1663 return AVIERR_MEMORY;
1664 pStream->cbHandlerData = ck.cksize;
1666 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1667 return AVIERR_FILEREAD;
1668 break;
1669 case ckidSTREAMFORMAT:
1670 if (pStream->lpFormat != NULL)
1671 return AVIERR_BADFORMAT;
1672 if (ck.cksize == 0)
1673 break;
1675 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1676 if (pStream->lpFormat == NULL)
1677 return AVIERR_MEMORY;
1678 pStream->cbFormat = ck.cksize;
1680 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1681 return AVIERR_FILEREAD;
1683 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1684 LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1686 /* some corrections to the video format */
1687 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1688 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1689 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1690 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1691 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1692 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1693 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1694 lpbi->biCompression = BI_RLE8;
1696 if (lpbi->biCompression == BI_RGB &&
1697 (pStream->sInfo.fccHandler == 0 ||
1698 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1699 pStream->sInfo.fccHandler = comptypeDIB;
1701 /* init rcFrame if it's empty */
1702 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1703 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1705 break;
1706 case ckidSTREAMHEADER:
1708 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1709 static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
1711 AVIStreamHeader streamHdr;
1712 WCHAR szType[25];
1713 UINT count;
1714 LONG n = ck.cksize;
1716 if (ck.cksize > sizeof(streamHdr))
1717 n = sizeof(streamHdr);
1719 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1720 return AVIERR_FILEREAD;
1722 pStream->sInfo.fccType = streamHdr.fccType;
1723 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1724 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1725 pStream->sInfo.wPriority = streamHdr.wPriority;
1726 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1727 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1728 pStream->sInfo.dwScale = streamHdr.dwScale;
1729 pStream->sInfo.dwRate = streamHdr.dwRate;
1730 pStream->sInfo.dwStart = streamHdr.dwStart;
1731 pStream->sInfo.dwLength = streamHdr.dwLength;
1732 pStream->sInfo.dwSuggestedBufferSize = 0;
1733 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1734 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1735 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1736 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1737 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1738 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1739 pStream->sInfo.dwEditCount = 0;
1740 pStream->sInfo.dwFormatChangeCount = 0;
1742 /* generate description for stream like "filename.avi Type #n" */
1743 if (streamHdr.fccType == streamtypeVIDEO)
1744 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
1745 else if (streamHdr.fccType == streamtypeAUDIO)
1746 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
1747 else
1748 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1750 /* get count of this streamtype up to this stream */
1751 count = 0;
1752 for (n = nStream; 0 <= n; n--) {
1753 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1754 count++;
1757 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1759 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1760 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1761 AVIFILE_BasenameW(This->szFileName), szType, count);
1763 break;
1764 case ckidSTREAMNAME:
1765 { /* streamname will be saved as ASCII string */
1766 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1767 if (str == NULL)
1768 return AVIERR_MEMORY;
1770 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1772 HeapFree(GetProcessHeap(), 0, str);
1773 return AVIERR_FILEREAD;
1776 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1777 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1779 HeapFree(GetProcessHeap(), 0, str);
1781 break;
1782 case ckidAVIPADDING:
1783 case mmioFOURCC('p','a','d','d'):
1784 break;
1785 default:
1786 WARN(": found extra chunk 0x%08X\n", ck.ckid);
1787 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1788 if (FAILED(hr))
1789 return hr;
1791 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1793 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1794 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1795 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1796 pStream->sInfo.dwScale = 1;
1797 pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1799 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1800 return AVIERR_FILEREAD;
1802 } else {
1803 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1804 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1805 if (FAILED(hr))
1806 return hr;
1808 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1809 return AVIERR_FILEREAD;
1812 /* read any extra headers in "LIST","hdrl" */
1813 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1814 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1815 return AVIERR_FILEREAD;
1817 /* search "LIST","movi" chunk in "RIFF","AVI " */
1818 ckLIST1.fccType = listtypeAVIMOVIE;
1819 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1820 MMIO_FINDLIST);
1821 if (FAILED(hr))
1822 return hr;
1824 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1825 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1826 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1827 return AVIERR_FILEREAD;
1829 /* try to find an index */
1830 ck.ckid = ckidAVINEWINDEX;
1831 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1832 &ck, &ckRIFF, MMIO_FINDCHUNK);
1833 if (SUCCEEDED(hr) && ck.cksize > 0) {
1834 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1835 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1838 /* when we haven't found an index or it's bad, then build one
1839 * by parsing 'movi' chunk */
1840 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1841 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1842 This->ppStreams[nStream]->lLastFrame = -1;
1844 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1845 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1846 return AVIERR_FILEREAD;
1849 /* seek through the 'movi' list until end */
1850 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1851 if (ck.ckid != FOURCC_LIST) {
1852 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1853 nStream = StreamFromFOURCC(ck.ckid);
1855 if (nStream > This->fInfo.dwStreams)
1856 return AVIERR_BADFORMAT;
1858 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1859 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1860 } else {
1861 nStream = StreamFromFOURCC(ck.ckid);
1862 WARN(": file seems to be truncated!\n");
1863 if (nStream <= This->fInfo.dwStreams &&
1864 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1865 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1866 if (ck.cksize != -1) {
1867 ck.cksize -= ck.dwDataOffset;
1868 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1870 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1871 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1879 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1881 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1882 if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1883 This->fInfo.dwSuggestedBufferSize = sugbuf;
1886 /* find other chunks */
1887 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1889 return AVIERR_OK;
1892 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1894 AVIINDEXENTRY *lp;
1895 DWORD pos, n;
1896 HRESULT hr = AVIERR_OK;
1897 BOOL bAbsolute = TRUE;
1899 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1900 if (lp == NULL)
1901 return AVIERR_MEMORY;
1903 /* adjust limits for index tables, so that inserting will be faster */
1904 for (n = 0; n < This->fInfo.dwStreams; n++) {
1905 IAVIStreamImpl *pStream = This->ppStreams[n];
1907 pStream->lLastFrame = -1;
1909 if (pStream->idxFrames != NULL) {
1910 HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1911 pStream->idxFrames = NULL;
1912 pStream->nIdxFrames = 0;
1915 if (pStream->sInfo.dwSampleSize != 0) {
1916 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1917 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1918 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1919 pStream->nIdxFrames =
1920 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1922 } else
1923 pStream->nIdxFrames = pStream->sInfo.dwLength;
1925 pStream->idxFrames =
1926 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1927 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1928 pStream->nIdxFrames = 0;
1929 HeapFree(GetProcessHeap(), 0, lp);
1930 return AVIERR_MEMORY;
1934 pos = (DWORD)-1;
1935 while (size != 0) {
1936 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1938 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1939 hr = AVIERR_FILEREAD;
1940 break;
1942 size -= read;
1944 if (pos == (DWORD)-1)
1945 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1947 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1948 pos, &bAbsolute);
1951 HeapFree(GetProcessHeap(), 0, lp);
1953 /* checking ... */
1954 for (n = 0; n < This->fInfo.dwStreams; n++) {
1955 IAVIStreamImpl *pStream = This->ppStreams[n];
1957 if (pStream->sInfo.dwSampleSize == 0 &&
1958 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1959 ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1960 n, pStream->sInfo.dwLength, pStream->lLastFrame);
1963 return hr;
1966 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1967 LONG count, DWORD pos, BOOL *bAbsolute)
1969 if (lp == NULL)
1970 return AVIERR_BADPARAM;
1972 for (; count > 0; count--, lp++) {
1973 WORD nStream = StreamFromFOURCC(lp->ckid);
1975 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1976 continue; /* skip these */
1978 if (nStream > This->fInfo.dwStreams)
1979 return AVIERR_BADFORMAT;
1981 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1982 *bAbsolute = FALSE;
1984 if (*bAbsolute)
1985 lp->dwChunkOffset += sizeof(DWORD);
1986 else
1987 lp->dwChunkOffset += pos;
1989 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1990 return AVIERR_MEMORY;
1993 return AVIERR_OK;
1996 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
1997 LPVOID buffer, DWORD size)
1999 /* pre-conditions */
2000 assert(This != NULL);
2001 assert(This->paf != NULL);
2002 assert(This->paf->hmmio != NULL);
2003 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2004 assert(pos <= This->lLastFrame);
2006 /* should we read as much as block gives us? */
2007 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2008 size = This->idxFrames[pos].dwChunkLength;
2010 /* read into out own buffer or given one? */
2011 if (buffer == NULL) {
2012 /* we also read the chunk */
2013 size += 2 * sizeof(DWORD);
2015 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2016 if (This->lpBuffer == NULL || This->cbBuffer < size) {
2017 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2019 if (This->lpBuffer == NULL) {
2020 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2021 if (!This->lpBuffer) return AVIERR_MEMORY;
2022 } else {
2023 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
2024 if (!new_buffer) return AVIERR_MEMORY;
2025 This->lpBuffer = new_buffer;
2027 This->cbBuffer = maxSize;
2030 /* now read the complete chunk into our buffer */
2031 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2032 return AVIERR_FILEREAD;
2033 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2034 return AVIERR_FILEREAD;
2036 /* check if it was the correct block which we have read */
2037 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2038 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2039 ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
2040 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2041 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2042 This->idxFrames[pos].dwChunkLength);
2043 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n",
2044 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2045 return AVIERR_FILEREAD;
2047 } else {
2048 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2049 return AVIERR_FILEREAD;
2050 if (mmioRead(This->paf->hmmio, buffer, size) != size)
2051 return AVIERR_FILEREAD;
2054 return AVIERR_OK;
2057 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2059 LONG block;
2061 /* pre-conditions */
2062 assert(This != NULL);
2063 assert(pos != NULL);
2064 assert(offset != NULL);
2065 assert(This->sInfo.dwSampleSize != 0);
2066 assert(*pos >= This->sInfo.dwStart);
2068 /* convert start sample to start bytes */
2069 (*offset) = (*pos) - This->sInfo.dwStart;
2070 (*offset) *= This->sInfo.dwSampleSize;
2072 /* convert bytes to block number */
2073 for (block = 0; block <= This->lLastFrame; block++) {
2074 if (This->idxFrames[block].dwChunkLength <= *offset)
2075 (*offset) -= This->idxFrames[block].dwChunkLength;
2076 else
2077 break;
2080 *pos = block;
2083 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2085 MainAVIHeader MainAVIHdr;
2086 IAVIStreamImpl* pStream;
2087 MMCKINFO ckRIFF;
2088 MMCKINFO ckLIST1;
2089 MMCKINFO ckLIST2;
2090 MMCKINFO ck;
2091 DWORD nStream;
2092 DWORD dwPos;
2093 HRESULT hr;
2095 /* initialize some things */
2096 if (This->dwMoviChunkPos == 0)
2097 AVIFILE_ComputeMoviStart(This);
2099 /* written one record too much? */
2100 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2101 This->dwNextFramePos -= 3 * sizeof(DWORD);
2102 if (This->nIdxRecords > 0)
2103 This->nIdxRecords--;
2106 AVIFILE_UpdateInfo(This);
2108 assert(This->fInfo.dwScale != 0);
2110 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2111 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2112 This->fInfo.dwScale);
2113 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2114 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2115 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2116 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2117 MainAVIHdr.dwInitialFrames = 0;
2118 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2119 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2120 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2121 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2122 MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2124 /* now begin writing ... */
2125 mmioSeek(This->hmmio, 0, SEEK_SET);
2127 /* RIFF chunk */
2128 ckRIFF.cksize = 0;
2129 ckRIFF.fccType = formtypeAVI;
2130 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2131 return AVIERR_FILEWRITE;
2133 /* AVI headerlist */
2134 ckLIST1.cksize = 0;
2135 ckLIST1.fccType = listtypeAVIHEADER;
2136 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2137 return AVIERR_FILEWRITE;
2139 /* MainAVIHeader */
2140 ck.ckid = ckidAVIMAINHDR;
2141 ck.cksize = sizeof(MainAVIHdr);
2142 ck.fccType = 0;
2143 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2144 return AVIERR_FILEWRITE;
2145 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2146 return AVIERR_FILEWRITE;
2147 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2148 return AVIERR_FILEWRITE;
2150 /* write the headers of each stream into a separate streamheader list */
2151 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2152 AVIStreamHeader strHdr;
2154 pStream = This->ppStreams[nStream];
2156 /* begin the new streamheader list */
2157 ckLIST2.cksize = 0;
2158 ckLIST2.fccType = listtypeSTREAMHEADER;
2159 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2160 return AVIERR_FILEWRITE;
2162 /* create an AVIStreamHeader from the AVSTREAMINFO */
2163 strHdr.fccType = pStream->sInfo.fccType;
2164 strHdr.fccHandler = pStream->sInfo.fccHandler;
2165 strHdr.dwFlags = pStream->sInfo.dwFlags;
2166 strHdr.wPriority = pStream->sInfo.wPriority;
2167 strHdr.wLanguage = pStream->sInfo.wLanguage;
2168 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2169 strHdr.dwScale = pStream->sInfo.dwScale;
2170 strHdr.dwRate = pStream->sInfo.dwRate;
2171 strHdr.dwStart = pStream->sInfo.dwStart;
2172 strHdr.dwLength = pStream->sInfo.dwLength;
2173 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2174 strHdr.dwQuality = pStream->sInfo.dwQuality;
2175 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2176 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2177 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2178 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2179 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2181 /* now write the AVIStreamHeader */
2182 ck.ckid = ckidSTREAMHEADER;
2183 ck.cksize = sizeof(strHdr);
2184 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2185 return AVIERR_FILEWRITE;
2186 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2187 return AVIERR_FILEWRITE;
2188 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2189 return AVIERR_FILEWRITE;
2191 /* ... the hopefully ever present streamformat ... */
2192 ck.ckid = ckidSTREAMFORMAT;
2193 ck.cksize = pStream->cbFormat;
2194 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2195 return AVIERR_FILEWRITE;
2196 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2197 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2198 return AVIERR_FILEWRITE;
2200 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2201 return AVIERR_FILEWRITE;
2203 /* ... some optional existing handler data ... */
2204 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2205 ck.ckid = ckidSTREAMHANDLERDATA;
2206 ck.cksize = pStream->cbHandlerData;
2207 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2208 return AVIERR_FILEWRITE;
2209 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2210 return AVIERR_FILEWRITE;
2211 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2212 return AVIERR_FILEWRITE;
2215 /* ... some optional additional extra chunk for this stream ... */
2216 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2217 /* the chunk header(s) are already in the structure */
2218 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2219 return AVIERR_FILEWRITE;
2222 /* ... an optional name for this stream ... */
2223 if (lstrlenW(pStream->sInfo.szName) > 0) {
2224 LPSTR str;
2226 ck.ckid = ckidSTREAMNAME;
2227 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2228 if (ck.cksize & 1) /* align */
2229 ck.cksize++;
2230 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2231 return AVIERR_FILEWRITE;
2233 /* the streamname must be saved in ASCII not Unicode */
2234 str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
2235 if (str == NULL)
2236 return AVIERR_MEMORY;
2237 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2238 ck.cksize, NULL, NULL);
2240 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2241 HeapFree(GetProcessHeap(), 0, str);
2242 return AVIERR_FILEWRITE;
2245 HeapFree(GetProcessHeap(), 0, str);
2246 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2247 return AVIERR_FILEWRITE;
2250 /* close streamheader list for this stream */
2251 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2252 return AVIERR_FILEWRITE;
2253 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2255 /* close the aviheader list */
2256 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2257 return AVIERR_FILEWRITE;
2259 /* check for padding to pre-guessed 'movi'-chunk position */
2260 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2261 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2262 ck.ckid = ckidAVIPADDING;
2263 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2264 assert((LONG)ck.cksize >= 0);
2266 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2267 return AVIERR_FILEWRITE;
2268 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2269 return AVIERR_FILEWRITE;
2270 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2271 return AVIERR_FILEWRITE;
2274 /* now write the 'movi' chunk */
2275 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2276 ckLIST1.cksize = 0;
2277 ckLIST1.fccType = listtypeAVIMOVIE;
2278 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2279 return AVIERR_FILEWRITE;
2280 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2281 return AVIERR_FILEWRITE;
2282 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2283 return AVIERR_FILEWRITE;
2285 /* write 'idx1' chunk */
2286 hr = AVIFILE_SaveIndex(This);
2287 if (FAILED(hr))
2288 return hr;
2290 /* write optional extra file chunks */
2291 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2292 /* as for the streams, are the chunk header(s) in the structure */
2293 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2294 return AVIERR_FILEWRITE;
2297 /* close RIFF chunk */
2298 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2299 return AVIERR_FILEWRITE;
2301 /* add some JUNK at end for bad parsers */
2302 memset(&ckRIFF, 0, sizeof(ckRIFF));
2303 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2304 mmioFlush(This->hmmio, 0);
2306 return AVIERR_OK;
2309 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2311 IAVIStreamImpl *pStream;
2312 AVIINDEXENTRY idx;
2313 MMCKINFO ck;
2314 DWORD nStream;
2315 LONG n;
2317 ck.ckid = ckidAVINEWINDEX;
2318 ck.cksize = 0;
2319 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2320 return AVIERR_FILEWRITE;
2322 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2323 /* is interleaved -- write block of corresponding frames */
2324 LONG lInitialFrames = 0;
2325 LONG stepsize;
2326 LONG i;
2328 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2329 stepsize = 1;
2330 else
2331 stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000);
2333 assert(stepsize > 0);
2335 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2336 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2337 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2340 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2341 i += stepsize) {
2342 DWORD nFrame = lInitialFrames + i;
2344 assert(nFrame < This->nIdxRecords);
2346 idx.ckid = listtypeAVIRECORD;
2347 idx.dwFlags = AVIIF_LIST;
2348 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2349 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2350 - This->dwMoviChunkPos;
2351 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2352 return AVIERR_FILEWRITE;
2354 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2355 pStream = This->ppStreams[nStream];
2357 /* heave we reached start of this stream? */
2358 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2359 continue;
2361 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2362 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2364 /* reached end of this stream? */
2365 if (pStream->lLastFrame <= nFrame)
2366 continue;
2368 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2369 pStream->sInfo.dwFormatChangeCount != 0 &&
2370 pStream->idxFmtChanges != NULL) {
2371 DWORD pos;
2373 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2374 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2375 idx.dwFlags = AVIIF_NOTIME;
2376 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2377 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2378 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2379 - This->dwMoviChunkPos;
2381 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2382 return AVIERR_FILEWRITE;
2383 break;
2386 } /* if have formatchanges */
2388 idx.ckid = pStream->idxFrames[nFrame].ckid;
2389 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2390 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2391 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2392 - This->dwMoviChunkPos;
2393 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2394 return AVIERR_FILEWRITE;
2397 } else {
2398 /* not interleaved -- write index for each stream at once */
2399 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2400 pStream = This->ppStreams[nStream];
2402 for (n = 0; n <= pStream->lLastFrame; n++) {
2403 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2404 (pStream->sInfo.dwFormatChangeCount != 0)) {
2405 DWORD pos;
2407 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2408 if (pStream->idxFmtChanges[pos].ckid == n) {
2409 idx.dwFlags = AVIIF_NOTIME;
2410 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2411 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2412 idx.dwChunkOffset =
2413 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2414 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2415 return AVIERR_FILEWRITE;
2416 break;
2419 } /* if have formatchanges */
2421 idx.ckid = pStream->idxFrames[n].ckid;
2422 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2423 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2424 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2425 - This->dwMoviChunkPos;
2427 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2428 return AVIERR_FILEWRITE;
2431 } /* if not interleaved */
2433 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2434 return AVIERR_FILEWRITE;
2436 return AVIERR_OK;
2439 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2441 UINT i;
2442 UINT nStream;
2444 /* pre-condition */
2445 assert(lSkip >= 0);
2447 if (fcc != 0) {
2448 /* search the number of the specified stream */
2449 nStream = (ULONG)-1;
2450 for (i = 0; i < This->fInfo.dwStreams; i++) {
2451 assert(This->ppStreams[i] != NULL);
2453 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2454 if (lSkip == 0) {
2455 nStream = i;
2456 break;
2457 } else
2458 lSkip--;
2461 } else
2462 nStream = lSkip;
2464 return nStream;
2467 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2469 UINT i;
2471 /* pre-conditions */
2472 assert(This != NULL);
2474 This->fInfo.dwMaxBytesPerSec = 0;
2475 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2476 This->fInfo.dwSuggestedBufferSize = 0;
2477 This->fInfo.dwWidth = 0;
2478 This->fInfo.dwHeight = 0;
2479 This->fInfo.dwScale = 0;
2480 This->fInfo.dwRate = 0;
2481 This->fInfo.dwLength = 0;
2482 This->dwInitialFrames = 0;
2484 for (i = 0; i < This->fInfo.dwStreams; i++) {
2485 AVISTREAMINFOW *psi;
2486 DWORD n;
2488 /* pre-conditions */
2489 assert(This->ppStreams[i] != NULL);
2491 psi = &This->ppStreams[i]->sInfo;
2492 assert(psi->dwScale != 0);
2493 assert(psi->dwRate != 0);
2495 if (i == 0) {
2496 /* use first stream timings as base */
2497 This->fInfo.dwScale = psi->dwScale;
2498 This->fInfo.dwRate = psi->dwRate;
2499 This->fInfo.dwLength = psi->dwLength;
2500 } else {
2501 n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface,
2502 &This->ppStreams[i]->IAVIStream_iface, psi->dwLength);
2503 if (This->fInfo.dwLength < n)
2504 This->fInfo.dwLength = n;
2507 if (This->dwInitialFrames < psi->dwInitialFrames)
2508 This->dwInitialFrames = psi->dwInitialFrames;
2510 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2511 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2513 if (psi->dwSampleSize != 0) {
2514 /* fixed sample size -- exact computation */
2515 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2516 psi->dwScale);
2517 } else {
2518 /* variable sample size -- only upper limit */
2519 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2520 psi->dwRate, psi->dwScale);
2522 /* update dimensions */
2523 n = psi->rcFrame.right - psi->rcFrame.left;
2524 if (This->fInfo.dwWidth < n)
2525 This->fInfo.dwWidth = n;
2526 n = psi->rcFrame.bottom - psi->rcFrame.top;
2527 if (This->fInfo.dwHeight < n)
2528 This->fInfo.dwHeight = n;
2533 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2534 FOURCC ckid, DWORD flags, LPCVOID buffer,
2535 LONG size)
2537 MMCKINFO ck;
2539 ck.ckid = ckid;
2540 ck.cksize = size;
2541 ck.fccType = 0;
2543 /* if no frame/block is already written, we must compute start of movi chunk */
2544 if (This->paf->dwMoviChunkPos == 0)
2545 AVIFILE_ComputeMoviStart(This->paf);
2547 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2548 return AVIERR_FILEWRITE;
2550 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2551 return AVIERR_FILEWRITE;
2552 if (buffer != NULL && size > 0) {
2553 if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2554 return AVIERR_FILEWRITE;
2556 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2557 return AVIERR_FILEWRITE;
2559 This->paf->fDirty = TRUE;
2560 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2562 return AVIFILE_AddFrame(This, ckid, size,
2563 ck.dwDataOffset - 2 * sizeof(DWORD), flags);