comctl32: Remove the masked background when 32bpp bitmaps have no alpha values.
[wine.git] / dlls / avifil32 / avifile.c
blob8f5c5e645620ef53e7cf5784c0eb3786d98abbe1
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/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
53 #ifndef IDX_PER_BLOCK
54 #define IDX_PER_BLOCK 2730
55 #endif
57 typedef struct _IAVIFileImpl IAVIFileImpl;
59 typedef struct _IAVIStreamImpl {
60 IAVIStream IAVIStream_iface;
61 LONG ref;
63 IAVIFileImpl *paf;
64 DWORD nStream; /* the n-th stream in file */
65 AVISTREAMINFOW sInfo;
67 LPVOID lpFormat;
68 DWORD cbFormat;
70 LPVOID lpHandlerData;
71 DWORD cbHandlerData;
73 EXTRACHUNKS extra;
75 LPDWORD lpBuffer;
76 DWORD cbBuffer; /* size of lpBuffer */
77 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
79 LONG lLastFrame; /* last correct index in idxFrames */
80 AVIINDEXENTRY *idxFrames;
81 DWORD nIdxFrames; /* upper index limit of idxFrames */
82 AVIINDEXENTRY *idxFmtChanges;
83 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
84 } IAVIStreamImpl;
86 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
88 return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
91 struct _IAVIFileImpl {
92 IUnknown IUnknown_inner;
93 IAVIFile IAVIFile_iface;
94 IPersistFile IPersistFile_iface;
95 IUnknown *outer_unk;
96 LONG ref;
98 AVIFILEINFOW fInfo;
99 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
101 EXTRACHUNKS fileextra;
103 DWORD dwMoviChunkPos; /* some stuff for saving ... */
104 DWORD dwIdxChunkPos;
105 DWORD dwNextFramePos;
106 DWORD dwInitialFrames;
108 MMCKINFO ckLastRecord;
109 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
110 DWORD nIdxRecords; /* current fill level */
111 DWORD cbIdxRecords; /* size of idxRecords */
113 /* IPersistFile stuff ... */
114 HMMIO hmmio;
115 LPWSTR szFileName;
116 UINT uMode;
117 BOOL fDirty;
120 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
122 return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
125 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
127 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
130 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
132 return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
135 /***********************************************************************/
137 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
138 DWORD offset, DWORD flags);
139 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
140 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
141 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
142 const AVISTREAMINFOW *asi);
143 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
144 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
145 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
146 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
147 LONG count, DWORD pos, BOOL *bAbsolute);
148 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
149 LPVOID buffer, DWORD size);
150 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
151 LPLONG offset);
152 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
153 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
154 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
155 LONG lSkip);
156 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
157 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
158 FOURCC ckid, DWORD flags, LPCVOID buffer,
159 LONG size);
161 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
163 IAVIFileImpl *This = impl_from_IUnknown(iface);
165 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
167 if (!ppv) {
168 WARN("invalid parameter\n");
169 return E_INVALIDARG;
171 *ppv = NULL;
173 if (IsEqualIID(riid, &IID_IUnknown))
174 *ppv = &This->IUnknown_inner;
175 else if (IsEqualIID(riid, &IID_IAVIFile))
176 *ppv = &This->IAVIFile_iface;
177 else if (IsEqualGUID(riid, &IID_IPersistFile))
178 *ppv = &This->IPersistFile_iface;
179 else {
180 WARN("unknown IID %s\n", debugstr_guid(riid));
181 return E_NOINTERFACE;
184 /* Violation of the COM aggregation ref counting rule */
185 IUnknown_AddRef(&This->IUnknown_inner);
186 return S_OK;
189 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
191 IAVIFileImpl *This = impl_from_IUnknown(iface);
192 ULONG ref = InterlockedIncrement(&This->ref);
194 TRACE("(%p) ref=%d\n", This, ref);
196 return ref;
199 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
201 IAVIFileImpl *This = impl_from_IUnknown(iface);
202 ULONG ref = InterlockedDecrement(&This->ref);
203 UINT i;
205 TRACE("(%p) ref=%d\n", This, ref);
207 if (!ref) {
208 if (This->fDirty)
209 AVIFILE_SaveFile(This);
211 for (i = 0; i < This->fInfo.dwStreams; i++) {
212 if (This->ppStreams[i] != NULL) {
213 if (This->ppStreams[i]->ref != 0)
214 ERR(": someone has still %u reference to stream %u (%p)!\n",
215 This->ppStreams[i]->ref, i, This->ppStreams[i]);
216 AVIFILE_DestructAVIStream(This->ppStreams[i]);
217 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
218 This->ppStreams[i] = NULL;
222 if (This->idxRecords != NULL) {
223 HeapFree(GetProcessHeap(), 0, This->idxRecords);
224 This->idxRecords = NULL;
225 This->nIdxRecords = 0;
228 if (This->fileextra.lp != NULL) {
229 HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
230 This->fileextra.lp = NULL;
231 This->fileextra.cb = 0;
234 HeapFree(GetProcessHeap(), 0, This->szFileName);
235 This->szFileName = NULL;
237 if (This->hmmio != NULL) {
238 mmioClose(This->hmmio, 0);
239 This->hmmio = NULL;
242 HeapFree(GetProcessHeap(), 0, This);
244 return ref;
247 static const IUnknownVtbl unk_vtbl =
249 IUnknown_fnQueryInterface,
250 IUnknown_fnAddRef,
251 IUnknown_fnRelease
254 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
256 IAVIFileImpl *This = impl_from_IAVIFile(iface);
258 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
261 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
263 IAVIFileImpl *This = impl_from_IAVIFile(iface);
265 return IUnknown_AddRef(This->outer_unk);
268 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
270 IAVIFileImpl *This = impl_from_IAVIFile(iface);
272 return IUnknown_Release(This->outer_unk);
275 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
277 IAVIFileImpl *This = impl_from_IAVIFile(iface);
279 TRACE("(%p,%p,%d)\n",iface,afi,size);
281 if (afi == NULL)
282 return AVIERR_BADPARAM;
283 if (size < 0)
284 return AVIERR_BADSIZE;
286 AVIFILE_UpdateInfo(This);
288 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
290 if ((DWORD)size < sizeof(This->fInfo))
291 return AVIERR_BUFFERTOOSMALL;
292 return AVIERR_OK;
295 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
296 LONG lParam)
298 IAVIFileImpl *This = impl_from_IAVIFile(iface);
299 ULONG nStream;
301 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
303 if (avis == NULL || lParam < 0)
304 return AVIERR_BADPARAM;
306 nStream = AVIFILE_SearchStream(This, fccType, lParam);
308 /* Does the requested stream exist? */
309 if (nStream < This->fInfo.dwStreams &&
310 This->ppStreams[nStream] != NULL) {
311 *avis = &This->ppStreams[nStream]->IAVIStream_iface;
312 IAVIStream_AddRef(*avis);
314 return AVIERR_OK;
317 /* Sorry, but the specified stream doesn't exist */
318 *avis = NULL;
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]);
468 This->fInfo.dwStreams--;
469 if (nStream < This->fInfo.dwStreams)
470 memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1],
471 (This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0]));
473 This->ppStreams[This->fInfo.dwStreams] = NULL;
474 This->fDirty = TRUE;
476 /* This->fInfo will be updated further when asked for */
477 return AVIERR_OK;
478 } else
479 return AVIERR_NODATA;
482 static const struct IAVIFileVtbl avif_vt = {
483 IAVIFile_fnQueryInterface,
484 IAVIFile_fnAddRef,
485 IAVIFile_fnRelease,
486 IAVIFile_fnInfo,
487 IAVIFile_fnGetStream,
488 IAVIFile_fnCreateStream,
489 IAVIFile_fnWriteData,
490 IAVIFile_fnReadData,
491 IAVIFile_fnEndRecord,
492 IAVIFile_fnDeleteStream
496 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
498 IAVIFileImpl *This = impl_from_IPersistFile(iface);
500 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
503 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
505 IAVIFileImpl *This = impl_from_IPersistFile(iface);
507 return IUnknown_AddRef(This->outer_unk);
510 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
512 IAVIFileImpl *This = impl_from_IPersistFile(iface);
514 return IUnknown_Release(This->outer_unk);
517 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
519 TRACE("(%p,%p)\n", iface, pClassID);
521 if (pClassID == NULL)
522 return AVIERR_BADPARAM;
524 *pClassID = CLSID_AVIFile;
526 return AVIERR_OK;
529 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
531 IAVIFileImpl *This = impl_from_IPersistFile(iface);
533 TRACE("(%p)\n", iface);
535 return (This->fDirty ? S_OK : S_FALSE);
538 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
540 IAVIFileImpl *This = impl_from_IPersistFile(iface);
541 int len;
543 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
545 /* check parameter */
546 if (pszFileName == NULL)
547 return AVIERR_BADPARAM;
549 if (This->hmmio != NULL)
550 return AVIERR_ERROR; /* No reuse of this object for another file! */
552 /* remember mode and name */
553 This->uMode = dwMode;
555 len = lstrlenW(pszFileName) + 1;
556 This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
557 if (This->szFileName == NULL)
558 return AVIERR_MEMORY;
559 lstrcpyW(This->szFileName, pszFileName);
561 /* try to open the file */
562 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
563 if (This->hmmio == NULL) {
564 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
565 LPSTR szFileName;
567 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
568 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
569 if (szFileName == NULL)
570 return AVIERR_MEMORY;
572 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
574 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
575 HeapFree(GetProcessHeap(), 0, szFileName);
576 if (This->hmmio == NULL)
577 return AVIERR_FILEOPEN;
580 /* should we create a new file? */
581 if (dwMode & OF_CREATE) {
582 memset(& This->fInfo, 0, sizeof(This->fInfo));
583 This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
585 return AVIERR_OK;
586 } else
587 return AVIFILE_LoadFile(This);
590 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
591 BOOL fRemember)
593 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
595 /* We write directly to disk, so nothing to do. */
597 return AVIERR_OK;
600 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
602 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
604 /* We write directly to disk, so nothing to do. */
606 return AVIERR_OK;
609 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
611 IAVIFileImpl *This = impl_from_IPersistFile(iface);
613 TRACE("(%p,%p)\n", iface, ppszFileName);
615 if (ppszFileName == NULL)
616 return AVIERR_BADPARAM;
618 *ppszFileName = NULL;
620 if (This->szFileName != NULL) {
621 int len = lstrlenW(This->szFileName) + 1;
623 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
624 if (*ppszFileName == NULL)
625 return AVIERR_MEMORY;
627 lstrcpyW(*ppszFileName, This->szFileName);
630 return AVIERR_OK;
633 static const struct IPersistFileVtbl pf_vt = {
634 IPersistFile_fnQueryInterface,
635 IPersistFile_fnAddRef,
636 IPersistFile_fnRelease,
637 IPersistFile_fnGetClassID,
638 IPersistFile_fnIsDirty,
639 IPersistFile_fnLoad,
640 IPersistFile_fnSave,
641 IPersistFile_fnSaveCompleted,
642 IPersistFile_fnGetCurFile
645 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
647 IAVIFileImpl *obj;
648 HRESULT hr;
650 *ppv = NULL;
651 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
652 if (!obj)
653 return AVIERR_MEMORY;
655 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
656 obj->IAVIFile_iface.lpVtbl = &avif_vt;
657 obj->IPersistFile_iface.lpVtbl = &pf_vt;
658 obj->ref = 1;
659 if (pUnkOuter)
660 obj->outer_unk = pUnkOuter;
661 else
662 obj->outer_unk = &obj->IUnknown_inner;
664 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
665 IUnknown_Release(&obj->IUnknown_inner);
667 return hr;
671 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
673 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
675 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
677 if (!ppv) {
678 WARN("invalid parameter\n");
679 return E_INVALIDARG;
681 *ppv = NULL;
683 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
684 *ppv = iface;
685 IAVIStream_AddRef(iface);
687 return S_OK;
689 /* FIXME: IAVIStreaming interface */
691 return E_NOINTERFACE;
694 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
696 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
697 ULONG ref = InterlockedIncrement(&This->ref);
699 TRACE("(%p) ref=%d\n", This, ref);
701 /* also add ref to parent, so that it doesn't kill us */
702 if (This->paf != NULL && ref == 1)
703 IAVIFile_AddRef(&This->paf->IAVIFile_iface);
705 return ref;
708 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
710 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
711 ULONG ref = InterlockedDecrement(&This->ref);
713 TRACE("(%p) ref=%d\n", This, ref);
715 if (This->paf != NULL && ref == 0)
716 IAVIFile_Release(&This->paf->IAVIFile_iface);
718 return ref;
721 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
723 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
725 /* This IAVIStream interface needs an AVIFile */
726 return AVIERR_UNSUPPORTED;
729 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
731 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
733 TRACE("(%p,%p,%d)\n", iface, psi, size);
735 if (psi == NULL)
736 return AVIERR_BADPARAM;
737 if (size < 0)
738 return AVIERR_BADSIZE;
740 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
742 if ((DWORD)size < sizeof(This->sInfo))
743 return AVIERR_BUFFERTOOSMALL;
744 return AVIERR_OK;
747 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
749 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
750 LONG offset = 0;
752 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
754 if (flags & FIND_FROM_START) {
755 pos = This->sInfo.dwStart;
756 flags &= ~(FIND_FROM_START|FIND_PREV);
757 flags |= FIND_NEXT;
760 if (This->sInfo.dwSampleSize != 0) {
761 /* convert samples into block number with offset */
762 AVIFILE_SamplesToBlock(This, &pos, &offset);
765 if (flags & FIND_TYPE) {
766 if (flags & FIND_KEY) {
767 while (0 <= pos && pos <= This->lLastFrame) {
768 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
769 goto RETURN_FOUND;
771 if (flags & FIND_NEXT)
772 pos++;
773 else
774 pos--;
776 } else if (flags & FIND_ANY) {
777 while (0 <= pos && pos <= This->lLastFrame) {
778 if (This->idxFrames[pos].dwChunkLength > 0)
779 goto RETURN_FOUND;
781 if (flags & FIND_NEXT)
782 pos++;
783 else
784 pos--;
787 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
788 This->sInfo.fccType == streamtypeVIDEO) {
789 if (flags & FIND_NEXT) {
790 ULONG n;
792 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
793 if (This->idxFmtChanges[n].ckid >= pos) {
794 pos = This->idxFmtChanges[n].ckid;
795 goto RETURN_FOUND;
797 } else {
798 LONG n;
800 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
801 if (This->idxFmtChanges[n].ckid <= pos) {
802 pos = This->idxFmtChanges[n].ckid;
803 goto RETURN_FOUND;
807 if (pos > (LONG)This->sInfo.dwStart)
808 return 0; /* format changes always for first frame */
812 return -1;
815 RETURN_FOUND:
816 if (pos < (LONG)This->sInfo.dwStart)
817 return -1;
819 switch (flags & FIND_RET) {
820 case FIND_LENGTH:
821 /* physical size */
822 pos = This->idxFrames[pos].dwChunkLength;
823 break;
824 case FIND_OFFSET:
825 /* physical position */
826 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
827 + offset * This->sInfo.dwSampleSize;
828 break;
829 case FIND_SIZE:
830 /* logical size */
831 if (This->sInfo.dwSampleSize)
832 pos = This->sInfo.dwSampleSize;
833 else
834 pos = 1;
835 break;
836 case FIND_INDEX:
837 FIXME(": FIND_INDEX flag is not supported!\n");
838 /* This is an index in the index-table on disc. */
839 break;
840 }; /* else logical position */
842 return pos;
845 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
846 LONG *formatsize)
848 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
850 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
852 if (formatsize == NULL)
853 return AVIERR_BADPARAM;
855 /* only interested in needed buffersize? */
856 if (format == NULL || *formatsize <= 0) {
857 *formatsize = This->cbFormat;
859 return AVIERR_OK;
862 /* copy initial format (only as much as will fit) */
863 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
864 if (*(DWORD*)formatsize < This->cbFormat) {
865 *formatsize = This->cbFormat;
866 return AVIERR_BUFFERTOOSMALL;
869 /* Could format change? When yes will it change? */
870 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
871 pos > This->sInfo.dwStart) {
872 LONG lLastFmt;
874 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
875 if (lLastFmt > 0) {
876 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
880 *formatsize = This->cbFormat;
881 return AVIERR_OK;
884 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
885 LONG formatsize)
887 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
888 BITMAPINFOHEADER *lpbiNew = format;
890 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
892 /* check parameters */
893 if (format == NULL || formatsize <= 0)
894 return AVIERR_BADPARAM;
896 /* Do we have write permission? */
897 if ((This->paf->uMode & MMIO_RWMODE) == 0)
898 return AVIERR_READONLY;
900 /* can only set format before frame is written! */
901 if (This->lLastFrame > pos)
902 return AVIERR_UNSUPPORTED;
904 /* initial format or a formatchange? */
905 if (This->lpFormat == NULL) {
906 /* initial format */
907 if (This->paf->dwMoviChunkPos != 0)
908 return AVIERR_ERROR; /* user has used API in wrong sequence! */
910 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
911 if (This->lpFormat == NULL)
912 return AVIERR_MEMORY;
913 This->cbFormat = formatsize;
915 memcpy(This->lpFormat, format, formatsize);
917 /* update some infos about stream */
918 if (This->sInfo.fccType == streamtypeVIDEO) {
919 LONG lDim;
921 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
922 if (lDim < lpbiNew->biWidth)
923 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
924 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
925 if (lDim < lpbiNew->biHeight)
926 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
927 } else if (This->sInfo.fccType == streamtypeAUDIO)
928 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
930 return AVIERR_OK;
931 } else {
932 MMCKINFO ck;
933 LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
934 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
935 AVIPALCHANGE *lppc = NULL;
936 UINT n;
938 /* perhaps format change, check it ... */
939 if (This->cbFormat != formatsize)
940 return AVIERR_UNSUPPORTED;
942 /* no format change, only the initial one */
943 if (memcmp(This->lpFormat, format, formatsize) == 0)
944 return AVIERR_OK;
946 /* check that's only the palette, which changes */
947 if (lpbiOld->biSize != lpbiNew->biSize ||
948 lpbiOld->biWidth != lpbiNew->biWidth ||
949 lpbiOld->biHeight != lpbiNew->biHeight ||
950 lpbiOld->biPlanes != lpbiNew->biPlanes ||
951 lpbiOld->biBitCount != lpbiNew->biBitCount ||
952 lpbiOld->biCompression != lpbiNew->biCompression ||
953 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
954 return AVIERR_UNSUPPORTED;
956 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
958 /* simply say all colors have changed */
959 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
960 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
961 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
962 if (lppc == NULL)
963 return AVIERR_MEMORY;
965 lppc->bFirstEntry = 0;
966 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
967 lppc->wFlags = 0;
968 for (n = 0; n < lpbiOld->biClrUsed; n++) {
969 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
970 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
971 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
972 lppc->peNew[n].peFlags = 0;
975 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
976 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
977 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
978 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
980 HeapFree(GetProcessHeap(), 0, lppc);
981 return AVIERR_FILEWRITE;
984 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
986 HeapFree(GetProcessHeap(), 0, lppc);
988 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
992 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
993 LONG buffersize, LONG *bytesread, LONG *samplesread)
995 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
996 DWORD size;
997 HRESULT hr;
999 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
1000 buffersize, bytesread, samplesread);
1002 /* clear return parameters if given */
1003 if (bytesread != NULL)
1004 *bytesread = 0;
1005 if (samplesread != NULL)
1006 *samplesread = 0;
1008 /* check parameters */
1009 if ((LONG)This->sInfo.dwStart > start)
1010 return AVIERR_NODATA; /* couldn't read before start of stream */
1011 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1012 return AVIERR_NODATA; /* start is past end of stream */
1014 /* should we read as much as possible? */
1015 if (samples == -1) {
1016 /* User should know how much we have read */
1017 if (bytesread == NULL && samplesread == NULL)
1018 return AVIERR_BADPARAM;
1020 if (This->sInfo.dwSampleSize != 0)
1021 samples = buffersize / This->sInfo.dwSampleSize;
1022 else
1023 samples = 1;
1026 /* limit to end of stream */
1027 if ((LONG)This->sInfo.dwLength < samples)
1028 samples = This->sInfo.dwLength;
1029 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1030 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1032 /* nothing to read? Then leave ... */
1033 if (samples == 0)
1034 return AVIERR_OK;
1036 if (This->sInfo.dwSampleSize != 0) {
1037 /* fixed samplesize -- we can read over frame/block boundaries */
1038 LONG block = start;
1039 LONG offset = 0;
1041 if (!buffer)
1043 if (bytesread)
1044 *bytesread = samples*This->sInfo.dwSampleSize;
1045 if (samplesread)
1046 *samplesread = samples;
1047 return AVIERR_OK;
1050 /* convert start sample to block,offset pair */
1051 AVIFILE_SamplesToBlock(This, &block, &offset);
1053 /* convert samples to bytes */
1054 samples *= This->sInfo.dwSampleSize;
1056 while (samples > 0 && buffersize > 0) {
1057 LONG blocksize;
1058 if (block != This->dwCurrentFrame) {
1059 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1060 if (FAILED(hr))
1061 return hr;
1064 size = min((DWORD)samples, (DWORD)buffersize);
1065 blocksize = This->lpBuffer[1];
1066 TRACE("blocksize = %u\n",blocksize);
1067 size = min(size, blocksize - offset);
1068 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1070 block++;
1071 offset = 0;
1072 buffer = ((LPBYTE)buffer)+size;
1073 samples -= size;
1074 buffersize -= size;
1076 /* fill out return parameters if given */
1077 if (bytesread != NULL)
1078 *bytesread += size;
1079 if (samplesread != NULL)
1080 *samplesread += size / This->sInfo.dwSampleSize;
1083 if (samples == 0)
1084 return AVIERR_OK;
1085 else
1086 return AVIERR_BUFFERTOOSMALL;
1087 } else {
1088 /* variable samplesize -- we can only read one full frame/block */
1089 if (samples > 1)
1090 samples = 1;
1092 assert(start <= This->lLastFrame);
1093 size = This->idxFrames[start].dwChunkLength;
1094 if (buffer != NULL && buffersize >= size) {
1095 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1096 if (FAILED(hr))
1097 return hr;
1098 } else if (buffer != NULL)
1099 return AVIERR_BUFFERTOOSMALL;
1101 /* fill out return parameters if given */
1102 if (bytesread != NULL)
1103 *bytesread = size;
1104 if (samplesread != NULL)
1105 *samplesread = samples;
1107 return AVIERR_OK;
1111 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
1112 LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1114 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1115 FOURCC ckid;
1116 HRESULT hr;
1118 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1119 buffer, buffersize, flags, sampwritten, byteswritten);
1121 /* clear return parameters if given */
1122 if (sampwritten != NULL)
1123 *sampwritten = 0;
1124 if (byteswritten != NULL)
1125 *byteswritten = 0;
1127 /* check parameters */
1128 if (buffer == NULL && (buffersize > 0 || samples > 0))
1129 return AVIERR_BADPARAM;
1131 /* Have we write permission? */
1132 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1133 return AVIERR_READONLY;
1135 switch (This->sInfo.fccType) {
1136 case streamtypeAUDIO:
1137 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1138 break;
1139 default:
1140 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1141 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1142 else
1143 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1144 break;
1147 /* append to end of stream? */
1148 if (start == -1) {
1149 if (This->lLastFrame == -1)
1150 start = This->sInfo.dwStart;
1151 else
1152 start = This->sInfo.dwLength;
1153 } else if (This->lLastFrame == -1)
1154 This->sInfo.dwStart = start;
1156 if (This->sInfo.dwSampleSize != 0) {
1157 /* fixed sample size -- audio like */
1158 if (samples * This->sInfo.dwSampleSize != buffersize)
1159 return AVIERR_BADPARAM;
1161 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1162 if (This->sInfo.dwLength != start)
1163 return AVIERR_UNSUPPORTED;
1165 /* Convert position to frame/block */
1166 start = This->lLastFrame + 1;
1168 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1169 FIXME(": not interleaved, could collect audio data!\n");
1171 } else {
1172 /* variable sample size -- video like */
1173 if (samples > 1)
1174 return AVIERR_UNSUPPORTED;
1176 /* must we fill up with empty frames? */
1177 if (This->lLastFrame != -1) {
1178 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1180 while (start > This->lLastFrame + 1) {
1181 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1182 if (FAILED(hr))
1183 return hr;
1188 /* write the block now */
1189 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1190 if (SUCCEEDED(hr)) {
1191 /* fill out return parameters if given */
1192 if (sampwritten != NULL)
1193 *sampwritten = samples;
1194 if (byteswritten != NULL)
1195 *byteswritten = buffersize;
1198 return hr;
1201 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1203 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1205 FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1207 /* check parameters */
1208 if (start < 0 || samples < 0)
1209 return AVIERR_BADPARAM;
1211 /* Delete before start of stream? */
1212 if (start + samples < This->sInfo.dwStart)
1213 return AVIERR_OK;
1215 /* Delete after end of stream? */
1216 if (start > This->sInfo.dwLength)
1217 return AVIERR_OK;
1219 /* For the rest we need write permissions */
1220 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1221 return AVIERR_READONLY;
1223 /* 1. overwrite the data with JUNK
1225 * if ISINTERLEAVED {
1226 * 2. concat all neighboured JUNK-blocks in this record to one
1227 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1228 * to start of this record, repeat this.
1229 * } else {
1230 * 2. concat all neighboured JUNK-blocks.
1231 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1232 * start of this block.
1236 return AVIERR_UNSUPPORTED;
1239 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1241 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1243 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1245 if (fcc == ckidSTREAMHANDLERDATA) {
1246 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1247 if (lp == NULL || *lpread <= 0) {
1248 *lpread = This->cbHandlerData;
1249 return AVIERR_OK;
1252 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1253 if (*lpread < This->cbHandlerData)
1254 return AVIERR_BUFFERTOOSMALL;
1255 return AVIERR_OK;
1256 } else
1257 return AVIERR_NODATA;
1258 } else
1259 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1262 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1264 IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1266 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1268 /* check parameters */
1269 if (lp == NULL)
1270 return AVIERR_BADPARAM;
1271 if (size <= 0)
1272 return AVIERR_BADSIZE;
1274 /* need write permission */
1275 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1276 return AVIERR_READONLY;
1278 /* already written something to this file? */
1279 if (This->paf->dwMoviChunkPos != 0) {
1280 /* the data will be inserted before the 'movi' chunk, so check for
1281 * enough space */
1282 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1284 /* ckid,size => 2 * sizeof(DWORD) */
1285 dwPos += 2 * sizeof(DWORD) + size;
1286 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1287 return AVIERR_UNSUPPORTED; /* not enough space left */
1290 This->paf->fDirty = TRUE;
1292 if (fcc == ckidSTREAMHANDLERDATA) {
1293 if (This->lpHandlerData != NULL) {
1294 FIXME(": handler data already set -- overwrite?\n");
1295 return AVIERR_UNSUPPORTED;
1298 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1299 if (This->lpHandlerData == NULL)
1300 return AVIERR_MEMORY;
1301 This->cbHandlerData = size;
1302 memcpy(This->lpHandlerData, lp, size);
1304 return AVIERR_OK;
1305 } else
1306 return WriteExtraChunk(&This->extra, fcc, lp, size);
1309 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1311 FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1313 return E_FAIL;
1316 static const struct IAVIStreamVtbl avist_vt = {
1317 IAVIStream_fnQueryInterface,
1318 IAVIStream_fnAddRef,
1319 IAVIStream_fnRelease,
1320 IAVIStream_fnCreate,
1321 IAVIStream_fnInfo,
1322 IAVIStream_fnFindSample,
1323 IAVIStream_fnReadFormat,
1324 IAVIStream_fnSetFormat,
1325 IAVIStream_fnRead,
1326 IAVIStream_fnWrite,
1327 IAVIStream_fnDelete,
1328 IAVIStream_fnReadData,
1329 IAVIStream_fnWriteData,
1330 IAVIStream_fnSetInfo
1334 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1336 UINT n;
1338 /* pre-conditions */
1339 assert(This != NULL);
1341 switch (TWOCCFromFOURCC(ckid)) {
1342 case cktypeDIBbits:
1343 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1344 flags |= AVIIF_KEYFRAME;
1345 break;
1346 case cktypeDIBcompressed:
1347 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1348 flags &= ~AVIIF_KEYFRAME;
1349 break;
1350 case cktypePALchange:
1351 if (This->sInfo.fccType != streamtypeVIDEO) {
1352 ERR(": found palette change in non-video stream!\n");
1353 return AVIERR_BADFORMAT;
1356 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1357 DWORD new_count = This->nIdxFmtChanges + 16;
1358 void *new_buffer;
1360 if (This->idxFmtChanges == NULL) {
1361 This->idxFmtChanges =
1362 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
1363 if (!This->idxFmtChanges) return AVIERR_MEMORY;
1364 } else {
1365 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
1366 new_count * sizeof(AVIINDEXENTRY));
1367 if (!new_buffer) return AVIERR_MEMORY;
1368 This->idxFmtChanges = new_buffer;
1370 This->nIdxFmtChanges = new_count;
1373 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1374 n = ++This->sInfo.dwFormatChangeCount;
1375 This->idxFmtChanges[n].ckid = This->lLastFrame;
1376 This->idxFmtChanges[n].dwFlags = 0;
1377 This->idxFmtChanges[n].dwChunkOffset = offset;
1378 This->idxFmtChanges[n].dwChunkLength = size;
1380 return AVIERR_OK;
1381 case cktypeWAVEbytes:
1382 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1383 flags |= AVIIF_KEYFRAME;
1384 break;
1385 default:
1386 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1387 break;
1390 /* first frame is always a keyframe */
1391 if (This->lLastFrame == -1)
1392 flags |= AVIIF_KEYFRAME;
1394 if (This->sInfo.dwSuggestedBufferSize < size)
1395 This->sInfo.dwSuggestedBufferSize = size;
1397 /* get memory for index */
1398 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1399 This->nIdxFrames += 512;
1400 if (This->idxFrames == NULL)
1401 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1402 else
1403 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1404 This->nIdxFrames * sizeof(AVIINDEXENTRY));
1405 if (This->idxFrames == NULL)
1406 return AVIERR_MEMORY;
1409 This->lLastFrame++;
1410 This->idxFrames[This->lLastFrame].ckid = ckid;
1411 This->idxFrames[This->lLastFrame].dwFlags = flags;
1412 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1413 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1415 /* update AVISTREAMINFO structure if necessary */
1416 if (This->sInfo.dwLength <= This->lLastFrame)
1417 This->sInfo.dwLength = This->lLastFrame + 1;
1419 return AVIERR_OK;
1422 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1424 /* pre-conditions */
1425 assert(This != NULL && This->ppStreams[0] != NULL);
1427 if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) {
1428 DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY);
1429 void *mem;
1430 if (!This->idxRecords)
1431 mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count);
1432 else
1433 mem = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxRecords, new_count);
1434 if (mem) {
1435 This->cbIdxRecords = new_count;
1436 This->idxRecords = mem;
1437 } else {
1438 HeapFree(GetProcessHeap(), 0, This->idxRecords);
1439 This->idxRecords = NULL;
1440 return AVIERR_MEMORY;
1444 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1446 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1447 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1448 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1449 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1450 This->idxRecords[This->nIdxRecords].dwChunkLength =
1451 This->ckLastRecord.cksize;
1452 This->nIdxRecords++;
1454 return AVIERR_OK;
1457 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1459 DWORD dwPos;
1460 DWORD nStream;
1462 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1463 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1465 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1466 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1468 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1469 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1470 dwPos += ((pStream->cbFormat + 1) & ~1U);
1471 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1472 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1473 if (pStream->sInfo.szName[0])
1474 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1477 if (This->dwMoviChunkPos == 0) {
1478 This->dwNextFramePos = dwPos;
1480 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1481 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1482 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1484 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1487 return dwPos;
1490 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1492 IAVIStreamImpl *pstream;
1494 /* pre-conditions */
1495 assert(paf != NULL);
1496 assert(nr < MAX_AVISTREAMS);
1497 assert(paf->ppStreams[nr] != NULL);
1499 pstream = paf->ppStreams[nr];
1501 pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1502 pstream->ref = 0;
1503 pstream->paf = paf;
1504 pstream->nStream = nr;
1505 pstream->dwCurrentFrame = (DWORD)-1;
1506 pstream->lLastFrame = -1;
1508 if (asi != NULL) {
1509 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1511 if (asi->dwLength > 0) {
1512 /* pre-allocate mem for frame-index structure */
1513 pstream->idxFrames =
1514 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1515 if (pstream->idxFrames != NULL)
1516 pstream->nIdxFrames = asi->dwLength;
1518 if (asi->dwFormatChangeCount > 0) {
1519 /* pre-allocate mem for formatchange-index structure */
1520 pstream->idxFmtChanges =
1521 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1522 if (pstream->idxFmtChanges != NULL)
1523 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1526 /* These values will be computed */
1527 pstream->sInfo.dwLength = 0;
1528 pstream->sInfo.dwSuggestedBufferSize = 0;
1529 pstream->sInfo.dwFormatChangeCount = 0;
1530 pstream->sInfo.dwEditCount = 1;
1531 if (pstream->sInfo.dwSampleSize > 0)
1532 SetRectEmpty(&pstream->sInfo.rcFrame);
1535 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1538 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1540 /* pre-conditions */
1541 assert(This != NULL);
1543 This->dwCurrentFrame = (DWORD)-1;
1544 This->lLastFrame = -1;
1545 This->paf = NULL;
1546 if (This->idxFrames != NULL) {
1547 HeapFree(GetProcessHeap(), 0, This->idxFrames);
1548 This->idxFrames = NULL;
1549 This->nIdxFrames = 0;
1551 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
1552 This->idxFmtChanges = NULL;
1553 if (This->lpBuffer != NULL) {
1554 HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1555 This->lpBuffer = NULL;
1556 This->cbBuffer = 0;
1558 if (This->lpHandlerData != NULL) {
1559 HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1560 This->lpHandlerData = NULL;
1561 This->cbHandlerData = 0;
1563 if (This->extra.lp != NULL) {
1564 HeapFree(GetProcessHeap(), 0, This->extra.lp);
1565 This->extra.lp = NULL;
1566 This->extra.cb = 0;
1568 if (This->lpFormat != NULL) {
1569 HeapFree(GetProcessHeap(), 0, This->lpFormat);
1570 This->lpFormat = NULL;
1571 This->cbFormat = 0;
1575 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1577 MainAVIHeader MainAVIHdr;
1578 MMCKINFO ckRIFF;
1579 MMCKINFO ckLIST1;
1580 MMCKINFO ckLIST2;
1581 MMCKINFO ck;
1582 IAVIStreamImpl *pStream;
1583 DWORD nStream;
1584 HRESULT hr;
1586 if (This->hmmio == NULL)
1587 return AVIERR_FILEOPEN;
1589 /* initialize stream ptr's */
1590 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1592 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1593 ckRIFF.fccType = formtypeAVI;
1594 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1595 ERR(": not an AVI!\n");
1596 return AVIERR_FILEREAD;
1599 /* get "LIST" "hdrl" */
1600 ckLIST1.fccType = listtypeAVIHEADER;
1601 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1602 if (FAILED(hr))
1603 return hr;
1605 /* get "avih" chunk */
1606 ck.ckid = ckidAVIMAINHDR;
1607 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1608 if (FAILED(hr))
1609 return hr;
1611 if (ck.cksize != sizeof(MainAVIHdr)) {
1612 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1613 return AVIERR_BADFORMAT;
1615 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1616 return AVIERR_FILEREAD;
1618 /* check for MAX_AVISTREAMS limit */
1619 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1620 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1621 return AVIERR_UNSUPPORTED;
1624 /* adjust permissions if copyrighted material in file */
1625 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1626 This->uMode &= ~MMIO_RWMODE;
1627 This->uMode |= MMIO_READ;
1630 /* convert MainAVIHeader into AVIFILINFOW */
1631 memset(&This->fInfo, 0, sizeof(This->fInfo));
1632 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1633 This->fInfo.dwScale = 1000000;
1634 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1635 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1636 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1637 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1638 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1639 This->fInfo.dwSuggestedBufferSize = 0;
1640 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1641 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1642 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1643 ARRAY_SIZE(This->fInfo.szFileType));
1645 /* go back to into header list */
1646 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1647 return AVIERR_FILEREAD;
1649 /* foreach stream exists a "LIST","strl" chunk */
1650 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1651 /* get next nested chunk in this "LIST","strl" */
1652 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1653 return AVIERR_FILEREAD;
1655 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1656 if (ckLIST2.ckid == FOURCC_LIST &&
1657 ckLIST2.fccType == listtypeSTREAMHEADER) {
1658 pStream = This->ppStreams[nStream] =
1659 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1660 if (pStream == NULL)
1661 return AVIERR_MEMORY;
1662 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1664 ck.ckid = 0;
1665 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1666 switch (ck.ckid) {
1667 case ckidSTREAMHANDLERDATA:
1668 if (pStream->lpHandlerData != NULL)
1669 return AVIERR_BADFORMAT;
1670 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1671 if (pStream->lpHandlerData == NULL)
1672 return AVIERR_MEMORY;
1673 pStream->cbHandlerData = ck.cksize;
1675 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1676 return AVIERR_FILEREAD;
1677 break;
1678 case ckidSTREAMFORMAT:
1679 if (pStream->lpFormat != NULL)
1680 return AVIERR_BADFORMAT;
1681 if (ck.cksize == 0)
1682 break;
1684 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1685 if (pStream->lpFormat == NULL)
1686 return AVIERR_MEMORY;
1687 pStream->cbFormat = ck.cksize;
1689 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1690 return AVIERR_FILEREAD;
1692 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1693 LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1695 /* some corrections to the video format */
1696 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1697 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1698 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1699 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1700 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1701 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1702 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1703 lpbi->biCompression = BI_RLE8;
1705 if (lpbi->biCompression == BI_RGB &&
1706 (pStream->sInfo.fccHandler == 0 ||
1707 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1708 pStream->sInfo.fccHandler = comptypeDIB;
1710 /* init rcFrame if it's empty */
1711 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1712 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1714 break;
1715 case ckidSTREAMHEADER:
1717 AVIStreamHeader streamHdr;
1718 WCHAR szType[25];
1719 UINT count;
1720 LONG n = ck.cksize;
1722 if (ck.cksize > sizeof(streamHdr))
1723 n = sizeof(streamHdr);
1725 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1726 return AVIERR_FILEREAD;
1728 pStream->sInfo.fccType = streamHdr.fccType;
1729 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1730 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1731 pStream->sInfo.wPriority = streamHdr.wPriority;
1732 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1733 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1734 pStream->sInfo.dwScale = streamHdr.dwScale;
1735 pStream->sInfo.dwRate = streamHdr.dwRate;
1736 pStream->sInfo.dwStart = streamHdr.dwStart;
1737 pStream->sInfo.dwLength = streamHdr.dwLength;
1738 pStream->sInfo.dwSuggestedBufferSize = 0;
1739 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1740 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1741 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1742 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1743 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1744 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1745 pStream->sInfo.dwEditCount = 0;
1746 pStream->sInfo.dwFormatChangeCount = 0;
1748 /* generate description for stream like "filename.avi Type #n" */
1749 if (streamHdr.fccType == streamtypeVIDEO)
1750 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, ARRAY_SIZE(szType));
1751 else if (streamHdr.fccType == streamtypeAUDIO)
1752 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, ARRAY_SIZE(szType));
1753 else
1754 wsprintfW(szType, L"%4.4hs", (char*)&streamHdr.fccType);
1756 /* get count of this streamtype up to this stream */
1757 count = 0;
1758 for (n = nStream; 0 <= n; n--) {
1759 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1760 count++;
1763 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1765 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1766 wsprintfW(pStream->sInfo.szName, L"%s %s #%d",
1767 AVIFILE_BasenameW(This->szFileName), szType, count);
1769 break;
1770 case ckidSTREAMNAME:
1771 { /* streamname will be saved as ASCII string */
1772 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1773 if (str == NULL)
1774 return AVIERR_MEMORY;
1776 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1778 HeapFree(GetProcessHeap(), 0, str);
1779 return AVIERR_FILEREAD;
1782 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1783 ARRAY_SIZE(pStream->sInfo.szName));
1785 HeapFree(GetProcessHeap(), 0, str);
1787 break;
1788 case ckidAVIPADDING:
1789 case mmioFOURCC('p','a','d','d'):
1790 break;
1791 default:
1792 WARN(": found extra chunk 0x%08X\n", ck.ckid);
1793 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1794 if (FAILED(hr))
1795 return hr;
1797 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1799 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1800 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1801 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1802 pStream->sInfo.dwScale = 1;
1803 pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1805 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1806 return AVIERR_FILEREAD;
1808 } else {
1809 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1810 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1811 if (FAILED(hr))
1812 return hr;
1814 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1815 return AVIERR_FILEREAD;
1818 /* read any extra headers in "LIST","hdrl" */
1819 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1820 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1821 return AVIERR_FILEREAD;
1823 /* search "LIST","movi" chunk in "RIFF","AVI " */
1824 ckLIST1.fccType = listtypeAVIMOVIE;
1825 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1826 MMIO_FINDLIST);
1827 if (FAILED(hr))
1828 return hr;
1830 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1831 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1832 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1833 return AVIERR_FILEREAD;
1835 /* try to find an index */
1836 ck.ckid = ckidAVINEWINDEX;
1837 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1838 &ck, &ckRIFF, MMIO_FINDCHUNK);
1839 if (SUCCEEDED(hr) && ck.cksize > 0) {
1840 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1841 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1844 /* when we haven't found an index or it's bad, then build one
1845 * by parsing 'movi' chunk */
1846 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1847 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1848 This->ppStreams[nStream]->lLastFrame = -1;
1850 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1851 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1852 return AVIERR_FILEREAD;
1855 /* seek through the 'movi' list until end */
1856 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1857 if (ck.ckid != FOURCC_LIST) {
1858 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1859 nStream = StreamFromFOURCC(ck.ckid);
1861 if (nStream > This->fInfo.dwStreams)
1862 return AVIERR_BADFORMAT;
1864 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1865 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1866 } else {
1867 nStream = StreamFromFOURCC(ck.ckid);
1868 WARN(": file seems to be truncated!\n");
1869 if (nStream <= This->fInfo.dwStreams &&
1870 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1871 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1872 if (ck.cksize != -1) {
1873 ck.cksize -= ck.dwDataOffset;
1874 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1876 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1877 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1885 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1887 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1888 if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1889 This->fInfo.dwSuggestedBufferSize = sugbuf;
1892 /* find other chunks */
1893 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1895 return AVIERR_OK;
1898 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1900 AVIINDEXENTRY *lp;
1901 DWORD pos, n;
1902 HRESULT hr = AVIERR_OK;
1903 BOOL bAbsolute = TRUE;
1905 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1906 if (lp == NULL)
1907 return AVIERR_MEMORY;
1909 /* adjust limits for index tables, so that inserting will be faster */
1910 for (n = 0; n < This->fInfo.dwStreams; n++) {
1911 IAVIStreamImpl *pStream = This->ppStreams[n];
1913 pStream->lLastFrame = -1;
1915 if (pStream->idxFrames != NULL) {
1916 HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1917 pStream->idxFrames = NULL;
1918 pStream->nIdxFrames = 0;
1921 if (pStream->sInfo.dwSampleSize != 0) {
1922 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1923 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1924 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1925 pStream->nIdxFrames =
1926 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1928 } else
1929 pStream->nIdxFrames = pStream->sInfo.dwLength;
1931 pStream->idxFrames =
1932 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1933 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1934 pStream->nIdxFrames = 0;
1935 HeapFree(GetProcessHeap(), 0, lp);
1936 return AVIERR_MEMORY;
1940 pos = (DWORD)-1;
1941 while (size != 0) {
1942 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1944 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1945 hr = AVIERR_FILEREAD;
1946 break;
1948 size -= read;
1950 if (pos == (DWORD)-1)
1951 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1953 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1954 pos, &bAbsolute);
1957 HeapFree(GetProcessHeap(), 0, lp);
1959 /* checking ... */
1960 for (n = 0; n < This->fInfo.dwStreams; n++) {
1961 IAVIStreamImpl *pStream = This->ppStreams[n];
1963 if (pStream->sInfo.dwSampleSize == 0 &&
1964 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1965 ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1966 n, pStream->sInfo.dwLength, pStream->lLastFrame);
1969 return hr;
1972 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1973 LONG count, DWORD pos, BOOL *bAbsolute)
1975 if (lp == NULL)
1976 return AVIERR_BADPARAM;
1978 for (; count > 0; count--, lp++) {
1979 WORD nStream = StreamFromFOURCC(lp->ckid);
1981 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1982 continue; /* skip these */
1984 if (nStream > This->fInfo.dwStreams)
1985 return AVIERR_BADFORMAT;
1987 /* Video frames can be either indexed in a relative position to the
1988 * "movi" chunk or in a absolute position in the file. If the index
1989 * is relative the frame offset will always be so small that it will
1990 * virtually never reach the "movi" offset so we can detect if the
1991 * video is relative very fast.
1993 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1994 *bAbsolute = FALSE;
1996 if (!*bAbsolute)
1997 lp->dwChunkOffset += pos; /* make the offset absolute */
1999 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
2000 return AVIERR_MEMORY;
2003 return AVIERR_OK;
2006 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
2007 LPVOID buffer, DWORD size)
2009 /* pre-conditions */
2010 assert(This != NULL);
2011 assert(This->paf != NULL);
2012 assert(This->paf->hmmio != NULL);
2013 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2014 assert(pos <= This->lLastFrame);
2016 /* should we read as much as block gives us? */
2017 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2018 size = This->idxFrames[pos].dwChunkLength;
2020 /* read into out own buffer or given one? */
2021 if (buffer == NULL) {
2022 /* we also read the chunk */
2023 size += 2 * sizeof(DWORD);
2025 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2026 if (This->lpBuffer == NULL || This->cbBuffer < size) {
2027 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2029 if (This->lpBuffer == NULL) {
2030 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2031 if (!This->lpBuffer) return AVIERR_MEMORY;
2032 } else {
2033 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
2034 if (!new_buffer) return AVIERR_MEMORY;
2035 This->lpBuffer = new_buffer;
2037 This->cbBuffer = maxSize;
2040 /* now read the complete chunk into our buffer */
2041 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2042 return AVIERR_FILEREAD;
2043 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2044 return AVIERR_FILEREAD;
2046 /* check if it was the correct block which we have read */
2047 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2048 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2049 ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
2050 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2051 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2052 This->idxFrames[pos].dwChunkLength);
2053 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n",
2054 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2055 return AVIERR_FILEREAD;
2057 } else {
2058 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2059 return AVIERR_FILEREAD;
2060 if (mmioRead(This->paf->hmmio, buffer, size) != size)
2061 return AVIERR_FILEREAD;
2064 return AVIERR_OK;
2067 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2069 LONG block;
2071 /* pre-conditions */
2072 assert(This != NULL);
2073 assert(pos != NULL);
2074 assert(offset != NULL);
2075 assert(This->sInfo.dwSampleSize != 0);
2076 assert(*pos >= This->sInfo.dwStart);
2078 /* convert start sample to start bytes */
2079 (*offset) = (*pos) - This->sInfo.dwStart;
2080 (*offset) *= This->sInfo.dwSampleSize;
2082 /* convert bytes to block number */
2083 for (block = 0; block <= This->lLastFrame; block++) {
2084 if (This->idxFrames[block].dwChunkLength <= *offset)
2085 (*offset) -= This->idxFrames[block].dwChunkLength;
2086 else
2087 break;
2090 *pos = block;
2093 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2095 MainAVIHeader MainAVIHdr;
2096 IAVIStreamImpl* pStream;
2097 MMCKINFO ckRIFF;
2098 MMCKINFO ckLIST1;
2099 MMCKINFO ckLIST2;
2100 MMCKINFO ck;
2101 DWORD nStream;
2102 DWORD dwPos;
2103 HRESULT hr;
2105 /* initialize some things */
2106 if (This->dwMoviChunkPos == 0)
2107 AVIFILE_ComputeMoviStart(This);
2109 /* written one record too much? */
2110 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2111 This->dwNextFramePos -= 3 * sizeof(DWORD);
2112 if (This->nIdxRecords > 0)
2113 This->nIdxRecords--;
2116 AVIFILE_UpdateInfo(This);
2118 assert(This->fInfo.dwScale != 0);
2120 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2121 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2122 This->fInfo.dwScale);
2123 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2124 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2125 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2126 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2127 MainAVIHdr.dwInitialFrames = 0;
2128 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2129 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2130 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2131 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2132 MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2134 /* now begin writing ... */
2135 mmioSeek(This->hmmio, 0, SEEK_SET);
2137 /* RIFF chunk */
2138 ckRIFF.cksize = 0;
2139 ckRIFF.fccType = formtypeAVI;
2140 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2141 return AVIERR_FILEWRITE;
2143 /* AVI headerlist */
2144 ckLIST1.cksize = 0;
2145 ckLIST1.fccType = listtypeAVIHEADER;
2146 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2147 return AVIERR_FILEWRITE;
2149 /* MainAVIHeader */
2150 ck.ckid = ckidAVIMAINHDR;
2151 ck.cksize = sizeof(MainAVIHdr);
2152 ck.fccType = 0;
2153 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2154 return AVIERR_FILEWRITE;
2155 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2156 return AVIERR_FILEWRITE;
2157 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2158 return AVIERR_FILEWRITE;
2160 /* write the headers of each stream into a separate streamheader list */
2161 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2162 AVIStreamHeader strHdr;
2164 pStream = This->ppStreams[nStream];
2166 /* begin the new streamheader list */
2167 ckLIST2.cksize = 0;
2168 ckLIST2.fccType = listtypeSTREAMHEADER;
2169 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2170 return AVIERR_FILEWRITE;
2172 /* create an AVIStreamHeader from the AVSTREAMINFO */
2173 strHdr.fccType = pStream->sInfo.fccType;
2174 strHdr.fccHandler = pStream->sInfo.fccHandler;
2175 strHdr.dwFlags = pStream->sInfo.dwFlags;
2176 strHdr.wPriority = pStream->sInfo.wPriority;
2177 strHdr.wLanguage = pStream->sInfo.wLanguage;
2178 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2179 strHdr.dwScale = pStream->sInfo.dwScale;
2180 strHdr.dwRate = pStream->sInfo.dwRate;
2181 strHdr.dwStart = pStream->sInfo.dwStart;
2182 strHdr.dwLength = pStream->sInfo.dwLength;
2183 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2184 strHdr.dwQuality = pStream->sInfo.dwQuality;
2185 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2186 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2187 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2188 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2189 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2191 /* now write the AVIStreamHeader */
2192 ck.ckid = ckidSTREAMHEADER;
2193 ck.cksize = sizeof(strHdr);
2194 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2195 return AVIERR_FILEWRITE;
2196 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2197 return AVIERR_FILEWRITE;
2198 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2199 return AVIERR_FILEWRITE;
2201 /* ... the hopefully ever present streamformat ... */
2202 ck.ckid = ckidSTREAMFORMAT;
2203 ck.cksize = pStream->cbFormat;
2204 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2205 return AVIERR_FILEWRITE;
2206 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2207 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2208 return AVIERR_FILEWRITE;
2210 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2211 return AVIERR_FILEWRITE;
2213 /* ... some optional existing handler data ... */
2214 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2215 ck.ckid = ckidSTREAMHANDLERDATA;
2216 ck.cksize = pStream->cbHandlerData;
2217 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2218 return AVIERR_FILEWRITE;
2219 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2220 return AVIERR_FILEWRITE;
2221 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2222 return AVIERR_FILEWRITE;
2225 /* ... some optional additional extra chunk for this stream ... */
2226 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2227 /* the chunk header(s) are already in the structure */
2228 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2229 return AVIERR_FILEWRITE;
2232 /* ... an optional name for this stream ... */
2233 if (pStream->sInfo.szName[0]) {
2234 LPSTR str;
2236 ck.ckid = ckidSTREAMNAME;
2237 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2238 if (ck.cksize & 1) /* align */
2239 ck.cksize++;
2240 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2241 return AVIERR_FILEWRITE;
2243 /* the streamname must be saved in ASCII not Unicode */
2244 str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
2245 if (str == NULL)
2246 return AVIERR_MEMORY;
2247 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2248 ck.cksize, NULL, NULL);
2250 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2251 HeapFree(GetProcessHeap(), 0, str);
2252 return AVIERR_FILEWRITE;
2255 HeapFree(GetProcessHeap(), 0, str);
2256 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2257 return AVIERR_FILEWRITE;
2260 /* close streamheader list for this stream */
2261 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2262 return AVIERR_FILEWRITE;
2263 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2265 /* close the aviheader list */
2266 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2267 return AVIERR_FILEWRITE;
2269 /* check for padding to pre-guessed 'movi'-chunk position */
2270 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2271 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2272 ck.ckid = ckidAVIPADDING;
2273 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2274 assert((LONG)ck.cksize >= 0);
2276 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2277 return AVIERR_FILEWRITE;
2278 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2279 return AVIERR_FILEWRITE;
2280 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2281 return AVIERR_FILEWRITE;
2284 /* now write the 'movi' chunk */
2285 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2286 ckLIST1.cksize = 0;
2287 ckLIST1.fccType = listtypeAVIMOVIE;
2288 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2289 return AVIERR_FILEWRITE;
2290 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2291 return AVIERR_FILEWRITE;
2292 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2293 return AVIERR_FILEWRITE;
2295 /* write 'idx1' chunk */
2296 hr = AVIFILE_SaveIndex(This);
2297 if (FAILED(hr))
2298 return hr;
2300 /* write optional extra file chunks */
2301 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2302 /* as for the streams, are the chunk header(s) in the structure */
2303 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2304 return AVIERR_FILEWRITE;
2307 /* close RIFF chunk */
2308 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2309 return AVIERR_FILEWRITE;
2311 /* add some JUNK at end for bad parsers */
2312 memset(&ckRIFF, 0, sizeof(ckRIFF));
2313 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2314 mmioFlush(This->hmmio, 0);
2316 return AVIERR_OK;
2319 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2321 IAVIStreamImpl *pStream;
2322 AVIINDEXENTRY idx;
2323 MMCKINFO ck;
2324 DWORD nStream;
2325 LONG n;
2327 ck.ckid = ckidAVINEWINDEX;
2328 ck.cksize = 0;
2329 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2330 return AVIERR_FILEWRITE;
2332 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2333 /* is interleaved -- write block of corresponding frames */
2334 LONG lInitialFrames = 0;
2335 LONG stepsize;
2336 LONG i;
2338 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2339 stepsize = 1;
2340 else
2341 stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000);
2343 assert(stepsize > 0);
2345 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2346 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2347 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2350 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2351 i += stepsize) {
2352 DWORD nFrame = lInitialFrames + i;
2354 assert(nFrame < This->nIdxRecords);
2356 idx.ckid = listtypeAVIRECORD;
2357 idx.dwFlags = AVIIF_LIST;
2358 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2359 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2360 - This->dwMoviChunkPos;
2361 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2362 return AVIERR_FILEWRITE;
2364 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2365 pStream = This->ppStreams[nStream];
2367 /* heave we reached start of this stream? */
2368 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2369 continue;
2371 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2372 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2374 /* reached end of this stream? */
2375 if (pStream->lLastFrame <= nFrame)
2376 continue;
2378 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2379 pStream->sInfo.dwFormatChangeCount != 0 &&
2380 pStream->idxFmtChanges != NULL) {
2381 DWORD pos;
2383 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2384 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2385 idx.dwFlags = AVIIF_NOTIME;
2386 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2387 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2388 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2389 - This->dwMoviChunkPos;
2391 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2392 return AVIERR_FILEWRITE;
2393 break;
2396 } /* if have formatchanges */
2398 idx.ckid = pStream->idxFrames[nFrame].ckid;
2399 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2400 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2401 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2402 - This->dwMoviChunkPos;
2403 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2404 return AVIERR_FILEWRITE;
2407 } else {
2408 /* not interleaved -- write index for each stream at once */
2409 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2410 pStream = This->ppStreams[nStream];
2412 for (n = 0; n <= pStream->lLastFrame; n++) {
2413 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2414 (pStream->sInfo.dwFormatChangeCount != 0)) {
2415 DWORD pos;
2417 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2418 if (pStream->idxFmtChanges[pos].ckid == n) {
2419 idx.dwFlags = AVIIF_NOTIME;
2420 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2421 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2422 idx.dwChunkOffset =
2423 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2424 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2425 return AVIERR_FILEWRITE;
2426 break;
2429 } /* if have formatchanges */
2431 idx.ckid = pStream->idxFrames[n].ckid;
2432 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2433 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2434 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2435 - This->dwMoviChunkPos;
2437 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2438 return AVIERR_FILEWRITE;
2441 } /* if not interleaved */
2443 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2444 return AVIERR_FILEWRITE;
2446 return AVIERR_OK;
2449 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2451 UINT i;
2452 UINT nStream;
2454 /* pre-condition */
2455 assert(lSkip >= 0);
2457 if (fcc != 0) {
2458 /* search the number of the specified stream */
2459 nStream = (ULONG)-1;
2460 for (i = 0; i < This->fInfo.dwStreams; i++) {
2461 assert(This->ppStreams[i] != NULL);
2463 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2464 if (lSkip == 0) {
2465 nStream = i;
2466 break;
2467 } else
2468 lSkip--;
2471 } else
2472 nStream = lSkip;
2474 return nStream;
2477 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2479 UINT i;
2481 /* pre-conditions */
2482 assert(This != NULL);
2484 This->fInfo.dwMaxBytesPerSec = 0;
2485 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2486 This->fInfo.dwSuggestedBufferSize = 0;
2487 This->fInfo.dwWidth = 0;
2488 This->fInfo.dwHeight = 0;
2489 This->fInfo.dwScale = 0;
2490 This->fInfo.dwRate = 0;
2491 This->fInfo.dwLength = 0;
2492 This->dwInitialFrames = 0;
2494 for (i = 0; i < This->fInfo.dwStreams; i++) {
2495 AVISTREAMINFOW *psi;
2496 DWORD n;
2498 /* pre-conditions */
2499 assert(This->ppStreams[i] != NULL);
2501 psi = &This->ppStreams[i]->sInfo;
2502 assert(psi->dwScale != 0);
2503 assert(psi->dwRate != 0);
2505 if (i == 0) {
2506 /* use first stream timings as base */
2507 This->fInfo.dwScale = psi->dwScale;
2508 This->fInfo.dwRate = psi->dwRate;
2509 This->fInfo.dwLength = psi->dwLength;
2510 } else {
2511 n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface,
2512 &This->ppStreams[i]->IAVIStream_iface, psi->dwLength);
2513 if (This->fInfo.dwLength < n)
2514 This->fInfo.dwLength = n;
2517 if (This->dwInitialFrames < psi->dwInitialFrames)
2518 This->dwInitialFrames = psi->dwInitialFrames;
2520 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2521 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2523 if (psi->dwSampleSize != 0) {
2524 /* fixed sample size -- exact computation */
2525 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2526 psi->dwScale);
2527 } else {
2528 /* variable sample size -- only upper limit */
2529 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2530 psi->dwRate, psi->dwScale);
2532 /* update dimensions */
2533 n = psi->rcFrame.right - psi->rcFrame.left;
2534 if (This->fInfo.dwWidth < n)
2535 This->fInfo.dwWidth = n;
2536 n = psi->rcFrame.bottom - psi->rcFrame.top;
2537 if (This->fInfo.dwHeight < n)
2538 This->fInfo.dwHeight = n;
2543 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2544 FOURCC ckid, DWORD flags, LPCVOID buffer,
2545 LONG size)
2547 MMCKINFO ck;
2549 ck.ckid = ckid;
2550 ck.cksize = size;
2551 ck.fccType = 0;
2553 /* if no frame/block is already written, we must compute start of movi chunk */
2554 if (This->paf->dwMoviChunkPos == 0)
2555 AVIFILE_ComputeMoviStart(This->paf);
2557 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2558 return AVIERR_FILEWRITE;
2560 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2561 return AVIERR_FILEWRITE;
2562 if (buffer != NULL && size > 0) {
2563 if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2564 return AVIERR_FILEWRITE;
2566 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2567 return AVIERR_FILEWRITE;
2569 This->paf->fDirty = TRUE;
2570 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2572 return AVIFILE_AddFrame(This, ckid, size,
2573 ck.dwDataOffset - 2 * sizeof(DWORD), flags);