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
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.
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 #include "avifile_private.h"
36 #define IDX_PER_BLOCK 2730
39 typedef struct _IAVIFileImpl IAVIFileImpl
;
41 typedef struct _IAVIStreamImpl
{
42 IAVIStream IAVIStream_iface
;
46 DWORD nStream
; /* the n-th stream in file */
58 DWORD cbBuffer
; /* size of lpBuffer */
59 DWORD dwCurrentFrame
; /* frame/block currently in lpBuffer */
61 LONG lLastFrame
; /* last correct index in idxFrames */
62 AVIINDEXENTRY
*idxFrames
;
63 DWORD nIdxFrames
; /* upper index limit of idxFrames */
64 AVIINDEXENTRY
*idxFmtChanges
;
65 DWORD nIdxFmtChanges
; /* upper index limit of idxFmtChanges */
68 static inline IAVIStreamImpl
*impl_from_IAVIStream(IAVIStream
*iface
)
70 return CONTAINING_RECORD(iface
, IAVIStreamImpl
, IAVIStream_iface
);
73 struct _IAVIFileImpl
{
74 IUnknown IUnknown_inner
;
75 IAVIFile IAVIFile_iface
;
76 IPersistFile IPersistFile_iface
;
81 IAVIStreamImpl
*ppStreams
[MAX_AVISTREAMS
];
83 EXTRACHUNKS fileextra
;
85 DWORD dwMoviChunkPos
; /* some stuff for saving ... */
88 DWORD dwInitialFrames
;
90 MMCKINFO ckLastRecord
;
91 AVIINDEXENTRY
*idxRecords
; /* won't be updated while loading */
92 DWORD nIdxRecords
; /* current fill level */
93 DWORD cbIdxRecords
; /* size of idxRecords */
95 /* IPersistFile stuff ... */
102 static inline IAVIFileImpl
*impl_from_IUnknown(IUnknown
*iface
)
104 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IUnknown_inner
);
107 static inline IAVIFileImpl
*impl_from_IAVIFile(IAVIFile
*iface
)
109 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IAVIFile_iface
);
112 static inline IAVIFileImpl
*impl_from_IPersistFile(IPersistFile
*iface
)
114 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IPersistFile_iface
);
117 /***********************************************************************/
119 static HRESULT
AVIFILE_AddFrame(IAVIStreamImpl
*This
, DWORD ckid
, DWORD size
,
120 DWORD offset
, DWORD flags
);
121 static HRESULT
AVIFILE_AddRecord(IAVIFileImpl
*This
);
122 static DWORD
AVIFILE_ComputeMoviStart(IAVIFileImpl
*This
);
123 static void AVIFILE_ConstructAVIStream(IAVIFileImpl
*paf
, DWORD nr
,
124 const AVISTREAMINFOW
*asi
);
125 static void AVIFILE_DestructAVIStream(IAVIStreamImpl
*This
);
126 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
);
127 static HRESULT
AVIFILE_LoadIndex(const IAVIFileImpl
*This
, DWORD size
, DWORD offset
);
128 static HRESULT
AVIFILE_ParseIndex(const IAVIFileImpl
*This
, AVIINDEXENTRY
*lp
,
129 LONG count
, DWORD pos
, BOOL
*bAbsolute
);
130 static HRESULT
AVIFILE_ReadBlock(IAVIStreamImpl
*This
, DWORD start
,
131 LPVOID buffer
, DWORD size
);
132 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl
*This
, LPLONG pos
,
134 static HRESULT
AVIFILE_SaveFile(IAVIFileImpl
*This
);
135 static HRESULT
AVIFILE_SaveIndex(const IAVIFileImpl
*This
);
136 static ULONG
AVIFILE_SearchStream(const IAVIFileImpl
*This
, DWORD fccType
,
138 static void AVIFILE_UpdateInfo(IAVIFileImpl
*This
);
139 static HRESULT
AVIFILE_WriteBlock(IAVIStreamImpl
*This
, DWORD block
,
140 FOURCC ckid
, DWORD flags
, LPCVOID buffer
,
143 static HRESULT WINAPI
IUnknown_fnQueryInterface(IUnknown
*iface
, REFIID riid
, void **ppv
)
145 IAVIFileImpl
*This
= impl_from_IUnknown(iface
);
147 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
150 WARN("invalid parameter\n");
155 if (IsEqualIID(riid
, &IID_IUnknown
))
156 *ppv
= &This
->IUnknown_inner
;
157 else if (IsEqualIID(riid
, &IID_IAVIFile
))
158 *ppv
= &This
->IAVIFile_iface
;
159 else if (IsEqualGUID(riid
, &IID_IPersistFile
))
160 *ppv
= &This
->IPersistFile_iface
;
162 WARN("unknown IID %s\n", debugstr_guid(riid
));
163 return E_NOINTERFACE
;
166 /* Violation of the COM aggregation ref counting rule */
167 IUnknown_AddRef(&This
->IUnknown_inner
);
171 static ULONG WINAPI
IUnknown_fnAddRef(IUnknown
*iface
)
173 IAVIFileImpl
*This
= impl_from_IUnknown(iface
);
174 ULONG ref
= InterlockedIncrement(&This
->ref
);
176 TRACE("(%p) ref=%d\n", This
, ref
);
181 static ULONG WINAPI
IUnknown_fnRelease(IUnknown
*iface
)
183 IAVIFileImpl
*This
= impl_from_IUnknown(iface
);
184 ULONG ref
= InterlockedDecrement(&This
->ref
);
187 TRACE("(%p) ref=%d\n", This
, ref
);
191 AVIFILE_SaveFile(This
);
193 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
194 if (This
->ppStreams
[i
] != NULL
) {
195 if (This
->ppStreams
[i
]->ref
!= 0)
196 ERR(": someone has still %u reference to stream %u (%p)!\n",
197 This
->ppStreams
[i
]->ref
, i
, This
->ppStreams
[i
]);
198 AVIFILE_DestructAVIStream(This
->ppStreams
[i
]);
199 HeapFree(GetProcessHeap(), 0, This
->ppStreams
[i
]);
200 This
->ppStreams
[i
] = NULL
;
204 if (This
->idxRecords
!= NULL
) {
205 HeapFree(GetProcessHeap(), 0, This
->idxRecords
);
206 This
->idxRecords
= NULL
;
207 This
->nIdxRecords
= 0;
210 if (This
->fileextra
.lp
!= NULL
) {
211 HeapFree(GetProcessHeap(), 0, This
->fileextra
.lp
);
212 This
->fileextra
.lp
= NULL
;
213 This
->fileextra
.cb
= 0;
216 HeapFree(GetProcessHeap(), 0, This
->szFileName
);
217 This
->szFileName
= NULL
;
219 if (This
->hmmio
!= NULL
) {
220 mmioClose(This
->hmmio
, 0);
224 HeapFree(GetProcessHeap(), 0, This
);
229 static const IUnknownVtbl unk_vtbl
=
231 IUnknown_fnQueryInterface
,
236 static HRESULT WINAPI
IAVIFile_fnQueryInterface(IAVIFile
*iface
, REFIID riid
, void **ppv
)
238 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
240 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ppv
);
243 static ULONG WINAPI
IAVIFile_fnAddRef(IAVIFile
*iface
)
245 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
247 return IUnknown_AddRef(This
->outer_unk
);
250 static ULONG WINAPI
IAVIFile_fnRelease(IAVIFile
*iface
)
252 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
254 return IUnknown_Release(This
->outer_unk
);
257 static HRESULT WINAPI
IAVIFile_fnInfo(IAVIFile
*iface
, AVIFILEINFOW
*afi
, LONG size
)
259 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
261 TRACE("(%p,%p,%d)\n",iface
,afi
,size
);
264 return AVIERR_BADPARAM
;
266 return AVIERR_BADSIZE
;
268 AVIFILE_UpdateInfo(This
);
270 memcpy(afi
, &This
->fInfo
, min((DWORD
)size
, sizeof(This
->fInfo
)));
272 if ((DWORD
)size
< sizeof(This
->fInfo
))
273 return AVIERR_BUFFERTOOSMALL
;
277 static HRESULT WINAPI
IAVIFile_fnGetStream(IAVIFile
*iface
, IAVIStream
**avis
, DWORD fccType
,
280 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
283 TRACE("(%p,%p,0x%08X,%d)\n", iface
, avis
, fccType
, lParam
);
285 if (avis
== NULL
|| lParam
< 0)
286 return AVIERR_BADPARAM
;
288 nStream
= AVIFILE_SearchStream(This
, fccType
, lParam
);
290 /* Does the requested stream exist? */
291 if (nStream
< This
->fInfo
.dwStreams
&&
292 This
->ppStreams
[nStream
] != NULL
) {
293 *avis
= &This
->ppStreams
[nStream
]->IAVIStream_iface
;
294 IAVIStream_AddRef(*avis
);
299 /* Sorry, but the specified stream doesn't exist */
300 return AVIERR_NODATA
;
303 static HRESULT WINAPI
IAVIFile_fnCreateStream(IAVIFile
*iface
, IAVIStream
**avis
,
306 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
309 TRACE("(%p,%p,%p)\n", iface
, avis
, asi
);
311 /* check parameters */
312 if (avis
== NULL
|| asi
== NULL
)
313 return AVIERR_BADPARAM
;
317 /* Does the user have write permission? */
318 if ((This
->uMode
& MMIO_RWMODE
) == 0)
319 return AVIERR_READONLY
;
321 /* Can we add another stream? */
322 n
= This
->fInfo
.dwStreams
;
323 if (n
>= MAX_AVISTREAMS
|| This
->dwMoviChunkPos
!= 0) {
324 /* already reached max nr of streams
325 * or have already written frames to disk */
326 return AVIERR_UNSUPPORTED
;
329 /* check AVISTREAMINFO for some really needed things */
330 if (asi
->fccType
== 0 || asi
->dwScale
== 0 || asi
->dwRate
== 0)
331 return AVIERR_BADFORMAT
;
333 /* now it seems to be save to add the stream */
334 assert(This
->ppStreams
[n
] == NULL
);
335 This
->ppStreams
[n
] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
336 sizeof(IAVIStreamImpl
));
337 if (This
->ppStreams
[n
] == NULL
)
338 return AVIERR_MEMORY
;
340 /* initialize the new allocated stream */
341 AVIFILE_ConstructAVIStream(This
, n
, asi
);
343 This
->fInfo
.dwStreams
++;
346 /* update our AVIFILEINFO structure */
347 AVIFILE_UpdateInfo(This
);
350 *avis
= &This
->ppStreams
[n
]->IAVIStream_iface
;
351 IAVIStream_AddRef(*avis
);
356 static HRESULT WINAPI
IAVIFile_fnWriteData(IAVIFile
*iface
, DWORD ckid
, void *lpData
, LONG size
)
358 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
360 TRACE("(%p,0x%08X,%p,%d)\n", iface
, ckid
, lpData
, size
);
362 /* check parameters */
364 return AVIERR_BADPARAM
;
366 return AVIERR_BADSIZE
;
368 /* Do we have write permission? */
369 if ((This
->uMode
& MMIO_RWMODE
) == 0)
370 return AVIERR_READONLY
;
374 return WriteExtraChunk(&This
->fileextra
, ckid
, lpData
, size
);
377 static HRESULT WINAPI
IAVIFile_fnReadData(IAVIFile
*iface
, DWORD ckid
, void *lpData
, LONG
*size
)
379 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
381 TRACE("(%p,0x%08X,%p,%p)\n", iface
, ckid
, lpData
, size
);
383 return ReadExtraChunk(&This
->fileextra
, ckid
, lpData
, size
);
386 static HRESULT WINAPI
IAVIFile_fnEndRecord(IAVIFile
*iface
)
388 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
390 TRACE("(%p)\n",iface
);
392 if ((This
->uMode
& MMIO_RWMODE
) == 0)
393 return AVIERR_READONLY
;
397 /* no frames written to any stream? -- compute start of 'movi'-chunk */
398 if (This
->dwMoviChunkPos
== 0)
399 AVIFILE_ComputeMoviStart(This
);
401 This
->fInfo
.dwFlags
|= AVIFILEINFO_ISINTERLEAVED
;
403 /* already written frames to any stream, ... */
404 if (This
->ckLastRecord
.dwFlags
& MMIO_DIRTY
) {
405 /* close last record */
406 if (mmioAscend(This
->hmmio
, &This
->ckLastRecord
, 0) != 0)
407 return AVIERR_FILEWRITE
;
409 AVIFILE_AddRecord(This
);
411 if (This
->fInfo
.dwSuggestedBufferSize
< This
->ckLastRecord
.cksize
+ 3 * sizeof(DWORD
))
412 This
->fInfo
.dwSuggestedBufferSize
= This
->ckLastRecord
.cksize
+ 3 * sizeof(DWORD
);
415 /* write out a new record into file, but don't close it */
416 This
->ckLastRecord
.cksize
= 0;
417 This
->ckLastRecord
.fccType
= listtypeAVIRECORD
;
418 if (mmioSeek(This
->hmmio
, This
->dwNextFramePos
, SEEK_SET
) == -1)
419 return AVIERR_FILEWRITE
;
420 if (mmioCreateChunk(This
->hmmio
, &This
->ckLastRecord
, MMIO_CREATELIST
) != 0)
421 return AVIERR_FILEWRITE
;
422 This
->dwNextFramePos
+= 3 * sizeof(DWORD
);
427 static HRESULT WINAPI
IAVIFile_fnDeleteStream(IAVIFile
*iface
, DWORD fccType
, LONG lParam
)
429 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
432 TRACE("(%p,0x%08X,%d)\n", iface
, fccType
, lParam
);
434 /* check parameter */
436 return AVIERR_BADPARAM
;
438 /* Have user write permissions? */
439 if ((This
->uMode
& MMIO_RWMODE
) == 0)
440 return AVIERR_READONLY
;
442 nStream
= AVIFILE_SearchStream(This
, fccType
, lParam
);
444 /* Does the requested stream exist? */
445 if (nStream
< This
->fInfo
.dwStreams
&&
446 This
->ppStreams
[nStream
] != NULL
) {
447 /* ... so delete it now */
448 HeapFree(GetProcessHeap(), 0, This
->ppStreams
[nStream
]);
449 This
->fInfo
.dwStreams
--;
450 if (nStream
< This
->fInfo
.dwStreams
)
451 memmove(&This
->ppStreams
[nStream
], &This
->ppStreams
[nStream
+ 1],
452 (This
->fInfo
.dwStreams
- nStream
) * sizeof(This
->ppStreams
[0]));
454 This
->ppStreams
[This
->fInfo
.dwStreams
] = NULL
;
457 /* This->fInfo will be updated further when asked for */
460 return AVIERR_NODATA
;
463 static const struct IAVIFileVtbl avif_vt
= {
464 IAVIFile_fnQueryInterface
,
468 IAVIFile_fnGetStream
,
469 IAVIFile_fnCreateStream
,
470 IAVIFile_fnWriteData
,
472 IAVIFile_fnEndRecord
,
473 IAVIFile_fnDeleteStream
477 static HRESULT WINAPI
IPersistFile_fnQueryInterface(IPersistFile
*iface
, REFIID riid
, void **ppv
)
479 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
481 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ppv
);
484 static ULONG WINAPI
IPersistFile_fnAddRef(IPersistFile
*iface
)
486 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
488 return IUnknown_AddRef(This
->outer_unk
);
491 static ULONG WINAPI
IPersistFile_fnRelease(IPersistFile
*iface
)
493 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
495 return IUnknown_Release(This
->outer_unk
);
498 static HRESULT WINAPI
IPersistFile_fnGetClassID(IPersistFile
*iface
, LPCLSID pClassID
)
500 TRACE("(%p,%p)\n", iface
, pClassID
);
502 if (pClassID
== NULL
)
503 return AVIERR_BADPARAM
;
505 *pClassID
= CLSID_AVIFile
;
510 static HRESULT WINAPI
IPersistFile_fnIsDirty(IPersistFile
*iface
)
512 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
514 TRACE("(%p)\n", iface
);
516 return (This
->fDirty
? S_OK
: S_FALSE
);
519 static HRESULT WINAPI
IPersistFile_fnLoad(IPersistFile
*iface
, LPCOLESTR pszFileName
, DWORD dwMode
)
521 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
524 TRACE("(%p,%s,0x%08X)\n", iface
, debugstr_w(pszFileName
), dwMode
);
526 /* check parameter */
527 if (pszFileName
== NULL
)
528 return AVIERR_BADPARAM
;
530 if (This
->hmmio
!= NULL
)
531 return AVIERR_ERROR
; /* No reuse of this object for another file! */
533 /* remember mode and name */
534 This
->uMode
= dwMode
;
536 len
= lstrlenW(pszFileName
) + 1;
537 This
->szFileName
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
538 if (This
->szFileName
== NULL
)
539 return AVIERR_MEMORY
;
540 lstrcpyW(This
->szFileName
, pszFileName
);
542 /* try to open the file */
543 This
->hmmio
= mmioOpenW(This
->szFileName
, NULL
, MMIO_ALLOCBUF
| dwMode
);
544 if (This
->hmmio
== NULL
) {
545 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
548 len
= WideCharToMultiByte(CP_ACP
, 0, This
->szFileName
, -1, NULL
, 0, NULL
, NULL
);
549 szFileName
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(CHAR
));
550 if (szFileName
== NULL
)
551 return AVIERR_MEMORY
;
553 WideCharToMultiByte(CP_ACP
, 0, This
->szFileName
, -1, szFileName
, len
, NULL
, NULL
);
555 This
->hmmio
= mmioOpenA(szFileName
, NULL
, MMIO_ALLOCBUF
| dwMode
);
556 HeapFree(GetProcessHeap(), 0, szFileName
);
557 if (This
->hmmio
== NULL
)
558 return AVIERR_FILEOPEN
;
561 /* should we create a new file? */
562 if (dwMode
& OF_CREATE
) {
563 memset(& This
->fInfo
, 0, sizeof(This
->fInfo
));
564 This
->fInfo
.dwFlags
= AVIFILEINFO_HASINDEX
| AVIFILEINFO_TRUSTCKTYPE
;
568 return AVIFILE_LoadFile(This
);
571 static HRESULT WINAPI
IPersistFile_fnSave(IPersistFile
*iface
, LPCOLESTR pszFileName
,
574 TRACE("(%p,%s,%d)\n", iface
, debugstr_w(pszFileName
), fRemember
);
576 /* We write directly to disk, so nothing to do. */
581 static HRESULT WINAPI
IPersistFile_fnSaveCompleted(IPersistFile
*iface
, LPCOLESTR pszFileName
)
583 TRACE("(%p,%s)\n", iface
, debugstr_w(pszFileName
));
585 /* We write directly to disk, so nothing to do. */
590 static HRESULT WINAPI
IPersistFile_fnGetCurFile(IPersistFile
*iface
, LPOLESTR
*ppszFileName
)
592 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
594 TRACE("(%p,%p)\n", iface
, ppszFileName
);
596 if (ppszFileName
== NULL
)
597 return AVIERR_BADPARAM
;
599 *ppszFileName
= NULL
;
601 if (This
->szFileName
!= NULL
) {
602 int len
= lstrlenW(This
->szFileName
) + 1;
604 *ppszFileName
= CoTaskMemAlloc(len
* sizeof(WCHAR
));
605 if (*ppszFileName
== NULL
)
606 return AVIERR_MEMORY
;
608 strcpyW(*ppszFileName
, This
->szFileName
);
614 static const struct IPersistFileVtbl pf_vt
= {
615 IPersistFile_fnQueryInterface
,
616 IPersistFile_fnAddRef
,
617 IPersistFile_fnRelease
,
618 IPersistFile_fnGetClassID
,
619 IPersistFile_fnIsDirty
,
622 IPersistFile_fnSaveCompleted
,
623 IPersistFile_fnGetCurFile
626 HRESULT
AVIFILE_CreateAVIFile(IUnknown
*pUnkOuter
, REFIID riid
, void **ppv
)
632 obj
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIFileImpl
));
634 return AVIERR_MEMORY
;
636 obj
->IUnknown_inner
.lpVtbl
= &unk_vtbl
;
637 obj
->IAVIFile_iface
.lpVtbl
= &avif_vt
;
638 obj
->IPersistFile_iface
.lpVtbl
= &pf_vt
;
641 obj
->outer_unk
= pUnkOuter
;
643 obj
->outer_unk
= &obj
->IUnknown_inner
;
645 hr
= IUnknown_QueryInterface(&obj
->IUnknown_inner
, riid
, ppv
);
646 IUnknown_Release(&obj
->IUnknown_inner
);
652 static HRESULT WINAPI
IAVIStream_fnQueryInterface(IAVIStream
*iface
, REFIID riid
, void **ppv
)
654 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
656 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
659 WARN("invalid parameter\n");
664 if (IsEqualGUID(&IID_IUnknown
, riid
) || IsEqualGUID(&IID_IAVIStream
, riid
)) {
666 IAVIStream_AddRef(iface
);
670 /* FIXME: IAVIStreaming interface */
672 return E_NOINTERFACE
;
675 static ULONG WINAPI
IAVIStream_fnAddRef(IAVIStream
*iface
)
677 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
678 ULONG ref
= InterlockedIncrement(&This
->ref
);
680 TRACE("(%p) ref=%d\n", This
, ref
);
682 /* also add ref to parent, so that it doesn't kill us */
683 if (This
->paf
!= NULL
)
684 IAVIFile_AddRef(&This
->paf
->IAVIFile_iface
);
689 static ULONG WINAPI
IAVIStream_fnRelease(IAVIStream
*iface
)
691 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
692 ULONG ref
= InterlockedDecrement(&This
->ref
);
694 TRACE("(%p) ref=%d\n", This
, ref
);
696 if (This
->paf
!= NULL
)
697 IAVIFile_Release(&This
->paf
->IAVIFile_iface
);
702 static HRESULT WINAPI
IAVIStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
, LPARAM lParam2
)
704 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
706 /* This IAVIStream interface needs an AVIFile */
707 return AVIERR_UNSUPPORTED
;
710 static HRESULT WINAPI
IAVIStream_fnInfo(IAVIStream
*iface
, AVISTREAMINFOW
*psi
, LONG size
)
712 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
714 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
717 return AVIERR_BADPARAM
;
719 return AVIERR_BADSIZE
;
721 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
723 if ((DWORD
)size
< sizeof(This
->sInfo
))
724 return AVIERR_BUFFERTOOSMALL
;
728 static LONG WINAPI
IAVIStream_fnFindSample(IAVIStream
*iface
, LONG pos
, LONG flags
)
730 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
733 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
735 if (flags
& FIND_FROM_START
) {
736 pos
= This
->sInfo
.dwStart
;
737 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
741 if (This
->sInfo
.dwSampleSize
!= 0) {
742 /* convert samples into block number with offset */
743 AVIFILE_SamplesToBlock(This
, &pos
, &offset
);
746 if (flags
& FIND_TYPE
) {
747 if (flags
& FIND_KEY
) {
748 while (0 <= pos
&& pos
<= This
->lLastFrame
) {
749 if (This
->idxFrames
[pos
].dwFlags
& AVIIF_KEYFRAME
)
752 if (flags
& FIND_NEXT
)
757 } else if (flags
& FIND_ANY
) {
758 while (0 <= pos
&& pos
<= This
->lLastFrame
) {
759 if (This
->idxFrames
[pos
].dwChunkLength
> 0)
762 if (flags
& FIND_NEXT
)
768 } else if ((flags
& FIND_FORMAT
) && This
->idxFmtChanges
!= NULL
&&
769 This
->sInfo
.fccType
== streamtypeVIDEO
) {
770 if (flags
& FIND_NEXT
) {
773 for (n
= 0; n
< This
->sInfo
.dwFormatChangeCount
; n
++)
774 if (This
->idxFmtChanges
[n
].ckid
>= pos
) {
775 pos
= This
->idxFmtChanges
[n
].ckid
;
781 for (n
= (LONG
)This
->sInfo
.dwFormatChangeCount
; n
>= 0; n
--) {
782 if (This
->idxFmtChanges
[n
].ckid
<= pos
) {
783 pos
= This
->idxFmtChanges
[n
].ckid
;
788 if (pos
> (LONG
)This
->sInfo
.dwStart
)
789 return 0; /* format changes always for first frame */
797 if (pos
< (LONG
)This
->sInfo
.dwStart
)
800 switch (flags
& FIND_RET
) {
803 pos
= This
->idxFrames
[pos
].dwChunkLength
;
806 /* physical position */
807 pos
= This
->idxFrames
[pos
].dwChunkOffset
+ 2 * sizeof(DWORD
)
808 + offset
* This
->sInfo
.dwSampleSize
;
812 if (This
->sInfo
.dwSampleSize
)
813 pos
= This
->sInfo
.dwSampleSize
;
818 FIXME(": FIND_INDEX flag is not supported!\n");
819 /* This is an index in the index-table on disc. */
821 }; /* else logical position */
826 static HRESULT WINAPI
IAVIStream_fnReadFormat(IAVIStream
*iface
, LONG pos
, void *format
,
829 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
831 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
833 if (formatsize
== NULL
)
834 return AVIERR_BADPARAM
;
836 /* only interested in needed buffersize? */
837 if (format
== NULL
|| *formatsize
<= 0) {
838 *formatsize
= This
->cbFormat
;
843 /* copy initial format (only as much as will fit) */
844 memcpy(format
, This
->lpFormat
, min(*(DWORD
*)formatsize
, This
->cbFormat
));
845 if (*(DWORD
*)formatsize
< This
->cbFormat
) {
846 *formatsize
= This
->cbFormat
;
847 return AVIERR_BUFFERTOOSMALL
;
850 /* Could format change? When yes will it change? */
851 if ((This
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
852 pos
> This
->sInfo
.dwStart
) {
855 lLastFmt
= IAVIStream_fnFindSample(iface
, pos
, FIND_FORMAT
|FIND_PREV
);
857 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt
);
861 *formatsize
= This
->cbFormat
;
865 static HRESULT WINAPI
IAVIStream_fnSetFormat(IAVIStream
*iface
, LONG pos
, void *format
,
868 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
869 BITMAPINFOHEADER
*lpbiNew
= format
;
871 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
873 /* check parameters */
874 if (format
== NULL
|| formatsize
<= 0)
875 return AVIERR_BADPARAM
;
877 /* Do we have write permission? */
878 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
879 return AVIERR_READONLY
;
881 /* can only set format before frame is written! */
882 if (This
->lLastFrame
> pos
)
883 return AVIERR_UNSUPPORTED
;
885 /* initial format or a formatchange? */
886 if (This
->lpFormat
== NULL
) {
888 if (This
->paf
->dwMoviChunkPos
!= 0)
889 return AVIERR_ERROR
; /* user has used API in wrong sequence! */
891 This
->lpFormat
= HeapAlloc(GetProcessHeap(), 0, formatsize
);
892 if (This
->lpFormat
== NULL
)
893 return AVIERR_MEMORY
;
894 This
->cbFormat
= formatsize
;
896 memcpy(This
->lpFormat
, format
, formatsize
);
898 /* update some infos about stream */
899 if (This
->sInfo
.fccType
== streamtypeVIDEO
) {
902 lDim
= This
->sInfo
.rcFrame
.right
- This
->sInfo
.rcFrame
.left
;
903 if (lDim
< lpbiNew
->biWidth
)
904 This
->sInfo
.rcFrame
.right
= This
->sInfo
.rcFrame
.left
+ lpbiNew
->biWidth
;
905 lDim
= This
->sInfo
.rcFrame
.bottom
- This
->sInfo
.rcFrame
.top
;
906 if (lDim
< lpbiNew
->biHeight
)
907 This
->sInfo
.rcFrame
.bottom
= This
->sInfo
.rcFrame
.top
+ lpbiNew
->biHeight
;
908 } else if (This
->sInfo
.fccType
== streamtypeAUDIO
)
909 This
->sInfo
.dwSampleSize
= ((LPWAVEFORMATEX
)This
->lpFormat
)->nBlockAlign
;
914 LPBITMAPINFOHEADER lpbiOld
= This
->lpFormat
;
915 RGBQUAD
*rgbNew
= (RGBQUAD
*)((LPBYTE
)lpbiNew
+ lpbiNew
->biSize
);
916 AVIPALCHANGE
*lppc
= NULL
;
919 /* perhaps format change, check it ... */
920 if (This
->cbFormat
!= formatsize
)
921 return AVIERR_UNSUPPORTED
;
923 /* no format change, only the initial one */
924 if (memcmp(This
->lpFormat
, format
, formatsize
) == 0)
927 /* check that's only the palette, which changes */
928 if (lpbiOld
->biSize
!= lpbiNew
->biSize
||
929 lpbiOld
->biWidth
!= lpbiNew
->biWidth
||
930 lpbiOld
->biHeight
!= lpbiNew
->biHeight
||
931 lpbiOld
->biPlanes
!= lpbiNew
->biPlanes
||
932 lpbiOld
->biBitCount
!= lpbiNew
->biBitCount
||
933 lpbiOld
->biCompression
!= lpbiNew
->biCompression
||
934 lpbiOld
->biClrUsed
!= lpbiNew
->biClrUsed
)
935 return AVIERR_UNSUPPORTED
;
937 This
->sInfo
.dwFlags
|= AVISTREAMINFO_FORMATCHANGES
;
939 /* simply say all colors have changed */
940 ck
.ckid
= MAKEAVICKID(cktypePALchange
, This
->nStream
);
941 ck
.cksize
= 2 * sizeof(WORD
) + lpbiOld
->biClrUsed
* sizeof(PALETTEENTRY
);
942 lppc
= HeapAlloc(GetProcessHeap(), 0, ck
.cksize
);
944 return AVIERR_MEMORY
;
946 lppc
->bFirstEntry
= 0;
947 lppc
->bNumEntries
= (lpbiOld
->biClrUsed
< 256 ? lpbiOld
->biClrUsed
: 0);
949 for (n
= 0; n
< lpbiOld
->biClrUsed
; n
++) {
950 lppc
->peNew
[n
].peRed
= rgbNew
[n
].rgbRed
;
951 lppc
->peNew
[n
].peGreen
= rgbNew
[n
].rgbGreen
;
952 lppc
->peNew
[n
].peBlue
= rgbNew
[n
].rgbBlue
;
953 lppc
->peNew
[n
].peFlags
= 0;
956 if (mmioSeek(This
->paf
->hmmio
, This
->paf
->dwNextFramePos
, SEEK_SET
) == -1 ||
957 mmioCreateChunk(This
->paf
->hmmio
, &ck
, 0) != S_OK
||
958 mmioWrite(This
->paf
->hmmio
, (HPSTR
)lppc
, ck
.cksize
) != ck
.cksize
||
959 mmioAscend(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
961 HeapFree(GetProcessHeap(), 0, lppc
);
962 return AVIERR_FILEWRITE
;
965 This
->paf
->dwNextFramePos
+= ck
.cksize
+ 2 * sizeof(DWORD
);
967 HeapFree(GetProcessHeap(), 0, lppc
);
969 return AVIFILE_AddFrame(This
, cktypePALchange
, n
, ck
.dwDataOffset
, 0);
973 static HRESULT WINAPI
IAVIStream_fnRead(IAVIStream
*iface
, LONG start
, LONG samples
, void *buffer
,
974 LONG buffersize
, LONG
*bytesread
, LONG
*samplesread
)
976 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
980 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
981 buffersize
, bytesread
, samplesread
);
983 /* clear return parameters if given */
984 if (bytesread
!= NULL
)
986 if (samplesread
!= NULL
)
989 /* check parameters */
990 if ((LONG
)This
->sInfo
.dwStart
> start
)
991 return AVIERR_NODATA
; /* couldn't read before start of stream */
992 if (This
->sInfo
.dwStart
+ This
->sInfo
.dwLength
< (DWORD
)start
)
993 return AVIERR_NODATA
; /* start is past end of stream */
995 /* should we read as much as possible? */
997 /* User should know how much we have read */
998 if (bytesread
== NULL
&& samplesread
== NULL
)
999 return AVIERR_BADPARAM
;
1001 if (This
->sInfo
.dwSampleSize
!= 0)
1002 samples
= buffersize
/ This
->sInfo
.dwSampleSize
;
1007 /* limit to end of stream */
1008 if ((LONG
)This
->sInfo
.dwLength
< samples
)
1009 samples
= This
->sInfo
.dwLength
;
1010 if ((start
- This
->sInfo
.dwStart
) > (This
->sInfo
.dwLength
- samples
))
1011 samples
= This
->sInfo
.dwLength
- (start
- This
->sInfo
.dwStart
);
1013 /* nothing to read? Then leave ... */
1017 if (This
->sInfo
.dwSampleSize
!= 0) {
1018 /* fixed samplesize -- we can read over frame/block boundaries */
1025 *bytesread
= samples
*This
->sInfo
.dwSampleSize
;
1027 *samplesread
= samples
;
1031 /* convert start sample to block,offset pair */
1032 AVIFILE_SamplesToBlock(This
, &block
, &offset
);
1034 /* convert samples to bytes */
1035 samples
*= This
->sInfo
.dwSampleSize
;
1037 while (samples
> 0 && buffersize
> 0) {
1039 if (block
!= This
->dwCurrentFrame
) {
1040 hr
= AVIFILE_ReadBlock(This
, block
, NULL
, 0);
1045 size
= min((DWORD
)samples
, (DWORD
)buffersize
);
1046 blocksize
= This
->lpBuffer
[1];
1047 TRACE("blocksize = %u\n",blocksize
);
1048 size
= min(size
, blocksize
- offset
);
1049 memcpy(buffer
, ((BYTE
*)&This
->lpBuffer
[2]) + offset
, size
);
1053 buffer
= ((LPBYTE
)buffer
)+size
;
1057 /* fill out return parameters if given */
1058 if (bytesread
!= NULL
)
1060 if (samplesread
!= NULL
)
1061 *samplesread
+= size
/ This
->sInfo
.dwSampleSize
;
1067 return AVIERR_BUFFERTOOSMALL
;
1069 /* variable samplesize -- we can only read one full frame/block */
1073 assert(start
<= This
->lLastFrame
);
1074 size
= This
->idxFrames
[start
].dwChunkLength
;
1075 if (buffer
!= NULL
&& buffersize
>= size
) {
1076 hr
= AVIFILE_ReadBlock(This
, start
, buffer
, size
);
1079 } else if (buffer
!= NULL
)
1080 return AVIERR_BUFFERTOOSMALL
;
1082 /* fill out return parameters if given */
1083 if (bytesread
!= NULL
)
1085 if (samplesread
!= NULL
)
1086 *samplesread
= samples
;
1092 static HRESULT WINAPI
IAVIStream_fnWrite(IAVIStream
*iface
, LONG start
, LONG samples
, void *buffer
,
1093 LONG buffersize
, DWORD flags
, LONG
*sampwritten
, LONG
*byteswritten
)
1095 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
1099 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
1100 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
1102 /* clear return parameters if given */
1103 if (sampwritten
!= NULL
)
1105 if (byteswritten
!= NULL
)
1108 /* check parameters */
1109 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
1110 return AVIERR_BADPARAM
;
1112 /* Have we write permission? */
1113 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1114 return AVIERR_READONLY
;
1116 switch (This
->sInfo
.fccType
) {
1117 case streamtypeAUDIO
:
1118 ckid
= MAKEAVICKID(cktypeWAVEbytes
, This
->nStream
);
1121 if ((flags
& AVIIF_KEYFRAME
) && buffersize
!= 0)
1122 ckid
= MAKEAVICKID(cktypeDIBbits
, This
->nStream
);
1124 ckid
= MAKEAVICKID(cktypeDIBcompressed
, This
->nStream
);
1128 /* append to end of stream? */
1130 if (This
->lLastFrame
== -1)
1131 start
= This
->sInfo
.dwStart
;
1133 start
= This
->sInfo
.dwLength
;
1134 } else if (This
->lLastFrame
== -1)
1135 This
->sInfo
.dwStart
= start
;
1137 if (This
->sInfo
.dwSampleSize
!= 0) {
1138 /* fixed sample size -- audio like */
1139 if (samples
* This
->sInfo
.dwSampleSize
!= buffersize
)
1140 return AVIERR_BADPARAM
;
1142 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1143 if (This
->sInfo
.dwLength
!= start
)
1144 return AVIERR_UNSUPPORTED
;
1146 /* Convert position to frame/block */
1147 start
= This
->lLastFrame
+ 1;
1149 if ((This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) == 0) {
1150 FIXME(": not interleaved, could collect audio data!\n");
1153 /* variable sample size -- video like */
1155 return AVIERR_UNSUPPORTED
;
1157 /* must we fill up with empty frames? */
1158 if (This
->lLastFrame
!= -1) {
1159 FOURCC ckid2
= MAKEAVICKID(cktypeDIBcompressed
, This
->nStream
);
1161 while (start
> This
->lLastFrame
+ 1) {
1162 hr
= AVIFILE_WriteBlock(This
, This
->lLastFrame
+ 1, ckid2
, 0, NULL
, 0);
1169 /* write the block now */
1170 hr
= AVIFILE_WriteBlock(This
, start
, ckid
, flags
, buffer
, buffersize
);
1171 if (SUCCEEDED(hr
)) {
1172 /* fill out return parameters if given */
1173 if (sampwritten
!= NULL
)
1174 *sampwritten
= samples
;
1175 if (byteswritten
!= NULL
)
1176 *byteswritten
= buffersize
;
1182 static HRESULT WINAPI
IAVIStream_fnDelete(IAVIStream
*iface
, LONG start
, LONG samples
)
1184 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
1186 FIXME("(%p,%d,%d): stub\n", iface
, start
, samples
);
1188 /* check parameters */
1189 if (start
< 0 || samples
< 0)
1190 return AVIERR_BADPARAM
;
1192 /* Delete before start of stream? */
1193 if (start
+ samples
< This
->sInfo
.dwStart
)
1196 /* Delete after end of stream? */
1197 if (start
> This
->sInfo
.dwLength
)
1200 /* For the rest we need write permissions */
1201 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1202 return AVIERR_READONLY
;
1204 /* 1. overwrite the data with JUNK
1206 * if ISINTERLEAVED {
1207 * 2. concat all neighboured JUNK-blocks in this record to one
1208 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1209 * to start of this record, repeat this.
1211 * 2. concat all neighboured JUNK-blocks.
1212 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1213 * start of this block.
1217 return AVIERR_UNSUPPORTED
;
1220 static HRESULT WINAPI
IAVIStream_fnReadData(IAVIStream
*iface
, DWORD fcc
, void *lp
, LONG
*lpread
)
1222 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
1224 TRACE("(%p,0x%08X,%p,%p)\n", iface
, fcc
, lp
, lpread
);
1226 if (fcc
== ckidSTREAMHANDLERDATA
) {
1227 if (This
->lpHandlerData
!= NULL
&& This
->cbHandlerData
> 0) {
1228 if (lp
== NULL
|| *lpread
<= 0) {
1229 *lpread
= This
->cbHandlerData
;
1233 memcpy(lp
, This
->lpHandlerData
, min(This
->cbHandlerData
, *lpread
));
1234 if (*lpread
< This
->cbHandlerData
)
1235 return AVIERR_BUFFERTOOSMALL
;
1238 return AVIERR_NODATA
;
1240 return ReadExtraChunk(&This
->extra
, fcc
, lp
, lpread
);
1243 static HRESULT WINAPI
IAVIStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
, void *lp
, LONG size
)
1245 IAVIStreamImpl
*This
= impl_from_IAVIStream(iface
);
1247 TRACE("(%p,0x%08x,%p,%d)\n", iface
, fcc
, lp
, size
);
1249 /* check parameters */
1251 return AVIERR_BADPARAM
;
1253 return AVIERR_BADSIZE
;
1255 /* need write permission */
1256 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1257 return AVIERR_READONLY
;
1259 /* already written something to this file? */
1260 if (This
->paf
->dwMoviChunkPos
!= 0) {
1261 /* the data will be inserted before the 'movi' chunk, so check for
1263 DWORD dwPos
= AVIFILE_ComputeMoviStart(This
->paf
);
1265 /* ckid,size => 2 * sizeof(DWORD) */
1266 dwPos
+= 2 * sizeof(DWORD
) + size
;
1267 if (dwPos
>= This
->paf
->dwMoviChunkPos
- 2 * sizeof(DWORD
))
1268 return AVIERR_UNSUPPORTED
; /* not enough space left */
1271 This
->paf
->fDirty
= TRUE
;
1273 if (fcc
== ckidSTREAMHANDLERDATA
) {
1274 if (This
->lpHandlerData
!= NULL
) {
1275 FIXME(": handler data already set -- overwrite?\n");
1276 return AVIERR_UNSUPPORTED
;
1279 This
->lpHandlerData
= HeapAlloc(GetProcessHeap(), 0, size
);
1280 if (This
->lpHandlerData
== NULL
)
1281 return AVIERR_MEMORY
;
1282 This
->cbHandlerData
= size
;
1283 memcpy(This
->lpHandlerData
, lp
, size
);
1287 return WriteExtraChunk(&This
->extra
, fcc
, lp
, size
);
1290 static HRESULT WINAPI
IAVIStream_fnSetInfo(IAVIStream
*iface
, AVISTREAMINFOW
*info
, LONG infolen
)
1292 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
1297 static const struct IAVIStreamVtbl avist_vt
= {
1298 IAVIStream_fnQueryInterface
,
1299 IAVIStream_fnAddRef
,
1300 IAVIStream_fnRelease
,
1301 IAVIStream_fnCreate
,
1303 IAVIStream_fnFindSample
,
1304 IAVIStream_fnReadFormat
,
1305 IAVIStream_fnSetFormat
,
1308 IAVIStream_fnDelete
,
1309 IAVIStream_fnReadData
,
1310 IAVIStream_fnWriteData
,
1311 IAVIStream_fnSetInfo
1315 static HRESULT
AVIFILE_AddFrame(IAVIStreamImpl
*This
, DWORD ckid
, DWORD size
, DWORD offset
, DWORD flags
)
1319 /* pre-conditions */
1320 assert(This
!= NULL
);
1322 switch (TWOCCFromFOURCC(ckid
)) {
1324 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1325 flags
|= AVIIF_KEYFRAME
;
1327 case cktypeDIBcompressed
:
1328 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1329 flags
&= ~AVIIF_KEYFRAME
;
1331 case cktypePALchange
:
1332 if (This
->sInfo
.fccType
!= streamtypeVIDEO
) {
1333 ERR(": found palette change in non-video stream!\n");
1334 return AVIERR_BADFORMAT
;
1337 if (This
->idxFmtChanges
== NULL
|| This
->nIdxFmtChanges
<= This
->sInfo
.dwFormatChangeCount
) {
1338 DWORD new_count
= This
->nIdxFmtChanges
+ 16;
1341 if (This
->idxFmtChanges
== NULL
) {
1342 This
->idxFmtChanges
=
1343 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, new_count
* sizeof(AVIINDEXENTRY
));
1344 if (!This
->idxFmtChanges
) return AVIERR_MEMORY
;
1346 new_buffer
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->idxFmtChanges
,
1347 new_count
* sizeof(AVIINDEXENTRY
));
1348 if (!new_buffer
) return AVIERR_MEMORY
;
1349 This
->idxFmtChanges
= new_buffer
;
1351 This
->nIdxFmtChanges
= new_count
;
1354 This
->sInfo
.dwFlags
|= AVISTREAMINFO_FORMATCHANGES
;
1355 n
= ++This
->sInfo
.dwFormatChangeCount
;
1356 This
->idxFmtChanges
[n
].ckid
= This
->lLastFrame
;
1357 This
->idxFmtChanges
[n
].dwFlags
= 0;
1358 This
->idxFmtChanges
[n
].dwChunkOffset
= offset
;
1359 This
->idxFmtChanges
[n
].dwChunkLength
= size
;
1362 case cktypeWAVEbytes
:
1363 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1364 flags
|= AVIIF_KEYFRAME
;
1367 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid
));
1371 /* first frame is always a keyframe */
1372 if (This
->lLastFrame
== -1)
1373 flags
|= AVIIF_KEYFRAME
;
1375 if (This
->sInfo
.dwSuggestedBufferSize
< size
)
1376 This
->sInfo
.dwSuggestedBufferSize
= size
;
1378 /* get memory for index */
1379 if (This
->idxFrames
== NULL
|| This
->lLastFrame
+ 1 >= This
->nIdxFrames
) {
1380 This
->nIdxFrames
+= 512;
1381 if (This
->idxFrames
== NULL
)
1382 This
->idxFrames
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->nIdxFrames
* sizeof(AVIINDEXENTRY
));
1384 This
->idxFrames
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->idxFrames
,
1385 This
->nIdxFrames
* sizeof(AVIINDEXENTRY
));
1386 if (This
->idxFrames
== NULL
)
1387 return AVIERR_MEMORY
;
1391 This
->idxFrames
[This
->lLastFrame
].ckid
= ckid
;
1392 This
->idxFrames
[This
->lLastFrame
].dwFlags
= flags
;
1393 This
->idxFrames
[This
->lLastFrame
].dwChunkOffset
= offset
;
1394 This
->idxFrames
[This
->lLastFrame
].dwChunkLength
= size
;
1396 /* update AVISTREAMINFO structure if necessary */
1397 if (This
->sInfo
.dwLength
<= This
->lLastFrame
)
1398 This
->sInfo
.dwLength
= This
->lLastFrame
+ 1;
1403 static HRESULT
AVIFILE_AddRecord(IAVIFileImpl
*This
)
1405 /* pre-conditions */
1406 assert(This
!= NULL
&& This
->ppStreams
[0] != NULL
);
1408 if (This
->idxRecords
== NULL
|| This
->cbIdxRecords
/ sizeof(AVIINDEXENTRY
) <= This
->nIdxRecords
) {
1409 DWORD new_count
= This
->cbIdxRecords
+ 1024 * sizeof(AVIINDEXENTRY
);
1411 if (!This
->idxRecords
)
1412 mem
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, new_count
);
1414 mem
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->idxRecords
, new_count
);
1416 This
->cbIdxRecords
= new_count
;
1417 This
->idxRecords
= mem
;
1419 HeapFree(GetProcessHeap(), 0, This
->idxRecords
);
1420 This
->idxRecords
= NULL
;
1421 return AVIERR_MEMORY
;
1425 assert(This
->nIdxRecords
< This
->cbIdxRecords
/sizeof(AVIINDEXENTRY
));
1427 This
->idxRecords
[This
->nIdxRecords
].ckid
= listtypeAVIRECORD
;
1428 This
->idxRecords
[This
->nIdxRecords
].dwFlags
= AVIIF_LIST
;
1429 This
->idxRecords
[This
->nIdxRecords
].dwChunkOffset
=
1430 This
->ckLastRecord
.dwDataOffset
- 2 * sizeof(DWORD
);
1431 This
->idxRecords
[This
->nIdxRecords
].dwChunkLength
=
1432 This
->ckLastRecord
.cksize
;
1433 This
->nIdxRecords
++;
1438 static DWORD
AVIFILE_ComputeMoviStart(IAVIFileImpl
*This
)
1443 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1444 dwPos
= 11 * sizeof(DWORD
) + sizeof(MainAVIHeader
);
1446 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
1447 IAVIStreamImpl
*pStream
= This
->ppStreams
[nStream
];
1449 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1450 dwPos
+= 7 * sizeof(DWORD
) + sizeof(AVIStreamHeader
);
1451 dwPos
+= ((pStream
->cbFormat
+ 1) & ~1U);
1452 if (pStream
->lpHandlerData
!= NULL
&& pStream
->cbHandlerData
> 0)
1453 dwPos
+= 2 * sizeof(DWORD
) + ((pStream
->cbHandlerData
+ 1) & ~1U);
1454 if (pStream
->sInfo
.szName
[0])
1455 dwPos
+= 2 * sizeof(DWORD
) + ((lstrlenW(pStream
->sInfo
.szName
) + 1) & ~1U);
1458 if (This
->dwMoviChunkPos
== 0) {
1459 This
->dwNextFramePos
= dwPos
;
1461 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1462 if (((dwPos
+ AVI_HEADERSIZE
) & ~(AVI_HEADERSIZE
- 1)) - dwPos
> 2 * sizeof(DWORD
))
1463 This
->dwNextFramePos
= (dwPos
+ AVI_HEADERSIZE
) & ~(AVI_HEADERSIZE
- 1);
1465 This
->dwMoviChunkPos
= This
->dwNextFramePos
- sizeof(DWORD
);
1471 static void AVIFILE_ConstructAVIStream(IAVIFileImpl
*paf
, DWORD nr
, const AVISTREAMINFOW
*asi
)
1473 IAVIStreamImpl
*pstream
;
1475 /* pre-conditions */
1476 assert(paf
!= NULL
);
1477 assert(nr
< MAX_AVISTREAMS
);
1478 assert(paf
->ppStreams
[nr
] != NULL
);
1480 pstream
= paf
->ppStreams
[nr
];
1482 pstream
->IAVIStream_iface
.lpVtbl
= &avist_vt
;
1485 pstream
->nStream
= nr
;
1486 pstream
->dwCurrentFrame
= (DWORD
)-1;
1487 pstream
->lLastFrame
= -1;
1490 memcpy(&pstream
->sInfo
, asi
, sizeof(pstream
->sInfo
));
1492 if (asi
->dwLength
> 0) {
1493 /* pre-allocate mem for frame-index structure */
1494 pstream
->idxFrames
=
1495 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, asi
->dwLength
* sizeof(AVIINDEXENTRY
));
1496 if (pstream
->idxFrames
!= NULL
)
1497 pstream
->nIdxFrames
= asi
->dwLength
;
1499 if (asi
->dwFormatChangeCount
> 0) {
1500 /* pre-allocate mem for formatchange-index structure */
1501 pstream
->idxFmtChanges
=
1502 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, asi
->dwFormatChangeCount
* sizeof(AVIINDEXENTRY
));
1503 if (pstream
->idxFmtChanges
!= NULL
)
1504 pstream
->nIdxFmtChanges
= asi
->dwFormatChangeCount
;
1507 /* These values will be computed */
1508 pstream
->sInfo
.dwLength
= 0;
1509 pstream
->sInfo
.dwSuggestedBufferSize
= 0;
1510 pstream
->sInfo
.dwFormatChangeCount
= 0;
1511 pstream
->sInfo
.dwEditCount
= 1;
1512 if (pstream
->sInfo
.dwSampleSize
> 0)
1513 SetRectEmpty(&pstream
->sInfo
.rcFrame
);
1516 pstream
->sInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
1519 static void AVIFILE_DestructAVIStream(IAVIStreamImpl
*This
)
1521 /* pre-conditions */
1522 assert(This
!= NULL
);
1524 This
->dwCurrentFrame
= (DWORD
)-1;
1525 This
->lLastFrame
= -1;
1527 if (This
->idxFrames
!= NULL
) {
1528 HeapFree(GetProcessHeap(), 0, This
->idxFrames
);
1529 This
->idxFrames
= NULL
;
1530 This
->nIdxFrames
= 0;
1532 HeapFree(GetProcessHeap(), 0, This
->idxFmtChanges
);
1533 This
->idxFmtChanges
= NULL
;
1534 if (This
->lpBuffer
!= NULL
) {
1535 HeapFree(GetProcessHeap(), 0, This
->lpBuffer
);
1536 This
->lpBuffer
= NULL
;
1539 if (This
->lpHandlerData
!= NULL
) {
1540 HeapFree(GetProcessHeap(), 0, This
->lpHandlerData
);
1541 This
->lpHandlerData
= NULL
;
1542 This
->cbHandlerData
= 0;
1544 if (This
->extra
.lp
!= NULL
) {
1545 HeapFree(GetProcessHeap(), 0, This
->extra
.lp
);
1546 This
->extra
.lp
= NULL
;
1549 if (This
->lpFormat
!= NULL
) {
1550 HeapFree(GetProcessHeap(), 0, This
->lpFormat
);
1551 This
->lpFormat
= NULL
;
1556 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
)
1558 MainAVIHeader MainAVIHdr
;
1563 IAVIStreamImpl
*pStream
;
1567 if (This
->hmmio
== NULL
)
1568 return AVIERR_FILEOPEN
;
1570 /* initialize stream ptr's */
1571 memset(This
->ppStreams
, 0, sizeof(This
->ppStreams
));
1573 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1574 ckRIFF
.fccType
= formtypeAVI
;
1575 if (mmioDescend(This
->hmmio
, &ckRIFF
, NULL
, MMIO_FINDRIFF
) != S_OK
) {
1576 ERR(": not an AVI!\n");
1577 return AVIERR_FILEREAD
;
1580 /* get "LIST" "hdrl" */
1581 ckLIST1
.fccType
= listtypeAVIHEADER
;
1582 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ckLIST1
, &ckRIFF
, MMIO_FINDLIST
);
1586 /* get "avih" chunk */
1587 ck
.ckid
= ckidAVIMAINHDR
;
1588 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckLIST1
, MMIO_FINDCHUNK
);
1592 if (ck
.cksize
!= sizeof(MainAVIHdr
)) {
1593 ERR(": invalid size of %d for MainAVIHeader!\n", ck
.cksize
);
1594 return AVIERR_BADFORMAT
;
1596 if (mmioRead(This
->hmmio
, (HPSTR
)&MainAVIHdr
, ck
.cksize
) != ck
.cksize
)
1597 return AVIERR_FILEREAD
;
1599 /* check for MAX_AVISTREAMS limit */
1600 if (MainAVIHdr
.dwStreams
> MAX_AVISTREAMS
) {
1601 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr
.dwStreams
, MAX_AVISTREAMS
);
1602 return AVIERR_UNSUPPORTED
;
1605 /* adjust permissions if copyrighted material in file */
1606 if (MainAVIHdr
.dwFlags
& AVIFILEINFO_COPYRIGHTED
) {
1607 This
->uMode
&= ~MMIO_RWMODE
;
1608 This
->uMode
|= MMIO_READ
;
1611 /* convert MainAVIHeader into AVIFILINFOW */
1612 memset(&This
->fInfo
, 0, sizeof(This
->fInfo
));
1613 This
->fInfo
.dwRate
= MainAVIHdr
.dwMicroSecPerFrame
;
1614 This
->fInfo
.dwScale
= 1000000;
1615 This
->fInfo
.dwMaxBytesPerSec
= MainAVIHdr
.dwMaxBytesPerSec
;
1616 This
->fInfo
.dwFlags
= MainAVIHdr
.dwFlags
;
1617 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
1618 This
->fInfo
.dwLength
= MainAVIHdr
.dwTotalFrames
;
1619 This
->fInfo
.dwStreams
= MainAVIHdr
.dwStreams
;
1620 This
->fInfo
.dwSuggestedBufferSize
= 0;
1621 This
->fInfo
.dwWidth
= MainAVIHdr
.dwWidth
;
1622 This
->fInfo
.dwHeight
= MainAVIHdr
.dwHeight
;
1623 LoadStringW(AVIFILE_hModule
, IDS_AVIFILETYPE
, This
->fInfo
.szFileType
,
1624 sizeof(This
->fInfo
.szFileType
)/sizeof(This
->fInfo
.szFileType
[0]));
1626 /* go back to into header list */
1627 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1628 return AVIERR_FILEREAD
;
1630 /* foreach stream exists a "LIST","strl" chunk */
1631 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
1632 /* get next nested chunk in this "LIST","strl" */
1633 if (mmioDescend(This
->hmmio
, &ckLIST2
, &ckLIST1
, 0) != S_OK
)
1634 return AVIERR_FILEREAD
;
1636 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1637 if (ckLIST2
.ckid
== FOURCC_LIST
&&
1638 ckLIST2
.fccType
== listtypeSTREAMHEADER
) {
1639 pStream
= This
->ppStreams
[nStream
] =
1640 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IAVIStreamImpl
));
1641 if (pStream
== NULL
)
1642 return AVIERR_MEMORY
;
1643 AVIFILE_ConstructAVIStream(This
, nStream
, NULL
);
1646 while (mmioDescend(This
->hmmio
, &ck
, &ckLIST2
, 0) == S_OK
) {
1648 case ckidSTREAMHANDLERDATA
:
1649 if (pStream
->lpHandlerData
!= NULL
)
1650 return AVIERR_BADFORMAT
;
1651 pStream
->lpHandlerData
= HeapAlloc(GetProcessHeap(), 0, ck
.cksize
);
1652 if (pStream
->lpHandlerData
== NULL
)
1653 return AVIERR_MEMORY
;
1654 pStream
->cbHandlerData
= ck
.cksize
;
1656 if (mmioRead(This
->hmmio
, pStream
->lpHandlerData
, ck
.cksize
) != ck
.cksize
)
1657 return AVIERR_FILEREAD
;
1659 case ckidSTREAMFORMAT
:
1660 if (pStream
->lpFormat
!= NULL
)
1661 return AVIERR_BADFORMAT
;
1665 pStream
->lpFormat
= HeapAlloc(GetProcessHeap(), 0, ck
.cksize
);
1666 if (pStream
->lpFormat
== NULL
)
1667 return AVIERR_MEMORY
;
1668 pStream
->cbFormat
= ck
.cksize
;
1670 if (mmioRead(This
->hmmio
, pStream
->lpFormat
, ck
.cksize
) != ck
.cksize
)
1671 return AVIERR_FILEREAD
;
1673 if (pStream
->sInfo
.fccType
== streamtypeVIDEO
) {
1674 LPBITMAPINFOHEADER lpbi
= pStream
->lpFormat
;
1676 /* some corrections to the video format */
1677 if (lpbi
->biClrUsed
== 0 && lpbi
->biBitCount
<= 8)
1678 lpbi
->biClrUsed
= 1u << lpbi
->biBitCount
;
1679 if (lpbi
->biCompression
== BI_RGB
&& lpbi
->biSizeImage
== 0)
1680 lpbi
->biSizeImage
= DIBWIDTHBYTES(*lpbi
) * lpbi
->biHeight
;
1681 if (lpbi
->biCompression
!= BI_RGB
&& lpbi
->biBitCount
== 8) {
1682 if (pStream
->sInfo
.fccHandler
== mmioFOURCC('R','L','E','0') ||
1683 pStream
->sInfo
.fccHandler
== mmioFOURCC('R','L','E',' '))
1684 lpbi
->biCompression
= BI_RLE8
;
1686 if (lpbi
->biCompression
== BI_RGB
&&
1687 (pStream
->sInfo
.fccHandler
== 0 ||
1688 pStream
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E')))
1689 pStream
->sInfo
.fccHandler
= comptypeDIB
;
1691 /* init rcFrame if it's empty */
1692 if (IsRectEmpty(&pStream
->sInfo
.rcFrame
))
1693 SetRect(&pStream
->sInfo
.rcFrame
, 0, 0, lpbi
->biWidth
, lpbi
->biHeight
);
1696 case ckidSTREAMHEADER
:
1698 static const WCHAR streamTypeFmt
[] = {'%','4','.','4','h','s',0};
1699 static const WCHAR streamNameFmt
[] = {'%','s',' ','%','s',' ','#','%','d',0};
1701 AVIStreamHeader streamHdr
;
1706 if (ck
.cksize
> sizeof(streamHdr
))
1707 n
= sizeof(streamHdr
);
1709 if (mmioRead(This
->hmmio
, (HPSTR
)&streamHdr
, n
) != n
)
1710 return AVIERR_FILEREAD
;
1712 pStream
->sInfo
.fccType
= streamHdr
.fccType
;
1713 pStream
->sInfo
.fccHandler
= streamHdr
.fccHandler
;
1714 pStream
->sInfo
.dwFlags
= streamHdr
.dwFlags
;
1715 pStream
->sInfo
.wPriority
= streamHdr
.wPriority
;
1716 pStream
->sInfo
.wLanguage
= streamHdr
.wLanguage
;
1717 pStream
->sInfo
.dwInitialFrames
= streamHdr
.dwInitialFrames
;
1718 pStream
->sInfo
.dwScale
= streamHdr
.dwScale
;
1719 pStream
->sInfo
.dwRate
= streamHdr
.dwRate
;
1720 pStream
->sInfo
.dwStart
= streamHdr
.dwStart
;
1721 pStream
->sInfo
.dwLength
= streamHdr
.dwLength
;
1722 pStream
->sInfo
.dwSuggestedBufferSize
= 0;
1723 pStream
->sInfo
.dwQuality
= streamHdr
.dwQuality
;
1724 pStream
->sInfo
.dwSampleSize
= streamHdr
.dwSampleSize
;
1725 pStream
->sInfo
.rcFrame
.left
= streamHdr
.rcFrame
.left
;
1726 pStream
->sInfo
.rcFrame
.top
= streamHdr
.rcFrame
.top
;
1727 pStream
->sInfo
.rcFrame
.right
= streamHdr
.rcFrame
.right
;
1728 pStream
->sInfo
.rcFrame
.bottom
= streamHdr
.rcFrame
.bottom
;
1729 pStream
->sInfo
.dwEditCount
= 0;
1730 pStream
->sInfo
.dwFormatChangeCount
= 0;
1732 /* generate description for stream like "filename.avi Type #n" */
1733 if (streamHdr
.fccType
== streamtypeVIDEO
)
1734 LoadStringW(AVIFILE_hModule
, IDS_VIDEO
, szType
, sizeof(szType
)/sizeof(szType
[0]));
1735 else if (streamHdr
.fccType
== streamtypeAUDIO
)
1736 LoadStringW(AVIFILE_hModule
, IDS_AUDIO
, szType
, sizeof(szType
)/sizeof(szType
[0]));
1738 wsprintfW(szType
, streamTypeFmt
, (char*)&streamHdr
.fccType
);
1740 /* get count of this streamtype up to this stream */
1742 for (n
= nStream
; 0 <= n
; n
--) {
1743 if (This
->ppStreams
[n
]->sInfo
.fccHandler
== streamHdr
.fccType
)
1747 memset(pStream
->sInfo
.szName
, 0, sizeof(pStream
->sInfo
.szName
));
1749 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1750 wsprintfW(pStream
->sInfo
.szName
, streamNameFmt
,
1751 AVIFILE_BasenameW(This
->szFileName
), szType
, count
);
1754 case ckidSTREAMNAME
:
1755 { /* streamname will be saved as ASCII string */
1756 LPSTR str
= HeapAlloc(GetProcessHeap(), 0, ck
.cksize
);
1758 return AVIERR_MEMORY
;
1760 if (mmioRead(This
->hmmio
, str
, ck
.cksize
) != ck
.cksize
)
1762 HeapFree(GetProcessHeap(), 0, str
);
1763 return AVIERR_FILEREAD
;
1766 MultiByteToWideChar(CP_ACP
, 0, str
, -1, pStream
->sInfo
.szName
,
1767 sizeof(pStream
->sInfo
.szName
)/sizeof(pStream
->sInfo
.szName
[0]));
1769 HeapFree(GetProcessHeap(), 0, str
);
1772 case ckidAVIPADDING
:
1773 case mmioFOURCC('p','a','d','d'):
1776 WARN(": found extra chunk 0x%08X\n", ck
.ckid
);
1777 hr
= ReadChunkIntoExtra(&pStream
->extra
, This
->hmmio
, &ck
);
1781 if (pStream
->lpFormat
!= NULL
&& pStream
->sInfo
.fccType
== streamtypeAUDIO
)
1783 WAVEFORMATEX
*wfx
= pStream
->lpFormat
; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1784 pStream
->sInfo
.dwSampleSize
= wfx
->nBlockAlign
; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1785 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx
->nBlockAlign
, wfx
->nChannels
, wfx
->wBitsPerSample
);
1786 pStream
->sInfo
.dwScale
= 1;
1787 pStream
->sInfo
.dwRate
= wfx
->nSamplesPerSec
;
1789 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1790 return AVIERR_FILEREAD
;
1793 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1794 hr
= ReadChunkIntoExtra(&This
->fileextra
, This
->hmmio
, &ckLIST2
);
1798 if (mmioAscend(This
->hmmio
, &ckLIST2
, 0) != S_OK
)
1799 return AVIERR_FILEREAD
;
1802 /* read any extra headers in "LIST","hdrl" */
1803 FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckLIST1
, 0);
1804 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
1805 return AVIERR_FILEREAD
;
1807 /* search "LIST","movi" chunk in "RIFF","AVI " */
1808 ckLIST1
.fccType
= listtypeAVIMOVIE
;
1809 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ckLIST1
, &ckRIFF
,
1814 This
->dwMoviChunkPos
= ckLIST1
.dwDataOffset
;
1815 This
->dwIdxChunkPos
= ckLIST1
.cksize
+ ckLIST1
.dwDataOffset
;
1816 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
1817 return AVIERR_FILEREAD
;
1819 /* try to find an index */
1820 ck
.ckid
= ckidAVINEWINDEX
;
1821 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
,
1822 &ck
, &ckRIFF
, MMIO_FINDCHUNK
);
1823 if (SUCCEEDED(hr
) && ck
.cksize
> 0) {
1824 if (FAILED(AVIFILE_LoadIndex(This
, ck
.cksize
, ckLIST1
.dwDataOffset
)))
1825 This
->fInfo
.dwFlags
&= ~AVIFILEINFO_HASINDEX
;
1828 /* when we haven't found an index or it's bad, then build one
1829 * by parsing 'movi' chunk */
1830 if ((This
->fInfo
.dwFlags
& AVIFILEINFO_HASINDEX
) == 0) {
1831 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++)
1832 This
->ppStreams
[nStream
]->lLastFrame
= -1;
1834 if (mmioSeek(This
->hmmio
, ckLIST1
.dwDataOffset
+ sizeof(DWORD
), SEEK_SET
) == -1) {
1835 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1836 return AVIERR_FILEREAD
;
1839 /* seek through the 'movi' list until end */
1840 while (mmioDescend(This
->hmmio
, &ck
, &ckLIST1
, 0) == S_OK
) {
1841 if (ck
.ckid
!= FOURCC_LIST
) {
1842 if (mmioAscend(This
->hmmio
, &ck
, 0) == S_OK
) {
1843 nStream
= StreamFromFOURCC(ck
.ckid
);
1845 if (nStream
> This
->fInfo
.dwStreams
)
1846 return AVIERR_BADFORMAT
;
1848 AVIFILE_AddFrame(This
->ppStreams
[nStream
], ck
.ckid
, ck
.cksize
,
1849 ck
.dwDataOffset
- 2 * sizeof(DWORD
), 0);
1851 nStream
= StreamFromFOURCC(ck
.ckid
);
1852 WARN(": file seems to be truncated!\n");
1853 if (nStream
<= This
->fInfo
.dwStreams
&&
1854 This
->ppStreams
[nStream
]->sInfo
.dwSampleSize
> 0) {
1855 ck
.cksize
= mmioSeek(This
->hmmio
, 0, SEEK_END
);
1856 if (ck
.cksize
!= -1) {
1857 ck
.cksize
-= ck
.dwDataOffset
;
1858 ck
.cksize
&= ~(This
->ppStreams
[nStream
]->sInfo
.dwSampleSize
- 1);
1860 AVIFILE_AddFrame(This
->ppStreams
[nStream
], ck
.ckid
, ck
.cksize
,
1861 ck
.dwDataOffset
- 2 * sizeof(DWORD
), 0);
1869 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++)
1871 DWORD sugbuf
= This
->ppStreams
[nStream
]->sInfo
.dwSuggestedBufferSize
;
1872 if (This
->fInfo
.dwSuggestedBufferSize
< sugbuf
)
1873 This
->fInfo
.dwSuggestedBufferSize
= sugbuf
;
1876 /* find other chunks */
1877 FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckRIFF
, 0);
1882 static HRESULT
AVIFILE_LoadIndex(const IAVIFileImpl
*This
, DWORD size
, DWORD offset
)
1886 HRESULT hr
= AVIERR_OK
;
1887 BOOL bAbsolute
= TRUE
;
1889 lp
= HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK
* sizeof(AVIINDEXENTRY
));
1891 return AVIERR_MEMORY
;
1893 /* adjust limits for index tables, so that inserting will be faster */
1894 for (n
= 0; n
< This
->fInfo
.dwStreams
; n
++) {
1895 IAVIStreamImpl
*pStream
= This
->ppStreams
[n
];
1897 pStream
->lLastFrame
= -1;
1899 if (pStream
->idxFrames
!= NULL
) {
1900 HeapFree(GetProcessHeap(), 0, pStream
->idxFrames
);
1901 pStream
->idxFrames
= NULL
;
1902 pStream
->nIdxFrames
= 0;
1905 if (pStream
->sInfo
.dwSampleSize
!= 0) {
1906 if (n
> 0 && This
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) {
1907 pStream
->nIdxFrames
= This
->ppStreams
[0]->nIdxFrames
;
1908 } else if (pStream
->sInfo
.dwSuggestedBufferSize
) {
1909 pStream
->nIdxFrames
=
1910 pStream
->sInfo
.dwLength
/ pStream
->sInfo
.dwSuggestedBufferSize
;
1913 pStream
->nIdxFrames
= pStream
->sInfo
.dwLength
;
1915 pStream
->idxFrames
=
1916 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, pStream
->nIdxFrames
* sizeof(AVIINDEXENTRY
));
1917 if (pStream
->idxFrames
== NULL
&& pStream
->nIdxFrames
> 0) {
1918 pStream
->nIdxFrames
= 0;
1919 HeapFree(GetProcessHeap(), 0, lp
);
1920 return AVIERR_MEMORY
;
1926 LONG read
= min(IDX_PER_BLOCK
* sizeof(AVIINDEXENTRY
), size
);
1928 if (mmioRead(This
->hmmio
, (HPSTR
)lp
, read
) != read
) {
1929 hr
= AVIERR_FILEREAD
;
1934 if (pos
== (DWORD
)-1)
1935 pos
= offset
- lp
->dwChunkOffset
+ sizeof(DWORD
);
1937 AVIFILE_ParseIndex(This
, lp
, read
/ sizeof(AVIINDEXENTRY
),
1941 HeapFree(GetProcessHeap(), 0, lp
);
1944 for (n
= 0; n
< This
->fInfo
.dwStreams
; n
++) {
1945 IAVIStreamImpl
*pStream
= This
->ppStreams
[n
];
1947 if (pStream
->sInfo
.dwSampleSize
== 0 &&
1948 pStream
->sInfo
.dwLength
!= pStream
->lLastFrame
+1)
1949 ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1950 n
, pStream
->sInfo
.dwLength
, pStream
->lLastFrame
);
1956 static HRESULT
AVIFILE_ParseIndex(const IAVIFileImpl
*This
, AVIINDEXENTRY
*lp
,
1957 LONG count
, DWORD pos
, BOOL
*bAbsolute
)
1960 return AVIERR_BADPARAM
;
1962 for (; count
> 0; count
--, lp
++) {
1963 WORD nStream
= StreamFromFOURCC(lp
->ckid
);
1965 if (lp
->ckid
== listtypeAVIRECORD
|| nStream
== 0x7F)
1966 continue; /* skip these */
1968 if (nStream
> This
->fInfo
.dwStreams
)
1969 return AVIERR_BADFORMAT
;
1971 if (*bAbsolute
&& lp
->dwChunkOffset
< This
->dwMoviChunkPos
)
1975 lp
->dwChunkOffset
+= sizeof(DWORD
);
1977 lp
->dwChunkOffset
+= pos
;
1979 if (FAILED(AVIFILE_AddFrame(This
->ppStreams
[nStream
], lp
->ckid
, lp
->dwChunkLength
, lp
->dwChunkOffset
, lp
->dwFlags
)))
1980 return AVIERR_MEMORY
;
1986 static HRESULT
AVIFILE_ReadBlock(IAVIStreamImpl
*This
, DWORD pos
,
1987 LPVOID buffer
, DWORD size
)
1989 /* pre-conditions */
1990 assert(This
!= NULL
);
1991 assert(This
->paf
!= NULL
);
1992 assert(This
->paf
->hmmio
!= NULL
);
1993 assert(This
->sInfo
.dwStart
<= pos
&& pos
< This
->sInfo
.dwLength
);
1994 assert(pos
<= This
->lLastFrame
);
1996 /* should we read as much as block gives us? */
1997 if (size
== 0 || size
> This
->idxFrames
[pos
].dwChunkLength
)
1998 size
= This
->idxFrames
[pos
].dwChunkLength
;
2000 /* read into out own buffer or given one? */
2001 if (buffer
== NULL
) {
2002 /* we also read the chunk */
2003 size
+= 2 * sizeof(DWORD
);
2005 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2006 if (This
->lpBuffer
== NULL
|| This
->cbBuffer
< size
) {
2007 DWORD maxSize
= max(size
, This
->sInfo
.dwSuggestedBufferSize
);
2009 if (This
->lpBuffer
== NULL
) {
2010 This
->lpBuffer
= HeapAlloc(GetProcessHeap(), 0, maxSize
);
2011 if (!This
->lpBuffer
) return AVIERR_MEMORY
;
2013 void *new_buffer
= HeapReAlloc(GetProcessHeap(), 0, This
->lpBuffer
, maxSize
);
2014 if (!new_buffer
) return AVIERR_MEMORY
;
2015 This
->lpBuffer
= new_buffer
;
2017 This
->cbBuffer
= maxSize
;
2020 /* now read the complete chunk into our buffer */
2021 if (mmioSeek(This
->paf
->hmmio
, This
->idxFrames
[pos
].dwChunkOffset
, SEEK_SET
) == -1)
2022 return AVIERR_FILEREAD
;
2023 if (mmioRead(This
->paf
->hmmio
, (HPSTR
)This
->lpBuffer
, size
) != size
)
2024 return AVIERR_FILEREAD
;
2026 /* check if it was the correct block which we have read */
2027 if (This
->lpBuffer
[0] != This
->idxFrames
[pos
].ckid
||
2028 This
->lpBuffer
[1] != This
->idxFrames
[pos
].dwChunkLength
) {
2029 ERR(": block %d not found at 0x%08X\n", pos
, This
->idxFrames
[pos
].dwChunkOffset
);
2030 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2031 (char*)&This
->idxFrames
[pos
].ckid
, This
->idxFrames
[pos
].ckid
,
2032 This
->idxFrames
[pos
].dwChunkLength
);
2033 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n",
2034 (char*)&This
->lpBuffer
[0], This
->lpBuffer
[0], This
->lpBuffer
[1]);
2035 return AVIERR_FILEREAD
;
2038 if (mmioSeek(This
->paf
->hmmio
, This
->idxFrames
[pos
].dwChunkOffset
+ 2 * sizeof(DWORD
), SEEK_SET
) == -1)
2039 return AVIERR_FILEREAD
;
2040 if (mmioRead(This
->paf
->hmmio
, buffer
, size
) != size
)
2041 return AVIERR_FILEREAD
;
2047 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl
*This
, LPLONG pos
, LPLONG offset
)
2051 /* pre-conditions */
2052 assert(This
!= NULL
);
2053 assert(pos
!= NULL
);
2054 assert(offset
!= NULL
);
2055 assert(This
->sInfo
.dwSampleSize
!= 0);
2056 assert(*pos
>= This
->sInfo
.dwStart
);
2058 /* convert start sample to start bytes */
2059 (*offset
) = (*pos
) - This
->sInfo
.dwStart
;
2060 (*offset
) *= This
->sInfo
.dwSampleSize
;
2062 /* convert bytes to block number */
2063 for (block
= 0; block
<= This
->lLastFrame
; block
++) {
2064 if (This
->idxFrames
[block
].dwChunkLength
<= *offset
)
2065 (*offset
) -= This
->idxFrames
[block
].dwChunkLength
;
2073 static HRESULT
AVIFILE_SaveFile(IAVIFileImpl
*This
)
2075 MainAVIHeader MainAVIHdr
;
2076 IAVIStreamImpl
* pStream
;
2085 /* initialize some things */
2086 if (This
->dwMoviChunkPos
== 0)
2087 AVIFILE_ComputeMoviStart(This
);
2089 /* written one record too much? */
2090 if (This
->ckLastRecord
.dwFlags
& MMIO_DIRTY
) {
2091 This
->dwNextFramePos
-= 3 * sizeof(DWORD
);
2092 if (This
->nIdxRecords
> 0)
2093 This
->nIdxRecords
--;
2096 AVIFILE_UpdateInfo(This
);
2098 assert(This
->fInfo
.dwScale
!= 0);
2100 memset(&MainAVIHdr
, 0, sizeof(MainAVIHdr
));
2101 MainAVIHdr
.dwMicroSecPerFrame
= MulDiv(This
->fInfo
.dwRate
, 1000000,
2102 This
->fInfo
.dwScale
);
2103 MainAVIHdr
.dwMaxBytesPerSec
= This
->fInfo
.dwMaxBytesPerSec
;
2104 MainAVIHdr
.dwPaddingGranularity
= AVI_HEADERSIZE
;
2105 MainAVIHdr
.dwFlags
= This
->fInfo
.dwFlags
;
2106 MainAVIHdr
.dwTotalFrames
= This
->fInfo
.dwLength
;
2107 MainAVIHdr
.dwInitialFrames
= 0;
2108 MainAVIHdr
.dwStreams
= This
->fInfo
.dwStreams
;
2109 MainAVIHdr
.dwSuggestedBufferSize
= This
->fInfo
.dwSuggestedBufferSize
;
2110 MainAVIHdr
.dwWidth
= This
->fInfo
.dwWidth
;
2111 MainAVIHdr
.dwHeight
= This
->fInfo
.dwHeight
;
2112 MainAVIHdr
.dwInitialFrames
= This
->dwInitialFrames
;
2114 /* now begin writing ... */
2115 mmioSeek(This
->hmmio
, 0, SEEK_SET
);
2119 ckRIFF
.fccType
= formtypeAVI
;
2120 if (mmioCreateChunk(This
->hmmio
, &ckRIFF
, MMIO_CREATERIFF
) != S_OK
)
2121 return AVIERR_FILEWRITE
;
2123 /* AVI headerlist */
2125 ckLIST1
.fccType
= listtypeAVIHEADER
;
2126 if (mmioCreateChunk(This
->hmmio
, &ckLIST1
, MMIO_CREATELIST
) != S_OK
)
2127 return AVIERR_FILEWRITE
;
2130 ck
.ckid
= ckidAVIMAINHDR
;
2131 ck
.cksize
= sizeof(MainAVIHdr
);
2133 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2134 return AVIERR_FILEWRITE
;
2135 if (mmioWrite(This
->hmmio
, (HPSTR
)&MainAVIHdr
, ck
.cksize
) != ck
.cksize
)
2136 return AVIERR_FILEWRITE
;
2137 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2138 return AVIERR_FILEWRITE
;
2140 /* write the headers of each stream into a separate streamheader list */
2141 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2142 AVIStreamHeader strHdr
;
2144 pStream
= This
->ppStreams
[nStream
];
2146 /* begin the new streamheader list */
2148 ckLIST2
.fccType
= listtypeSTREAMHEADER
;
2149 if (mmioCreateChunk(This
->hmmio
, &ckLIST2
, MMIO_CREATELIST
) != S_OK
)
2150 return AVIERR_FILEWRITE
;
2152 /* create an AVIStreamHeader from the AVSTREAMINFO */
2153 strHdr
.fccType
= pStream
->sInfo
.fccType
;
2154 strHdr
.fccHandler
= pStream
->sInfo
.fccHandler
;
2155 strHdr
.dwFlags
= pStream
->sInfo
.dwFlags
;
2156 strHdr
.wPriority
= pStream
->sInfo
.wPriority
;
2157 strHdr
.wLanguage
= pStream
->sInfo
.wLanguage
;
2158 strHdr
.dwInitialFrames
= pStream
->sInfo
.dwInitialFrames
;
2159 strHdr
.dwScale
= pStream
->sInfo
.dwScale
;
2160 strHdr
.dwRate
= pStream
->sInfo
.dwRate
;
2161 strHdr
.dwStart
= pStream
->sInfo
.dwStart
;
2162 strHdr
.dwLength
= pStream
->sInfo
.dwLength
;
2163 strHdr
.dwSuggestedBufferSize
= pStream
->sInfo
.dwSuggestedBufferSize
;
2164 strHdr
.dwQuality
= pStream
->sInfo
.dwQuality
;
2165 strHdr
.dwSampleSize
= pStream
->sInfo
.dwSampleSize
;
2166 strHdr
.rcFrame
.left
= pStream
->sInfo
.rcFrame
.left
;
2167 strHdr
.rcFrame
.top
= pStream
->sInfo
.rcFrame
.top
;
2168 strHdr
.rcFrame
.right
= pStream
->sInfo
.rcFrame
.right
;
2169 strHdr
.rcFrame
.bottom
= pStream
->sInfo
.rcFrame
.bottom
;
2171 /* now write the AVIStreamHeader */
2172 ck
.ckid
= ckidSTREAMHEADER
;
2173 ck
.cksize
= sizeof(strHdr
);
2174 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2175 return AVIERR_FILEWRITE
;
2176 if (mmioWrite(This
->hmmio
, (HPSTR
)&strHdr
, ck
.cksize
) != ck
.cksize
)
2177 return AVIERR_FILEWRITE
;
2178 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2179 return AVIERR_FILEWRITE
;
2181 /* ... the hopefully ever present streamformat ... */
2182 ck
.ckid
= ckidSTREAMFORMAT
;
2183 ck
.cksize
= pStream
->cbFormat
;
2184 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2185 return AVIERR_FILEWRITE
;
2186 if (pStream
->lpFormat
!= NULL
&& ck
.cksize
> 0) {
2187 if (mmioWrite(This
->hmmio
, pStream
->lpFormat
, ck
.cksize
) != ck
.cksize
)
2188 return AVIERR_FILEWRITE
;
2190 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2191 return AVIERR_FILEWRITE
;
2193 /* ... some optional existing handler data ... */
2194 if (pStream
->lpHandlerData
!= NULL
&& pStream
->cbHandlerData
> 0) {
2195 ck
.ckid
= ckidSTREAMHANDLERDATA
;
2196 ck
.cksize
= pStream
->cbHandlerData
;
2197 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2198 return AVIERR_FILEWRITE
;
2199 if (mmioWrite(This
->hmmio
, pStream
->lpHandlerData
, ck
.cksize
) != ck
.cksize
)
2200 return AVIERR_FILEWRITE
;
2201 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2202 return AVIERR_FILEWRITE
;
2205 /* ... some optional additional extra chunk for this stream ... */
2206 if (pStream
->extra
.lp
!= NULL
&& pStream
->extra
.cb
> 0) {
2207 /* the chunk header(s) are already in the structure */
2208 if (mmioWrite(This
->hmmio
, pStream
->extra
.lp
, pStream
->extra
.cb
) != pStream
->extra
.cb
)
2209 return AVIERR_FILEWRITE
;
2212 /* ... an optional name for this stream ... */
2213 if (pStream
->sInfo
.szName
[0]) {
2216 ck
.ckid
= ckidSTREAMNAME
;
2217 ck
.cksize
= lstrlenW(pStream
->sInfo
.szName
) + 1;
2218 if (ck
.cksize
& 1) /* align */
2220 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2221 return AVIERR_FILEWRITE
;
2223 /* the streamname must be saved in ASCII not Unicode */
2224 str
= HeapAlloc(GetProcessHeap(), 0, ck
.cksize
);
2226 return AVIERR_MEMORY
;
2227 WideCharToMultiByte(CP_ACP
, 0, pStream
->sInfo
.szName
, -1, str
,
2228 ck
.cksize
, NULL
, NULL
);
2230 if (mmioWrite(This
->hmmio
, str
, ck
.cksize
) != ck
.cksize
) {
2231 HeapFree(GetProcessHeap(), 0, str
);
2232 return AVIERR_FILEWRITE
;
2235 HeapFree(GetProcessHeap(), 0, str
);
2236 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2237 return AVIERR_FILEWRITE
;
2240 /* close streamheader list for this stream */
2241 if (mmioAscend(This
->hmmio
, &ckLIST2
, 0) != S_OK
)
2242 return AVIERR_FILEWRITE
;
2243 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2245 /* close the aviheader list */
2246 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
2247 return AVIERR_FILEWRITE
;
2249 /* check for padding to pre-guessed 'movi'-chunk position */
2250 dwPos
= ckLIST1
.dwDataOffset
+ ckLIST1
.cksize
;
2251 if (This
->dwMoviChunkPos
- 2 * sizeof(DWORD
) > dwPos
) {
2252 ck
.ckid
= ckidAVIPADDING
;
2253 ck
.cksize
= This
->dwMoviChunkPos
- dwPos
- 4 * sizeof(DWORD
);
2254 assert((LONG
)ck
.cksize
>= 0);
2256 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2257 return AVIERR_FILEWRITE
;
2258 if (mmioSeek(This
->hmmio
, ck
.cksize
, SEEK_CUR
) == -1)
2259 return AVIERR_FILEWRITE
;
2260 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2261 return AVIERR_FILEWRITE
;
2264 /* now write the 'movi' chunk */
2265 mmioSeek(This
->hmmio
, This
->dwMoviChunkPos
- 2 * sizeof(DWORD
), SEEK_SET
);
2267 ckLIST1
.fccType
= listtypeAVIMOVIE
;
2268 if (mmioCreateChunk(This
->hmmio
, &ckLIST1
, MMIO_CREATELIST
) != S_OK
)
2269 return AVIERR_FILEWRITE
;
2270 if (mmioSeek(This
->hmmio
, This
->dwNextFramePos
, SEEK_SET
) == -1)
2271 return AVIERR_FILEWRITE
;
2272 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
2273 return AVIERR_FILEWRITE
;
2275 /* write 'idx1' chunk */
2276 hr
= AVIFILE_SaveIndex(This
);
2280 /* write optional extra file chunks */
2281 if (This
->fileextra
.lp
!= NULL
&& This
->fileextra
.cb
> 0) {
2282 /* as for the streams, are the chunk header(s) in the structure */
2283 if (mmioWrite(This
->hmmio
, This
->fileextra
.lp
, This
->fileextra
.cb
) != This
->fileextra
.cb
)
2284 return AVIERR_FILEWRITE
;
2287 /* close RIFF chunk */
2288 if (mmioAscend(This
->hmmio
, &ckRIFF
, 0) != S_OK
)
2289 return AVIERR_FILEWRITE
;
2291 /* add some JUNK at end for bad parsers */
2292 memset(&ckRIFF
, 0, sizeof(ckRIFF
));
2293 mmioWrite(This
->hmmio
, (HPSTR
)&ckRIFF
, sizeof(ckRIFF
));
2294 mmioFlush(This
->hmmio
, 0);
2299 static HRESULT
AVIFILE_SaveIndex(const IAVIFileImpl
*This
)
2301 IAVIStreamImpl
*pStream
;
2307 ck
.ckid
= ckidAVINEWINDEX
;
2309 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2310 return AVIERR_FILEWRITE
;
2312 if (This
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) {
2313 /* is interleaved -- write block of corresponding frames */
2314 LONG lInitialFrames
= 0;
2318 if (This
->ppStreams
[0]->sInfo
.dwSampleSize
== 0)
2321 stepsize
= AVIStreamTimeToSample(&This
->ppStreams
[0]->IAVIStream_iface
, 1000000);
2323 assert(stepsize
> 0);
2325 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2326 if (lInitialFrames
< This
->ppStreams
[nStream
]->sInfo
.dwInitialFrames
)
2327 lInitialFrames
= This
->ppStreams
[nStream
]->sInfo
.dwInitialFrames
;
2330 for (i
= -lInitialFrames
; i
< (LONG
)This
->fInfo
.dwLength
- lInitialFrames
;
2332 DWORD nFrame
= lInitialFrames
+ i
;
2334 assert(nFrame
< This
->nIdxRecords
);
2336 idx
.ckid
= listtypeAVIRECORD
;
2337 idx
.dwFlags
= AVIIF_LIST
;
2338 idx
.dwChunkLength
= This
->idxRecords
[nFrame
].dwChunkLength
;
2339 idx
.dwChunkOffset
= This
->idxRecords
[nFrame
].dwChunkOffset
2340 - This
->dwMoviChunkPos
;
2341 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2342 return AVIERR_FILEWRITE
;
2344 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2345 pStream
= This
->ppStreams
[nStream
];
2347 /* heave we reached start of this stream? */
2348 if (-(LONG
)pStream
->sInfo
.dwInitialFrames
> i
)
2351 if (pStream
->sInfo
.dwInitialFrames
< lInitialFrames
)
2352 nFrame
-= (lInitialFrames
- pStream
->sInfo
.dwInitialFrames
);
2354 /* reached end of this stream? */
2355 if (pStream
->lLastFrame
<= nFrame
)
2358 if ((pStream
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
2359 pStream
->sInfo
.dwFormatChangeCount
!= 0 &&
2360 pStream
->idxFmtChanges
!= NULL
) {
2363 for (pos
= 0; pos
< pStream
->sInfo
.dwFormatChangeCount
; pos
++) {
2364 if (pStream
->idxFmtChanges
[pos
].ckid
== nFrame
) {
2365 idx
.dwFlags
= AVIIF_NOTIME
;
2366 idx
.ckid
= MAKEAVICKID(cktypePALchange
, pStream
->nStream
);
2367 idx
.dwChunkLength
= pStream
->idxFmtChanges
[pos
].dwChunkLength
;
2368 idx
.dwChunkOffset
= pStream
->idxFmtChanges
[pos
].dwChunkOffset
2369 - This
->dwMoviChunkPos
;
2371 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2372 return AVIERR_FILEWRITE
;
2376 } /* if have formatchanges */
2378 idx
.ckid
= pStream
->idxFrames
[nFrame
].ckid
;
2379 idx
.dwFlags
= pStream
->idxFrames
[nFrame
].dwFlags
;
2380 idx
.dwChunkLength
= pStream
->idxFrames
[nFrame
].dwChunkLength
;
2381 idx
.dwChunkOffset
= pStream
->idxFrames
[nFrame
].dwChunkOffset
2382 - This
->dwMoviChunkPos
;
2383 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2384 return AVIERR_FILEWRITE
;
2388 /* not interleaved -- write index for each stream at once */
2389 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2390 pStream
= This
->ppStreams
[nStream
];
2392 for (n
= 0; n
<= pStream
->lLastFrame
; n
++) {
2393 if ((pStream
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
2394 (pStream
->sInfo
.dwFormatChangeCount
!= 0)) {
2397 for (pos
= 0; pos
< pStream
->sInfo
.dwFormatChangeCount
; pos
++) {
2398 if (pStream
->idxFmtChanges
[pos
].ckid
== n
) {
2399 idx
.dwFlags
= AVIIF_NOTIME
;
2400 idx
.ckid
= MAKEAVICKID(cktypePALchange
, pStream
->nStream
);
2401 idx
.dwChunkLength
= pStream
->idxFmtChanges
[pos
].dwChunkLength
;
2403 pStream
->idxFmtChanges
[pos
].dwChunkOffset
- This
->dwMoviChunkPos
;
2404 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2405 return AVIERR_FILEWRITE
;
2409 } /* if have formatchanges */
2411 idx
.ckid
= pStream
->idxFrames
[n
].ckid
;
2412 idx
.dwFlags
= pStream
->idxFrames
[n
].dwFlags
;
2413 idx
.dwChunkLength
= pStream
->idxFrames
[n
].dwChunkLength
;
2414 idx
.dwChunkOffset
= pStream
->idxFrames
[n
].dwChunkOffset
2415 - This
->dwMoviChunkPos
;
2417 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2418 return AVIERR_FILEWRITE
;
2421 } /* if not interleaved */
2423 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2424 return AVIERR_FILEWRITE
;
2429 static ULONG
AVIFILE_SearchStream(const IAVIFileImpl
*This
, DWORD fcc
, LONG lSkip
)
2438 /* search the number of the specified stream */
2439 nStream
= (ULONG
)-1;
2440 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
2441 assert(This
->ppStreams
[i
] != NULL
);
2443 if (This
->ppStreams
[i
]->sInfo
.fccType
== fcc
) {
2457 static void AVIFILE_UpdateInfo(IAVIFileImpl
*This
)
2461 /* pre-conditions */
2462 assert(This
!= NULL
);
2464 This
->fInfo
.dwMaxBytesPerSec
= 0;
2465 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
2466 This
->fInfo
.dwSuggestedBufferSize
= 0;
2467 This
->fInfo
.dwWidth
= 0;
2468 This
->fInfo
.dwHeight
= 0;
2469 This
->fInfo
.dwScale
= 0;
2470 This
->fInfo
.dwRate
= 0;
2471 This
->fInfo
.dwLength
= 0;
2472 This
->dwInitialFrames
= 0;
2474 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
2475 AVISTREAMINFOW
*psi
;
2478 /* pre-conditions */
2479 assert(This
->ppStreams
[i
] != NULL
);
2481 psi
= &This
->ppStreams
[i
]->sInfo
;
2482 assert(psi
->dwScale
!= 0);
2483 assert(psi
->dwRate
!= 0);
2486 /* use first stream timings as base */
2487 This
->fInfo
.dwScale
= psi
->dwScale
;
2488 This
->fInfo
.dwRate
= psi
->dwRate
;
2489 This
->fInfo
.dwLength
= psi
->dwLength
;
2491 n
= AVIStreamSampleToSample(&This
->ppStreams
[0]->IAVIStream_iface
,
2492 &This
->ppStreams
[i
]->IAVIStream_iface
, psi
->dwLength
);
2493 if (This
->fInfo
.dwLength
< n
)
2494 This
->fInfo
.dwLength
= n
;
2497 if (This
->dwInitialFrames
< psi
->dwInitialFrames
)
2498 This
->dwInitialFrames
= psi
->dwInitialFrames
;
2500 if (This
->fInfo
.dwSuggestedBufferSize
< psi
->dwSuggestedBufferSize
)
2501 This
->fInfo
.dwSuggestedBufferSize
= psi
->dwSuggestedBufferSize
;
2503 if (psi
->dwSampleSize
!= 0) {
2504 /* fixed sample size -- exact computation */
2505 This
->fInfo
.dwMaxBytesPerSec
+= MulDiv(psi
->dwSampleSize
, psi
->dwRate
,
2508 /* variable sample size -- only upper limit */
2509 This
->fInfo
.dwMaxBytesPerSec
+= MulDiv(psi
->dwSuggestedBufferSize
,
2510 psi
->dwRate
, psi
->dwScale
);
2512 /* update dimensions */
2513 n
= psi
->rcFrame
.right
- psi
->rcFrame
.left
;
2514 if (This
->fInfo
.dwWidth
< n
)
2515 This
->fInfo
.dwWidth
= n
;
2516 n
= psi
->rcFrame
.bottom
- psi
->rcFrame
.top
;
2517 if (This
->fInfo
.dwHeight
< n
)
2518 This
->fInfo
.dwHeight
= n
;
2523 static HRESULT
AVIFILE_WriteBlock(IAVIStreamImpl
*This
, DWORD block
,
2524 FOURCC ckid
, DWORD flags
, LPCVOID buffer
,
2533 /* if no frame/block is already written, we must compute start of movi chunk */
2534 if (This
->paf
->dwMoviChunkPos
== 0)
2535 AVIFILE_ComputeMoviStart(This
->paf
);
2537 if (mmioSeek(This
->paf
->hmmio
, This
->paf
->dwNextFramePos
, SEEK_SET
) == -1)
2538 return AVIERR_FILEWRITE
;
2540 if (mmioCreateChunk(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
2541 return AVIERR_FILEWRITE
;
2542 if (buffer
!= NULL
&& size
> 0) {
2543 if (mmioWrite(This
->paf
->hmmio
, buffer
, size
) != size
)
2544 return AVIERR_FILEWRITE
;
2546 if (mmioAscend(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
2547 return AVIERR_FILEWRITE
;
2549 This
->paf
->fDirty
= TRUE
;
2550 This
->paf
->dwNextFramePos
= mmioSeek(This
->paf
->hmmio
, 0, SEEK_CUR
);
2552 return AVIFILE_AddFrame(This
, ckid
, size
,
2553 ck
.dwDataOffset
- 2 * sizeof(DWORD
), flags
);