2 * Copyright 1999 Marcus Meissner
3 * Copyright 2002 Michael Günnewig
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * - IAVIFile_fnEndRecord: a stub -- needed for creating interleaved AVIs.
22 * - IAVIStreaming interface is missing for the IAVIStreamImpl
23 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
24 * - IAVIStream_fnReadFormat: formatchanges aren't read in.
25 * - IAVIStream_fnDelete: a stub.
26 * - IAVIStream_fnSetInfo: a stub.
39 #include "avifile_private.h"
40 #include "extrachunk.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
47 #define IDX_PER_BLOCK 2730
50 /***********************************************************************/
52 static HRESULT WINAPI
IAVIFile_fnQueryInterface(IAVIFile
* iface
,REFIID refiid
,LPVOID
*obj
);
53 static ULONG WINAPI
IAVIFile_fnAddRef(IAVIFile
* iface
);
54 static ULONG WINAPI
IAVIFile_fnRelease(IAVIFile
* iface
);
55 static HRESULT WINAPI
IAVIFile_fnInfo(IAVIFile
*iface
,AVIFILEINFOW
*afi
,LONG size
);
56 static HRESULT WINAPI
IAVIFile_fnGetStream(IAVIFile
*iface
,PAVISTREAM
*avis
,DWORD fccType
,LONG lParam
);
57 static HRESULT WINAPI
IAVIFile_fnCreateStream(IAVIFile
*iface
,PAVISTREAM
*avis
,AVISTREAMINFOW
*asi
);
58 static HRESULT WINAPI
IAVIFile_fnWriteData(IAVIFile
*iface
,DWORD ckid
,LPVOID lpData
,LONG size
);
59 static HRESULT WINAPI
IAVIFile_fnReadData(IAVIFile
*iface
,DWORD ckid
,LPVOID lpData
,LONG
*size
);
60 static HRESULT WINAPI
IAVIFile_fnEndRecord(IAVIFile
*iface
);
61 static HRESULT WINAPI
IAVIFile_fnDeleteStream(IAVIFile
*iface
,DWORD fccType
,LONG lParam
);
63 struct ICOM_VTABLE(IAVIFile
) iavift
= {
64 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
65 IAVIFile_fnQueryInterface
,
70 IAVIFile_fnCreateStream
,
74 IAVIFile_fnDeleteStream
77 static HRESULT WINAPI
IPersistFile_fnQueryInterface(IPersistFile
*iface
,REFIID refiid
,LPVOID
*obj
);
78 static ULONG WINAPI
IPersistFile_fnAddRef(IPersistFile
*iface
);
79 static ULONG WINAPI
IPersistFile_fnRelease(IPersistFile
*iface
);
80 static HRESULT WINAPI
IPersistFile_fnGetClassID(IPersistFile
*iface
,CLSID
*pClassID
);
81 static HRESULT WINAPI
IPersistFile_fnIsDirty(IPersistFile
*iface
);
82 static HRESULT WINAPI
IPersistFile_fnLoad(IPersistFile
*iface
,LPCOLESTR pszFileName
,DWORD dwMode
);
83 static HRESULT WINAPI
IPersistFile_fnSave(IPersistFile
*iface
,LPCOLESTR pszFileName
,BOOL fRemember
);
84 static HRESULT WINAPI
IPersistFile_fnSaveCompleted(IPersistFile
*iface
,LPCOLESTR pszFileName
);
85 static HRESULT WINAPI
IPersistFile_fnGetCurFile(IPersistFile
*iface
,LPOLESTR
*ppszFileName
);
87 struct ICOM_VTABLE(IPersistFile
) ipersistft
= {
88 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
89 IPersistFile_fnQueryInterface
,
90 IPersistFile_fnAddRef
,
91 IPersistFile_fnRelease
,
92 IPersistFile_fnGetClassID
,
93 IPersistFile_fnIsDirty
,
96 IPersistFile_fnSaveCompleted
,
97 IPersistFile_fnGetCurFile
100 static HRESULT WINAPI
IAVIStream_fnQueryInterface(IAVIStream
*iface
,REFIID refiid
,LPVOID
*obj
);
101 static ULONG WINAPI
IAVIStream_fnAddRef(IAVIStream
*iface
);
102 static ULONG WINAPI
IAVIStream_fnRelease(IAVIStream
* iface
);
103 static HRESULT WINAPI
IAVIStream_fnCreate(IAVIStream
*iface
,LPARAM lParam1
,LPARAM lParam2
);
104 static HRESULT WINAPI
IAVIStream_fnInfo(IAVIStream
*iface
,AVISTREAMINFOW
*psi
,LONG size
);
105 static LONG WINAPI
IAVIStream_fnFindSample(IAVIStream
*iface
,LONG pos
,LONG flags
);
106 static HRESULT WINAPI
IAVIStream_fnReadFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG
*formatsize
);
107 static HRESULT WINAPI
IAVIStream_fnSetFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG formatsize
);
108 static HRESULT WINAPI
IAVIStream_fnRead(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,LONG
*bytesread
,LONG
*samplesread
);
109 static HRESULT WINAPI
IAVIStream_fnWrite(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,DWORD flags
,LONG
*sampwritten
,LONG
*byteswritten
);
110 static HRESULT WINAPI
IAVIStream_fnDelete(IAVIStream
*iface
,LONG start
,LONG samples
);
111 static HRESULT WINAPI
IAVIStream_fnReadData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG
*lpread
);
112 static HRESULT WINAPI
IAVIStream_fnWriteData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG size
);
113 static HRESULT WINAPI
IAVIStream_fnSetInfo(IAVIStream
*iface
,AVISTREAMINFOW
*info
,LONG infolen
);
115 struct ICOM_VTABLE(IAVIStream
) iavist
= {
116 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
117 IAVIStream_fnQueryInterface
,
119 IAVIStream_fnRelease
,
122 IAVIStream_fnFindSample
,
123 IAVIStream_fnReadFormat
,
124 IAVIStream_fnSetFormat
,
128 IAVIStream_fnReadData
,
129 IAVIStream_fnWriteData
,
133 typedef struct _IAVIFileImpl IAVIFileImpl
;
135 typedef struct _IPersistFileImpl
{
137 ICOM_VFIELD(IPersistFile
);
139 /* IPersistFile stuff */
143 typedef struct _IAVIStreamImpl
{
145 ICOM_VFIELD(IAVIStream
);
148 /* IAVIStream stuff */
150 DWORD nStream
; /* the n-th stream in file */
151 AVISTREAMINFOW sInfo
;
156 LPVOID lpHandlerData
;
162 DWORD cbBuffer
; /* size of lpBuffer */
163 DWORD dwCurrentFrame
; /* frame/block currently in lpBuffer */
165 LONG lLastFrame
; /* last correct index in idxFrames */
166 AVIINDEXENTRY
*idxFrames
;
167 DWORD nIdxFrames
; /* upper index limit of idxFrames */
168 AVIINDEXENTRY
*idxFmtChanges
;
169 DWORD nIdxFmtChanges
; /* upper index limit of idxFmtChanges */
172 struct _IAVIFileImpl
{
174 ICOM_VFIELD(IAVIFile
);
177 /* IAVIFile stuff... */
178 IPersistFileImpl iPersistFile
;
181 IAVIStreamImpl
*ppStreams
[MAX_AVISTREAMS
];
183 EXTRACHUNKS fileextra
;
185 DWORD dwMoviChunkPos
; /* some stuff for saving ... */
187 DWORD dwNextFramePos
;
189 AVIINDEXENTRY
*idxRecords
; /* won't be updated while loading */
192 /* IPersistFile stuff ... */
199 /***********************************************************************/
201 static HRESULT
AVIFILE_AddFrame(IAVIStreamImpl
*This
, DWORD ckid
, DWORD size
,
202 DWORD offset
, DWORD flags
);
203 static DWORD
AVIFILE_ComputeMoviStart(IAVIFileImpl
*This
);
204 static void AVIFILE_ConstructAVIStream(IAVIFileImpl
*paf
, DWORD nr
,
205 LPAVISTREAMINFOW asi
);
206 static void AVIFILE_DestructAVIStream(IAVIStreamImpl
*This
);
207 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
);
208 static HRESULT
AVIFILE_LoadIndex(IAVIFileImpl
*This
, DWORD size
, DWORD offset
);
209 static HRESULT
AVIFILE_ParseIndex(IAVIFileImpl
*This
, AVIINDEXENTRY
*lp
,
210 LONG count
, DWORD pos
, BOOL
*bAbsolute
);
211 static HRESULT
AVIFILE_ReadBlock(IAVIStreamImpl
*This
, DWORD start
,
212 LPVOID buffer
, LONG size
);
213 static void AVIFILE_SamplesToBlock(IAVIStreamImpl
*This
, LPLONG pos
,
215 static HRESULT
AVIFILE_SaveFile(IAVIFileImpl
*This
);
216 static HRESULT
AVIFILE_SaveIndex(IAVIFileImpl
*This
);
217 static ULONG
AVIFILE_SearchStream(IAVIFileImpl
*This
, DWORD fccType
,
219 static void AVIFILE_UpdateInfo(IAVIFileImpl
*This
);
220 static HRESULT
AVIFILE_WriteBlock(IAVIStreamImpl
*This
, DWORD block
,
221 FOURCC ckid
, DWORD flags
, LPVOID buffer
,
224 HRESULT
AVIFILE_CreateAVIFile(REFIID riid
, LPVOID
*ppv
)
229 assert(riid
!= NULL
&& ppv
!= NULL
);
233 pfile
= (IAVIFileImpl
*)LocalAlloc(LPTR
, sizeof(IAVIFileImpl
));
235 return AVIERR_MEMORY
;
237 ICOM_VTBL(pfile
) = &iavift
;
238 ICOM_VTBL(&pfile
->iPersistFile
) = &ipersistft
;
240 pfile
->iPersistFile
.paf
= pfile
;
242 hr
= IUnknown_QueryInterface((IUnknown
*)pfile
, riid
, ppv
);
244 LocalFree((HLOCAL
)pfile
);
249 static HRESULT WINAPI
IAVIFile_fnQueryInterface(IAVIFile
*iface
, REFIID refiid
,
252 ICOM_THIS(IAVIFileImpl
,iface
);
254 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(refiid
), obj
);
256 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
257 IsEqualGUID(&IID_IAVIFile
, refiid
)) {
259 IAVIFile_AddRef(iface
);
262 } else if (IsEqualGUID(&IID_IPersistFile
, refiid
)) {
263 *obj
= &This
->iPersistFile
;
264 IAVIFile_AddRef(iface
);
269 return OLE_E_ENUM_NOMORE
;
272 static ULONG WINAPI
IAVIFile_fnAddRef(IAVIFile
*iface
)
274 ICOM_THIS(IAVIFileImpl
,iface
);
276 TRACE("(%p) -> %ld\n", iface
, This
->ref
+ 1);
277 return ++(This
->ref
);
280 static ULONG WINAPI
IAVIFile_fnRelease(IAVIFile
*iface
)
282 ICOM_THIS(IAVIFileImpl
,iface
);
285 TRACE("(%p) -> %ld\n", iface
, This
->ref
- 1);
287 if (!--(This
->ref
)) {
289 /* need to write headers to file */
290 AVIFILE_SaveFile(This
);
293 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
294 if (This
->ppStreams
[i
] != NULL
) {
295 if (This
->ppStreams
[i
]->ref
!= 0) {
296 ERR(": someone has still a reference to stream %u (%p)!\n",
297 i
, This
->ppStreams
[i
]);
299 AVIFILE_DestructAVIStream(This
->ppStreams
[i
]);
300 LocalFree((HLOCAL
)This
->ppStreams
[i
]);
301 This
->ppStreams
[i
] = NULL
;
305 if (This
->idxRecords
!= NULL
) {
306 GlobalFreePtr(This
->idxRecords
);
307 This
->idxRecords
= NULL
;
308 This
->nIdxRecords
= 0;
311 if (This
->fileextra
.lp
!= NULL
) {
312 GlobalFreePtr(This
->fileextra
.lp
);
313 This
->fileextra
.lp
= NULL
;
314 This
->fileextra
.cb
= 0;
317 if (This
->szFileName
!= NULL
) {
318 LocalFree((HLOCAL
)This
->szFileName
);
319 This
->szFileName
= NULL
;
321 if (This
->hmmio
!= NULL
) {
322 mmioClose(This
->hmmio
, 0);
326 LocalFree((HLOCAL
)This
);
332 static HRESULT WINAPI
IAVIFile_fnInfo(IAVIFile
*iface
, LPAVIFILEINFOW afi
,
335 ICOM_THIS(IAVIFileImpl
,iface
);
337 TRACE("(%p,%p,%ld)\n",iface
,afi
,size
);
340 return AVIERR_BADPARAM
;
342 return AVIERR_BADSIZE
;
344 AVIFILE_UpdateInfo(This
);
346 memcpy(afi
, &This
->fInfo
, min(size
, sizeof(This
->fInfo
)));
348 if (size
< sizeof(This
->fInfo
))
349 return AVIERR_BUFFERTOOSMALL
;
353 static HRESULT WINAPI
IAVIFile_fnGetStream(IAVIFile
*iface
, PAVISTREAM
*avis
,
354 DWORD fccType
, LONG lParam
)
356 ICOM_THIS(IAVIFileImpl
,iface
);
360 TRACE("(%p,%p,0x%08lX,%ld)\n", iface
, avis
, fccType
, lParam
);
362 if (avis
== NULL
|| lParam
< 0)
363 return AVIERR_BADPARAM
;
365 nStream
= AVIFILE_SearchStream(This
, fccType
, lParam
);
367 /* Does the requested stream exist? */
368 if (nStream
< This
->fInfo
.dwStreams
&&
369 This
->ppStreams
[nStream
] != NULL
) {
370 *avis
= (PAVISTREAM
)This
->ppStreams
[nStream
];
371 IAVIStream_AddRef(*avis
);
376 /* Sorry, but the specified stream doesn't exist */
377 return AVIERR_NODATA
;
380 static HRESULT WINAPI
IAVIFile_fnCreateStream(IAVIFile
*iface
,PAVISTREAM
*avis
,
381 LPAVISTREAMINFOW asi
)
383 ICOM_THIS(IAVIFileImpl
,iface
);
387 TRACE("(%p,%p,%p)\n", iface
, avis
, asi
);
389 /* check parameters */
390 if (avis
== NULL
|| asi
== NULL
)
391 return AVIERR_BADPARAM
;
395 /* Does the user have write permission? */
396 if ((This
->uMode
& MMIO_RWMODE
) == 0)
397 return AVIERR_READONLY
;
399 /* Can we add another stream? */
400 n
= This
->fInfo
.dwStreams
;
401 if (n
>= MAX_AVISTREAMS
|| This
->dwMoviChunkPos
!= 0) {
402 /* already reached max nr of streams
403 * or have already written frames to disk */
404 return AVIERR_UNSUPPORTED
;
407 /* check AVISTREAMINFO for some really needed things */
408 if (asi
->fccType
== 0 || asi
->dwScale
== 0 || asi
->dwRate
== 0)
409 return AVIERR_BADFORMAT
;
411 /* now it seems to be save to add the stream */
412 assert(This
->ppStreams
[n
] == NULL
);
413 This
->ppStreams
[n
] = (IAVIStreamImpl
*)LocalAlloc(LPTR
,
414 sizeof(IAVIStreamImpl
));
415 if (This
->ppStreams
[n
] == NULL
)
416 return AVIERR_MEMORY
;
418 /* initialize the new allocated stream */
419 AVIFILE_ConstructAVIStream(This
, n
, asi
);
421 This
->fInfo
.dwStreams
++;
424 /* update our AVIFILEINFO structure */
425 AVIFILE_UpdateInfo(This
);
428 *avis
= (PAVISTREAM
)This
->ppStreams
[n
];
429 IAVIStream_AddRef(*avis
);
434 static HRESULT WINAPI
IAVIFile_fnWriteData(IAVIFile
*iface
, DWORD ckid
,
435 LPVOID lpData
, LONG size
)
437 ICOM_THIS(IAVIFileImpl
,iface
);
439 TRACE("(%p,0x%08lX,%p,%ld)\n", iface
, ckid
, lpData
, size
);
441 /* check parameters */
443 return AVIERR_BADPARAM
;
445 return AVIERR_BADSIZE
;
447 /* Do we have write permission? */
448 if ((This
->uMode
& MMIO_RWMODE
) == 0)
449 return AVIERR_READONLY
;
453 return WriteExtraChunk(&This
->fileextra
, ckid
, lpData
, size
);
456 static HRESULT WINAPI
IAVIFile_fnReadData(IAVIFile
*iface
, DWORD ckid
,
457 LPVOID lpData
, LONG
*size
)
459 ICOM_THIS(IAVIFileImpl
,iface
);
461 TRACE("(%p,0x%08lX,%p,%p)\n", iface
, ckid
, lpData
, size
);
463 return ReadExtraChunk(&This
->fileextra
, ckid
, lpData
, size
);
466 static HRESULT WINAPI
IAVIFile_fnEndRecord(IAVIFile
*iface
)
468 ICOM_THIS(IAVIFileImpl
,iface
);
470 FIXME("(%p): stub\n",iface
);
472 if ((This
->uMode
& MMIO_RWMODE
) == 0)
473 return AVIERR_READONLY
;
477 /* FIXME: end record -- for interleaved files */
482 static HRESULT WINAPI
IAVIFile_fnDeleteStream(IAVIFile
*iface
, DWORD fccType
,
485 ICOM_THIS(IAVIFileImpl
,iface
);
489 TRACE("(%p,0x%08lX,%ld)\n", iface
, fccType
, lParam
);
491 /* check parameter */
493 return AVIERR_BADPARAM
;
495 /* Habe user write permissions? */
496 if ((This
->uMode
& MMIO_RWMODE
) == 0)
497 return AVIERR_READONLY
;
499 nStream
= AVIFILE_SearchStream(This
, fccType
, lParam
);
501 /* Does the requested stream exist? */
502 if (nStream
< This
->fInfo
.dwStreams
&&
503 This
->ppStreams
[nStream
] != NULL
) {
504 /* ... so delete it now */
505 LocalFree((HLOCAL
)This
->ppStreams
[nStream
]);
507 if (This
->fInfo
.dwStreams
- nStream
> 0)
508 memcpy(This
->ppStreams
+ nStream
, This
->ppStreams
+ nStream
+ 1,
509 (This
->fInfo
.dwStreams
- nStream
) * sizeof(IAVIStreamImpl
*));
511 This
->ppStreams
[This
->fInfo
.dwStreams
] = NULL
;
512 This
->fInfo
.dwStreams
--;
515 /* This->fInfo will be updated further when asked for */
518 return AVIERR_NODATA
;
521 /***********************************************************************/
523 static HRESULT WINAPI
IPersistFile_fnQueryInterface(IPersistFile
*iface
,
524 REFIID refiid
, LPVOID
*obj
)
526 ICOM_THIS(IPersistFileImpl
,iface
);
528 assert(This
->paf
!= NULL
);
530 return IAVIFile_QueryInterface((PAVIFILE
)This
->paf
, refiid
, obj
);
533 static ULONG WINAPI
IPersistFile_fnAddRef(IPersistFile
*iface
)
535 ICOM_THIS(IPersistFileImpl
,iface
);
537 assert(This
->paf
!= NULL
);
539 return IAVIFile_AddRef((PAVIFILE
)This
->paf
);
542 static ULONG WINAPI
IPersistFile_fnRelease(IPersistFile
*iface
)
544 ICOM_THIS(IPersistFileImpl
,iface
);
546 assert(This
->paf
!= NULL
);
548 return IAVIFile_Release((PAVIFILE
)This
->paf
);
551 static HRESULT WINAPI
IPersistFile_fnGetClassID(IPersistFile
*iface
,
554 TRACE("(%p,%p)\n", iface
, pClassID
);
556 if (pClassID
== NULL
)
557 return AVIERR_BADPARAM
;
559 memcpy(pClassID
, &CLSID_AVIFile
, sizeof(CLSID_AVIFile
));
564 static HRESULT WINAPI
IPersistFile_fnIsDirty(IPersistFile
*iface
)
566 ICOM_THIS(IPersistFileImpl
,iface
);
568 TRACE("(%p)\n", iface
);
570 assert(This
->paf
!= NULL
);
572 return (This
->paf
->fDirty
? S_OK
: S_FALSE
);
575 static HRESULT WINAPI
IPersistFile_fnLoad(IPersistFile
*iface
,
576 LPCOLESTR pszFileName
, DWORD dwMode
)
578 ICOM_THIS(IPersistFileImpl
,iface
);
582 TRACE("(%p,%s,0x%08lX)\n", iface
, debugstr_w(pszFileName
), dwMode
);
584 /* check parameter */
585 if (pszFileName
== NULL
)
586 return AVIERR_BADPARAM
;
588 assert(This
->paf
!= NULL
);
589 if (This
->paf
->hmmio
!= NULL
)
590 return AVIERR_ERROR
; /* No reuse of this object for another file! */
592 /* remeber mode and name */
593 This
->paf
->uMode
= dwMode
;
595 len
= lstrlenW(pszFileName
) + 1;
596 This
->paf
->szFileName
= (LPWSTR
)LocalAlloc(LPTR
, len
* sizeof(WCHAR
));
597 if (This
->paf
->szFileName
== NULL
)
598 return AVIERR_MEMORY
;
599 lstrcpyW(This
->paf
->szFileName
, pszFileName
);
601 /* try to open the file */
602 This
->paf
->hmmio
= mmioOpenW(This
->paf
->szFileName
, NULL
,
603 MMIO_ALLOCBUF
| dwMode
);
604 if (This
->paf
->hmmio
== NULL
)
605 return AVIERR_FILEOPEN
;
607 /* should we create a new file? */
608 if (dwMode
& OF_CREATE
) {
609 memset(& This
->paf
->fInfo
, 0, sizeof(This
->paf
->fInfo
));
610 This
->paf
->fInfo
.dwFlags
= AVIFILEINFO_HASINDEX
| AVIFILEINFO_TRUSTCKTYPE
;
614 return AVIFILE_LoadFile(This
->paf
);
617 static HRESULT WINAPI
IPersistFile_fnSave(IPersistFile
*iface
,
618 LPCOLESTR pszFileName
,BOOL fRemember
)
620 TRACE("(%p,%s,%d)\n", iface
, debugstr_w(pszFileName
), fRemember
);
622 /* We write directly to disk, so nothing to do. */
627 static HRESULT WINAPI
IPersistFile_fnSaveCompleted(IPersistFile
*iface
,
628 LPCOLESTR pszFileName
)
630 TRACE("(%p,%s)\n", iface
, debugstr_w(pszFileName
));
632 /* We write directly to disk, so nothing to do. */
637 static HRESULT WINAPI
IPersistFile_fnGetCurFile(IPersistFile
*iface
,
638 LPOLESTR
*ppszFileName
)
640 ICOM_THIS(IPersistFileImpl
,iface
);
642 TRACE("(%p,%p)\n", iface
, ppszFileName
);
644 if (ppszFileName
== NULL
)
645 return AVIERR_BADPARAM
;
647 *ppszFileName
= NULL
;
649 assert(This
->paf
!= NULL
);
651 if (This
->paf
->szFileName
!= NULL
) {
652 int len
= lstrlenW(This
->paf
->szFileName
);
654 *ppszFileName
= (LPOLESTR
)GlobalAllocPtr(GHND
, len
* sizeof(WCHAR
));
655 if (*ppszFileName
== NULL
)
656 return AVIERR_MEMORY
;
658 memcpy(*ppszFileName
, This
->paf
->szFileName
, len
* sizeof(WCHAR
));
664 /***********************************************************************/
666 static HRESULT WINAPI
IAVIStream_fnQueryInterface(IAVIStream
*iface
,
667 REFIID refiid
, LPVOID
*obj
)
669 ICOM_THIS(IAVIStreamImpl
,iface
);
671 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
673 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
674 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
676 IAVIStream_AddRef(iface
);
680 /* FIXME: IAVIStreaming interface */
682 return OLE_E_ENUM_NOMORE
;
685 static ULONG WINAPI
IAVIStream_fnAddRef(IAVIStream
*iface
)
687 ICOM_THIS(IAVIStreamImpl
,iface
);
689 TRACE("(%p) -> %ld\n", iface
, This
->ref
+ 1);
691 /* also add ref to parent, so that it doesn't kill us */
692 if (This
->paf
!= NULL
)
693 IAVIFile_AddRef((PAVIFILE
)This
->paf
);
695 return ++(This
->ref
);
698 static ULONG WINAPI
IAVIStream_fnRelease(IAVIStream
* iface
)
700 ICOM_THIS(IAVIStreamImpl
,iface
);
702 TRACE("(%p) -> %ld\n", iface
, This
->ref
- 1);
704 /* we belong to the AVIFile, which must free us! */
705 if (This
->ref
== 0) {
706 ERR(": already released!\n");
710 if (This
->paf
!= NULL
)
711 IAVIFile_Release((PAVIFILE
)This
->paf
);
716 static HRESULT WINAPI
IAVIStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
719 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
721 /* This IAVIStream interface needs an AVIFile */
722 return AVIERR_UNSUPPORTED
;
725 static HRESULT WINAPI
IAVIStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
728 ICOM_THIS(IAVIStreamImpl
,iface
);
730 TRACE("(%p,%p,%ld)\n", iface
, psi
, size
);
733 return AVIERR_BADPARAM
;
735 return AVIERR_BADSIZE
;
737 memcpy(psi
, &This
->sInfo
, min(size
, sizeof(This
->sInfo
)));
739 if (size
< sizeof(This
->sInfo
))
740 return AVIERR_BUFFERTOOSMALL
;
744 static LONG WINAPI
IAVIStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
747 ICOM_THIS(IAVIStreamImpl
,iface
);
751 TRACE("(%p,%ld,0x%08lX)\n",iface
,pos
,flags
);
753 if (flags
& FIND_FROM_START
) {
754 pos
= This
->sInfo
.dwStart
;
755 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
759 if (This
->sInfo
.dwSampleSize
!= 0) {
760 /* convert samples into block number with offset */
761 AVIFILE_SamplesToBlock(This
, &pos
, &offset
);
764 if (flags
& FIND_TYPE
) {
765 if (flags
& FIND_KEY
) {
766 while (0 <= pos
&& pos
<= This
->lLastFrame
) {
767 if (This
->idxFrames
[pos
].dwFlags
& AVIIF_KEYFRAME
)
770 if (flags
& FIND_NEXT
)
775 } else if (flags
& FIND_ANY
) {
776 while (0 <= pos
&& pos
<= This
->lLastFrame
) {
777 if (This
->idxFrames
[pos
].dwChunkLength
> 0)
780 if (flags
& FIND_NEXT
)
786 } else if ((flags
& FIND_FORMAT
) && This
->idxFmtChanges
!= NULL
&&
787 This
->sInfo
.fccType
== streamtypeVIDEO
) {
790 if (flags
& FIND_NEXT
) {
791 for (n
= 0; n
< This
->sInfo
.dwFormatChangeCount
; n
++)
792 if (This
->idxFmtChanges
[n
].ckid
>= pos
)
795 for (n
= This
->sInfo
.dwFormatChangeCount
; n
>= 0; n
--) {
796 if (This
->idxFmtChanges
[n
].ckid
<= pos
)
800 if (pos
> This
->sInfo
.dwStart
)
801 return 0; /* format changes always for first frame */
808 if (flags
& FIND_RET
) {
810 if (flags
& FIND_LENGTH
) {
812 if (This
->sInfo
.dwSampleSize
)
813 pos
= This
->sInfo
.dwSampleSize
;
816 } else if (flags
& FIND_OFFSET
) {
817 /* physical position */
818 pos
= This
->idxFrames
[pos
].dwChunkOffset
+ offset
* This
->sInfo
.dwSampleSize
;
819 } else if (flags
& FIND_SIZE
) {
821 pos
= This
->idxFrames
[pos
].dwChunkLength
;
822 } else if (flags
& FIND_INDEX
) {
823 FIXME(": FIND_INDEX flag is not supported!");
825 pos
= This
->paf
->dwIdxChunkPos
;
826 } /* else logical position */
834 static HRESULT WINAPI
IAVIStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
835 LPVOID format
, LONG
*formatsize
)
837 ICOM_THIS(IAVIStreamImpl
,iface
);
839 TRACE("(%p,%ld,%p,%p)\n", iface
, pos
, format
, formatsize
);
841 if (formatsize
== NULL
)
842 return AVIERR_BADPARAM
;
844 /* only interested in needed buffersize? */
845 if (format
== NULL
|| *formatsize
<= 0) {
846 *formatsize
= This
->cbFormat
;
851 /* copy initial format (only as much as will fit) */
852 memcpy(format
, This
->lpFormat
, min(*formatsize
, This
->cbFormat
));
853 if (*formatsize
< This
->cbFormat
) {
854 *formatsize
= This
->cbFormat
;
855 return AVIERR_BUFFERTOOSMALL
;
858 /* Could format change? When yes will it change? */
859 if ((This
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
860 pos
> This
->sInfo
.dwStart
) {
863 lLastFmt
= IAVIStream_fnFindSample(iface
, pos
, FIND_FORMAT
|FIND_PREV
);
865 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt
);
869 *formatsize
= This
->cbFormat
;
873 static HRESULT WINAPI
IAVIStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
874 LPVOID format
, LONG formatsize
)
876 ICOM_THIS(IAVIStreamImpl
,iface
);
878 LPBITMAPINFOHEADER lpbiNew
= (LPBITMAPINFOHEADER
)format
;
880 TRACE("(%p,%ld,%p,%ld)\n", iface
, pos
, format
, formatsize
);
882 /* check parameters */
883 if (format
== NULL
|| formatsize
<= 0)
884 return AVIERR_BADPARAM
;
886 /* Do we have write permission? */
887 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
888 return AVIERR_READONLY
;
890 /* can only set format before frame is written! */
891 if (This
->lLastFrame
> pos
)
892 return AVIERR_UNSUPPORTED
;
894 /* initial format or a formatchange? */
895 if (This
->lpFormat
== NULL
) {
897 if (This
->paf
->dwMoviChunkPos
!= 0)
898 return AVIERR_ERROR
; /* user has used API in wrong sequnece! */
900 This
->lpFormat
= GlobalAllocPtr(GMEM_MOVEABLE
, formatsize
);
901 if (This
->lpFormat
== NULL
)
902 return AVIERR_MEMORY
;
903 This
->cbFormat
= formatsize
;
905 memcpy(This
->lpFormat
, format
, formatsize
);
907 /* update some infos about stream */
908 if (This
->sInfo
.fccType
== streamtypeVIDEO
) {
911 lDim
= This
->sInfo
.rcFrame
.right
- This
->sInfo
.rcFrame
.left
;
912 if (lDim
< lpbiNew
->biWidth
)
913 This
->sInfo
.rcFrame
.right
= This
->sInfo
.rcFrame
.left
+ lpbiNew
->biWidth
;
914 lDim
= This
->sInfo
.rcFrame
.bottom
- This
->sInfo
.rcFrame
.top
;
915 if (lDim
< lpbiNew
->biHeight
)
916 This
->sInfo
.rcFrame
.bottom
= This
->sInfo
.rcFrame
.top
+ lpbiNew
->biHeight
;
917 } else if (This
->sInfo
.fccType
== streamtypeAUDIO
)
918 This
->sInfo
.dwSampleSize
= ((LPWAVEFORMATEX
)This
->lpFormat
)->nBlockAlign
;
923 LPBITMAPINFOHEADER lpbiOld
= (LPBITMAPINFOHEADER
)This
->lpFormat
;
924 RGBQUAD
*rgbNew
= (RGBQUAD
*)((LPBYTE
)lpbiNew
+ lpbiNew
->biSize
);
925 AVIPALCHANGE
*lppc
= NULL
;
928 /* pherhaps formatchange, check it ... */
929 if (This
->cbFormat
!= formatsize
)
930 return AVIERR_UNSUPPORTED
;
932 /* no formatchange, only the initial one */
933 if (memcmp(This
->lpFormat
, format
, formatsize
) == 0)
936 /* check that's only the palette, which changes */
937 if (lpbiOld
->biSize
!= lpbiNew
->biSize
||
938 lpbiOld
->biWidth
!= lpbiNew
->biWidth
||
939 lpbiOld
->biHeight
!= lpbiNew
->biHeight
||
940 lpbiOld
->biPlanes
!= lpbiNew
->biPlanes
||
941 lpbiOld
->biBitCount
!= lpbiNew
->biBitCount
||
942 lpbiOld
->biCompression
!= lpbiNew
->biCompression
||
943 lpbiOld
->biClrUsed
!= lpbiNew
->biClrUsed
)
944 return AVIERR_UNSUPPORTED
;
946 This
->sInfo
.dwFlags
|= AVISTREAMINFO_FORMATCHANGES
;
948 /* simply say all colors have changed */
949 ck
.ckid
= MAKEAVICKID(cktypePALchange
, This
->nStream
);
950 ck
.cksize
= 2 * sizeof(WORD
) + lpbiOld
->biClrUsed
* sizeof(PALETTEENTRY
);
951 lppc
= (AVIPALCHANGE
*)GlobalAllocPtr(GMEM_MOVEABLE
, ck
.cksize
);
953 return AVIERR_MEMORY
;
955 lppc
->bFirstEntry
= 0;
956 lppc
->bNumEntries
= (lpbiOld
->biClrUsed
< 256 ? lpbiOld
->biClrUsed
: 0);
958 for (n
= 0; n
< lpbiOld
->biClrUsed
; n
++) {
959 lppc
->peNew
[n
].peRed
= rgbNew
[n
].rgbRed
;
960 lppc
->peNew
[n
].peGreen
= rgbNew
[n
].rgbGreen
;
961 lppc
->peNew
[n
].peBlue
= rgbNew
[n
].rgbBlue
;
962 lppc
->peNew
[n
].peFlags
= 0;
965 if (mmioSeek(This
->paf
->hmmio
, This
->paf
->dwNextFramePos
, SEEK_SET
) == -1)
966 return AVIERR_FILEWRITE
;
967 if (mmioCreateChunk(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
968 return AVIERR_FILEWRITE
;
969 if (mmioWrite(This
->paf
->hmmio
, (HPSTR
)lppc
, ck
.cksize
) != ck
.cksize
)
970 return AVIERR_FILEWRITE
;
971 if (mmioAscend(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
972 return AVIERR_FILEWRITE
;
973 This
->paf
->dwNextFramePos
+= ck
.cksize
+ 2 * sizeof(DWORD
);
977 return AVIFILE_AddFrame(This
, cktypePALchange
, n
, ck
.dwDataOffset
, 0);
981 static HRESULT WINAPI
IAVIStream_fnRead(IAVIStream
*iface
, LONG start
,
982 LONG samples
, LPVOID buffer
,
983 LONG buffersize
, LPLONG bytesread
,
986 ICOM_THIS(IAVIStreamImpl
,iface
);
991 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface
, start
, samples
, buffer
,
992 buffersize
, bytesread
, samplesread
);
994 /* clear return parameters if given */
995 if (bytesread
!= NULL
)
997 if (samplesread
!= NULL
)
1000 /* check parameters */
1001 if (This
->sInfo
.dwStart
> start
)
1002 return AVIERR_NODATA
; /* couldn't read before start of stream */
1003 if (This
->sInfo
.dwStart
+ This
->sInfo
.dwLength
< start
)
1004 return AVIERR_NODATA
; /* start is past end of stream */
1006 /* should we read as much as possible? */
1007 if (samples
== -1) {
1008 /* User should know how much we have read */
1009 if (bytesread
== NULL
&& samplesread
== NULL
)
1010 return AVIERR_BADPARAM
;
1012 if (This
->sInfo
.dwSampleSize
!= 0)
1013 samples
= buffersize
/ This
->sInfo
.dwSampleSize
;
1018 /* limit to end of stream */
1019 if (This
->sInfo
.dwLength
< samples
)
1020 samples
= This
->sInfo
.dwLength
;
1021 if ((start
- This
->sInfo
.dwStart
) > (This
->sInfo
.dwLength
- samples
))
1022 samples
= This
->sInfo
.dwLength
- (start
- This
->sInfo
.dwStart
);
1024 /* nothing to read? Then leave ... */
1028 if (This
->sInfo
.dwSampleSize
!= 0) {
1029 /* fixed samplesize -- we can read over frame/block boundaries */
1033 /* convert start sample to block,offset pair */
1034 AVIFILE_SamplesToBlock(This
, &block
, &offset
);
1036 /* convert samples to bytes */
1037 samples
*= This
->sInfo
.dwSampleSize
;
1039 while (samples
> 0 && buffersize
> 0) {
1040 if (block
!= This
->dwCurrentFrame
) {
1041 hr
= AVIFILE_ReadBlock(This
, block
, NULL
, 0);
1046 size
= min((DWORD
)samples
, (DWORD
)buffersize
);
1047 size
= min(size
, This
->cbBuffer
- offset
);
1048 memcpy(buffer
, ((BYTE
*)&This
->lpBuffer
[2]) + offset
, size
);
1052 ((BYTE
*)buffer
) += size
;
1056 /* fill out return parameters if given */
1057 if (bytesread
!= NULL
)
1059 if (samplesread
!= NULL
)
1060 *samplesread
+= size
/ This
->sInfo
.dwSampleSize
;
1066 return AVIERR_BUFFERTOOSMALL
;
1068 /* variable samplesize -- we can only read one full frame/block */
1072 assert(start
<= This
->lLastFrame
);
1073 size
= This
->idxFrames
[start
].dwChunkLength
;
1074 if (buffer
!= NULL
&& buffersize
>= size
) {
1075 hr
= AVIFILE_ReadBlock(This
, start
, buffer
, size
);
1078 } else if (buffer
!= NULL
)
1079 return AVIERR_BUFFERTOOSMALL
;
1081 /* fill out return parameters if given */
1082 if (bytesread
!= NULL
)
1084 if (samplesread
!= NULL
)
1085 *samplesread
= samples
;
1091 static HRESULT WINAPI
IAVIStream_fnWrite(IAVIStream
*iface
, LONG start
,
1092 LONG samples
, LPVOID buffer
,
1093 LONG buffersize
, DWORD flags
,
1095 LPLONG byteswritten
)
1097 ICOM_THIS(IAVIStreamImpl
,iface
);
1102 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface
, start
, samples
,
1103 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
1105 /* clear return parameters if given */
1106 if (sampwritten
!= NULL
)
1108 if (byteswritten
!= NULL
)
1111 /* check parameters */
1112 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
1113 return AVIERR_BADPARAM
;
1115 /* Have we write permission? */
1116 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1117 return AVIERR_READONLY
;
1119 switch (This
->sInfo
.fccType
) {
1120 case streamtypeAUDIO
:
1121 ckid
= MAKEAVICKID(cktypeWAVEbytes
, This
->nStream
);
1124 if ((flags
& AVIIF_KEYFRAME
) && buffersize
!= 0)
1125 ckid
= MAKEAVICKID(cktypeDIBbits
, This
->nStream
);
1127 ckid
= MAKEAVICKID(cktypeDIBcompressed
, This
->nStream
);
1131 /* append to end of stream? */
1133 if (This
->lLastFrame
== -1)
1134 start
= This
->sInfo
.dwStart
;
1136 start
= This
->sInfo
.dwLength
;
1137 } else if (This
->lLastFrame
== -1)
1138 This
->sInfo
.dwStart
= start
;
1140 if (This
->sInfo
.dwSampleSize
!= 0) {
1141 /* fixed sample size -- audio like */
1142 if (samples
* This
->sInfo
.dwSampleSize
!= buffersize
)
1143 return AVIERR_BADPARAM
;
1145 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1146 if (This
->sInfo
.dwLength
!= start
)
1147 return AVIERR_UNSUPPORTED
;
1149 /* Convert position to frame/block */
1150 start
= This
->lLastFrame
+ 1;
1152 if ((This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) == 0) {
1153 FIXME(": not interleaved, could collect audio data!\n");
1156 /* variable sample size -- video like */
1158 return AVIERR_UNSUPPORTED
;
1160 /* must we fill up with empty frames? */
1161 if (This
->lLastFrame
!= -1) {
1162 FOURCC ckid2
= MAKEAVICKID(cktypeDIBcompressed
, This
->nStream
);
1164 while (start
> This
->lLastFrame
+ 1) {
1165 hr
= AVIFILE_WriteBlock(This
, This
->lLastFrame
+ 1, ckid2
, 0, NULL
, 0);
1172 /* write the block now */
1173 hr
= AVIFILE_WriteBlock(This
, start
, ckid
, flags
, buffer
, buffersize
);
1174 if (SUCCEEDED(hr
)) {
1175 /* fill out return parameters if given */
1176 if (sampwritten
!= NULL
)
1177 *sampwritten
= samples
;
1178 if (byteswritten
!= NULL
)
1179 *byteswritten
= buffersize
;
1185 static HRESULT WINAPI
IAVIStream_fnDelete(IAVIStream
*iface
, LONG start
,
1188 ICOM_THIS(IAVIStreamImpl
,iface
);
1190 FIXME("(%p,%ld,%ld): stub\n", iface
, start
, samples
);
1192 /* check parameters */
1193 if (start
< 0 || samples
< 0)
1194 return AVIERR_BADPARAM
;
1196 /* Delete before start of stream? */
1197 if (start
+ samples
< This
->sInfo
.dwStart
)
1200 /* Delete after end of stream? */
1201 if (start
> This
->sInfo
.dwLength
)
1204 /* For the rest we need write permissions */
1205 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1206 return AVIERR_READONLY
;
1208 /* 1. overwrite the data with JUNK
1210 * if ISINTERLEAVED {
1211 * 2. concat all neighboured JUNK-blocks in this record to one
1212 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1213 * to start of this record, repeat this.
1215 * 2. concat all neighboured JUNK-blocks.
1216 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1217 * start of this block.
1221 return AVIERR_UNSUPPORTED
;
1224 static HRESULT WINAPI
IAVIStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
1225 LPVOID lp
, LPLONG lpread
)
1227 ICOM_THIS(IAVIStreamImpl
,iface
);
1229 TRACE("(%p,0x%08lX,%p,%p)\n", iface
, fcc
, lp
, lpread
);
1231 if (fcc
== ckidSTREAMHANDLERDATA
) {
1232 if (This
->lpHandlerData
!= NULL
&& This
->cbHandlerData
> 0) {
1233 if (lp
== NULL
|| *lpread
<= 0) {
1234 *lpread
= This
->cbHandlerData
;
1238 memcpy(lp
, This
->lpHandlerData
, min(This
->cbHandlerData
, *lpread
));
1239 if (*lpread
< This
->cbHandlerData
)
1240 return AVIERR_BUFFERTOOSMALL
;
1243 return AVIERR_NODATA
;
1245 return ReadExtraChunk(&This
->extra
, fcc
, lp
, lpread
);
1248 static HRESULT WINAPI
IAVIStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
1249 LPVOID lp
, LONG size
)
1251 ICOM_THIS(IAVIStreamImpl
,iface
);
1253 TRACE("(%p,0x%08lx,%p,%ld)\n", iface
, fcc
, lp
, size
);
1255 /* check parameters */
1257 return AVIERR_BADPARAM
;
1259 return AVIERR_BADSIZE
;
1261 /* need write permission */
1262 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1263 return AVIERR_READONLY
;
1265 /* already written something to this file? */
1266 if (This
->paf
->dwMoviChunkPos
!= 0) {
1267 /* the data will be inserted before the 'movi' chunk, so check for
1269 DWORD dwPos
= AVIFILE_ComputeMoviStart(This
->paf
);
1271 /* ckid,size => 2 * sizeof(DWORD) */
1272 dwPos
+= 2 * sizeof(DWORD
) + size
;
1273 if (size
>= This
->paf
->dwMoviChunkPos
)
1274 return AVIERR_UNSUPPORTED
; /* not enough space left */
1277 This
->paf
->fDirty
= TRUE
;
1279 if (fcc
== ckidSTREAMHANDLERDATA
) {
1280 if (This
->lpHandlerData
!= NULL
) {
1281 FIXME(": handler data already set -- overwirte?\n");
1282 return AVIERR_UNSUPPORTED
;
1285 This
->lpHandlerData
= GlobalAllocPtr(GMEM_MOVEABLE
, size
);
1286 if (This
->lpHandlerData
== NULL
)
1287 return AVIERR_MEMORY
;
1288 This
->cbHandlerData
= size
;
1289 memcpy(This
->lpHandlerData
, lp
, size
);
1293 return WriteExtraChunk(&This
->extra
, fcc
, lp
, size
);
1296 static HRESULT WINAPI
IAVIStream_fnSetInfo(IAVIStream
*iface
,
1297 LPAVISTREAMINFOW info
, LONG infolen
)
1299 FIXME("(%p,%p,%ld): stub\n", iface
, info
, infolen
);
1304 /***********************************************************************/
1306 static HRESULT
AVIFILE_AddFrame(IAVIStreamImpl
*This
, DWORD ckid
, DWORD size
, DWORD offset
, DWORD flags
)
1308 /* pre-conditions */
1309 assert(This
!= NULL
);
1311 switch (TWOCCFromFOURCC(ckid
)) {
1313 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1314 flags
|= AVIIF_KEYFRAME
;
1316 case cktypeDIBcompressed
:
1317 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1318 flags
&= ~AVIIF_KEYFRAME
;
1320 case cktypePALchange
:
1321 if (This
->sInfo
.fccType
!= streamtypeVIDEO
) {
1322 ERR(": found palette change in non-video stream!\n");
1323 return AVIERR_BADFORMAT
;
1325 This
->sInfo
.dwFlags
|= AVISTREAMINFO_FORMATCHANGES
;
1326 This
->sInfo
.dwFormatChangeCount
++;
1328 if (This
->idxFmtChanges
== NULL
|| This
->sInfo
.dwFormatChangeCount
< This
->nIdxFmtChanges
) {
1329 UINT n
= This
->sInfo
.dwFormatChangeCount
;
1331 This
->nIdxFmtChanges
+= 16;
1332 This
->idxFmtChanges
= GlobalReAllocPtr(This
->idxFmtChanges
, This
->nIdxFmtChanges
* sizeof(AVIINDEXENTRY
), GHND
);
1333 if (This
->idxFmtChanges
== NULL
)
1334 return AVIERR_MEMORY
;
1336 This
->idxFmtChanges
[n
].ckid
= This
->lLastFrame
;
1337 This
->idxFmtChanges
[n
].dwFlags
= 0;
1338 This
->idxFmtChanges
[n
].dwChunkOffset
= offset
;
1339 This
->idxFmtChanges
[n
].dwChunkLength
= size
;
1344 case cktypeWAVEbytes
:
1345 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1346 flags
|= AVIIF_KEYFRAME
;
1349 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid
));
1353 /* first frame is alwasy a keyframe */
1354 if (This
->lLastFrame
== -1)
1355 flags
|= AVIIF_KEYFRAME
;
1357 if (This
->sInfo
.dwSuggestedBufferSize
< size
)
1358 This
->sInfo
.dwSuggestedBufferSize
= size
;
1360 /* get memory for index */
1361 if (This
->idxFrames
== NULL
|| This
->lLastFrame
+ 1 >= This
->nIdxFrames
) {
1362 This
->nIdxFrames
+= 512;
1363 This
->idxFrames
= GlobalReAllocPtr(This
->idxFrames
, This
->nIdxFrames
* sizeof(AVIINDEXENTRY
), GHND
);
1364 if (This
->idxFrames
== NULL
)
1365 return AVIERR_MEMORY
;
1369 This
->idxFrames
[This
->lLastFrame
].ckid
= ckid
;
1370 This
->idxFrames
[This
->lLastFrame
].dwFlags
= flags
;
1371 This
->idxFrames
[This
->lLastFrame
].dwChunkOffset
= offset
;
1372 This
->idxFrames
[This
->lLastFrame
].dwChunkLength
= size
;
1374 /* update AVISTREAMINFO structure if neccessary */
1375 if (This
->sInfo
.dwLength
< This
->lLastFrame
)
1376 This
->sInfo
.dwLength
= This
->lLastFrame
;
1381 static DWORD
AVIFILE_ComputeMoviStart(IAVIFileImpl
*This
)
1386 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1387 dwPos
= 11 * sizeof(DWORD
) + sizeof(MainAVIHeader
);
1389 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
1390 IAVIStreamImpl
*pStream
= This
->ppStreams
[nStream
];
1392 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1393 dwPos
+= 7 * sizeof(DWORD
) + sizeof(AVIStreamHeader
);
1394 dwPos
+= ((pStream
->cbFormat
+ 1) & ~1);
1395 if (pStream
->lpHandlerData
!= NULL
&& pStream
->cbHandlerData
> 0)
1396 dwPos
+= 2 * sizeof(DWORD
) + ((pStream
->cbHandlerData
+ 1) & ~1);
1397 if (lstrlenW(pStream
->sInfo
.szName
) > 0)
1398 dwPos
+= 2 * sizeof(DWORD
) + ((lstrlenW(pStream
->sInfo
.szName
) + 1) & ~1);
1401 if (This
->dwMoviChunkPos
== 0) {
1402 This
->dwNextFramePos
= dwPos
;
1404 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1405 if (((dwPos
+ AVI_HEADERSIZE
) & ~(AVI_HEADERSIZE
- 1)) - dwPos
> 2 * sizeof(DWORD
))
1406 This
->dwNextFramePos
= (dwPos
+ AVI_HEADERSIZE
) & ~(AVI_HEADERSIZE
- 1);
1408 This
->dwMoviChunkPos
= This
->dwNextFramePos
- sizeof(DWORD
);
1414 static void AVIFILE_ConstructAVIStream(IAVIFileImpl
*paf
, DWORD nr
, LPAVISTREAMINFOW asi
)
1416 IAVIStreamImpl
*pstream
;
1418 /* pre-conditions */
1419 assert(paf
!= NULL
);
1420 assert(nr
< MAX_AVISTREAMS
);
1421 assert(paf
->ppStreams
[nr
] != NULL
);
1423 pstream
= paf
->ppStreams
[nr
];
1425 ICOM_VTBL(pstream
) = &iavist
;
1428 pstream
->nStream
= nr
;
1429 pstream
->dwCurrentFrame
= -1;
1430 pstream
->lLastFrame
= -1;
1433 memcpy(&pstream
->sInfo
, asi
, sizeof(pstream
->sInfo
));
1435 if (asi
->dwLength
> 0) {
1436 /* pre-allocate mem for frame-index structure */
1437 pstream
->idxFrames
=
1438 (AVIINDEXENTRY
*)GlobalAllocPtr(GHND
, asi
->dwLength
* sizeof(AVIINDEXENTRY
));
1439 if (pstream
->idxFrames
!= NULL
)
1440 pstream
->nIdxFrames
= asi
->dwLength
;
1442 if (asi
->dwFormatChangeCount
> 0) {
1443 /* pre-allocate mem for formatchange-index structure */
1444 pstream
->idxFmtChanges
=
1445 (AVIINDEXENTRY
*)GlobalAllocPtr(GHND
, asi
->dwFormatChangeCount
* sizeof(AVIINDEXENTRY
));
1446 if (pstream
->idxFmtChanges
!= NULL
)
1447 pstream
->nIdxFmtChanges
= asi
->dwFormatChangeCount
;
1450 /* These values will be computed */
1451 pstream
->sInfo
.dwLength
= 0;
1452 pstream
->sInfo
.dwSuggestedBufferSize
= 0;
1453 pstream
->sInfo
.dwFormatChangeCount
= 0;
1454 pstream
->sInfo
.dwEditCount
= 1;
1455 if (pstream
->sInfo
.dwSampleSize
> 0)
1456 SetRectEmpty(&pstream
->sInfo
.rcFrame
);
1459 pstream
->sInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
1462 static void AVIFILE_DestructAVIStream(IAVIStreamImpl
*This
)
1464 /* pre-conditions */
1465 assert(This
!= NULL
);
1467 This
->dwCurrentFrame
= -1;
1468 This
->lLastFrame
= -1;
1470 if (This
->idxFrames
!= NULL
) {
1471 GlobalFreePtr(This
->idxFrames
);
1472 This
->idxFrames
= NULL
;
1473 This
->nIdxFrames
= 0;
1475 if (This
->idxFmtChanges
!= NULL
) {
1476 GlobalFreePtr(This
->idxFmtChanges
);
1477 This
->idxFmtChanges
= NULL
;
1479 if (This
->lpBuffer
!= NULL
) {
1480 GlobalFreePtr(This
->lpBuffer
);
1481 This
->lpBuffer
= NULL
;
1484 if (This
->lpHandlerData
!= NULL
) {
1485 GlobalFreePtr(This
->lpHandlerData
);
1486 This
->lpHandlerData
= NULL
;
1487 This
->cbHandlerData
= 0;
1489 if (This
->extra
.lp
!= NULL
) {
1490 GlobalFreePtr(This
->extra
.lp
);
1491 This
->extra
.lp
= NULL
;
1494 if (This
->lpFormat
!= NULL
) {
1495 GlobalFreePtr(This
->lpFormat
);
1496 This
->lpFormat
= NULL
;
1501 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
)
1503 MainAVIHeader MainAVIHdr
;
1508 IAVIStreamImpl
*pStream
;
1512 if (This
->hmmio
== NULL
)
1513 return AVIERR_FILEOPEN
;
1515 /* initialize stream ptr's */
1516 memset(This
->ppStreams
, 0, sizeof(This
->ppStreams
));
1518 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1519 ckRIFF
.fccType
= formtypeAVI
;
1520 if (mmioDescend(This
->hmmio
, &ckRIFF
, NULL
, MMIO_FINDRIFF
) != S_OK
) {
1521 ERR(": not an AVI!\n");
1522 return AVIERR_FILEREAD
;
1525 /* get "LIST" "hdrl" */
1526 ckLIST1
.fccType
= listtypeAVIHEADER
;
1527 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ckLIST1
, &ckRIFF
, MMIO_FINDLIST
);
1531 /* get "avih" chunk */
1532 ck
.ckid
= ckidAVIMAINHDR
;
1533 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckLIST1
, MMIO_FINDCHUNK
);
1537 if (ck
.cksize
!= sizeof(MainAVIHdr
)) {
1538 ERR(": invalid size of %ld for MainAVIHeader!\n", ck
.cksize
);
1539 return AVIERR_BADFORMAT
;
1541 if (mmioRead(This
->hmmio
, (HPSTR
)&MainAVIHdr
, ck
.cksize
) != ck
.cksize
)
1542 return AVIERR_FILEREAD
;
1544 /* adjust permissions if copyrighted material in file */
1545 if (MainAVIHdr
.dwFlags
& AVIFILEINFO_COPYRIGHTED
) {
1546 This
->uMode
&= ~MMIO_RWMODE
;
1547 This
->uMode
|= MMIO_READ
;
1550 /* convert MainAVIHeader into AVIFILINFOW */
1551 memset(&This
->fInfo
, 0, sizeof(This
->fInfo
));
1552 This
->fInfo
.dwRate
= MainAVIHdr
.dwMicroSecPerFrame
;
1553 This
->fInfo
.dwScale
= 1000000;
1554 This
->fInfo
.dwMaxBytesPerSec
= MainAVIHdr
.dwMaxBytesPerSec
;
1555 This
->fInfo
.dwFlags
= MainAVIHdr
.dwFlags
;
1556 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
1557 This
->fInfo
.dwLength
= MainAVIHdr
.dwTotalFrames
;
1558 This
->fInfo
.dwStreams
= MainAVIHdr
.dwStreams
;
1559 This
->fInfo
.dwSuggestedBufferSize
= MainAVIHdr
.dwSuggestedBufferSize
;
1560 This
->fInfo
.dwWidth
= MainAVIHdr
.dwWidth
;
1561 This
->fInfo
.dwHeight
= MainAVIHdr
.dwHeight
;
1562 LoadStringW(AVIFILE_hModule
, IDS_AVIFILETYPE
, This
->fInfo
.szFileType
,
1563 sizeof(This
->fInfo
.szFileType
));
1565 /* go back to into header list */
1566 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1567 return AVIERR_FILEREAD
;
1569 /* foreach stream exists a "LIST","strl" chunk */
1570 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
1571 /* get next nested chunk in this "LIST","strl" */
1572 if (mmioDescend(This
->hmmio
, &ckLIST2
, &ckLIST1
, 0) != S_OK
)
1573 return AVIERR_FILEREAD
;
1575 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1576 if (ckLIST2
.ckid
== FOURCC_LIST
&&
1577 ckLIST2
.fccType
== listtypeSTREAMHEADER
) {
1578 pStream
= This
->ppStreams
[nStream
] =
1579 (IAVIStreamImpl
*)LocalAlloc(LPTR
, sizeof(IAVIStreamImpl
));
1580 if (pStream
== NULL
)
1581 return AVIERR_MEMORY
;
1582 AVIFILE_ConstructAVIStream(This
, nStream
, NULL
);
1585 while (mmioDescend(This
->hmmio
, &ck
, &ckLIST2
, 0) == S_OK
) {
1587 case ckidSTREAMHANDLERDATA
:
1588 if (pStream
->lpHandlerData
!= NULL
)
1589 return AVIERR_BADFORMAT
;
1590 pStream
->lpHandlerData
= GlobalAllocPtr(GMEM_DDESHARE
|GMEM_MOVEABLE
,
1592 if (pStream
->lpHandlerData
== NULL
)
1593 return AVIERR_MEMORY
;
1594 pStream
->cbHandlerData
= ck
.cksize
;
1596 if (mmioRead(This
->hmmio
, (HPSTR
)pStream
->lpHandlerData
, ck
.cksize
) != ck
.cksize
)
1597 return AVIERR_FILEREAD
;
1599 case ckidSTREAMFORMAT
:
1600 if (pStream
->lpFormat
!= NULL
)
1601 return AVIERR_BADFORMAT
;
1602 pStream
->lpFormat
= GlobalAllocPtr(GMEM_DDESHARE
|GMEM_MOVEABLE
,
1604 if (pStream
->lpFormat
== NULL
)
1605 return AVIERR_MEMORY
;
1606 pStream
->cbFormat
= ck
.cksize
;
1608 if (mmioRead(This
->hmmio
, (HPSTR
)pStream
->lpFormat
, ck
.cksize
) != ck
.cksize
)
1609 return AVIERR_FILEREAD
;
1611 if (pStream
->sInfo
.fccType
== streamtypeVIDEO
) {
1612 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)pStream
->lpFormat
;
1614 /* some corrections to the video format */
1615 if (lpbi
->biClrUsed
== 0 && lpbi
->biBitCount
<= 8)
1616 lpbi
->biClrUsed
= 1u << lpbi
->biBitCount
;
1617 if (lpbi
->biCompression
== BI_RGB
&& lpbi
->biSizeImage
== 0)
1618 lpbi
->biSizeImage
= DIBWIDTHBYTES(*lpbi
) * lpbi
->biHeight
;
1619 if (lpbi
->biCompression
!= BI_RGB
&& lpbi
->biBitCount
== 8) {
1620 if (pStream
->sInfo
.fccHandler
== mmioFOURCC('R','L','E','0') ||
1621 pStream
->sInfo
.fccHandler
== mmioFOURCC('R','L','E',' '))
1622 lpbi
->biCompression
= BI_RLE8
;
1624 if (lpbi
->biCompression
== BI_RGB
&&
1625 (pStream
->sInfo
.fccHandler
== 0 ||
1626 pStream
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E')))
1627 pStream
->sInfo
.fccHandler
= comptypeDIB
;
1629 /* init rcFrame if it's empty */
1630 if (IsRectEmpty(&pStream
->sInfo
.rcFrame
))
1631 SetRect(&pStream
->sInfo
.rcFrame
, 0, 0, lpbi
->biWidth
, lpbi
->biHeight
);
1634 case ckidSTREAMHEADER
:
1636 static const WCHAR streamTypeFmt
[] = {'%','4','.','4','h','s',0};
1638 AVIStreamHeader streamHdr
;
1640 WCHAR streamNameFmt
[25];
1644 if (ck
.cksize
> sizeof(streamHdr
))
1645 n
= sizeof(streamHdr
);
1647 if (mmioRead(This
->hmmio
, (HPSTR
)&streamHdr
, n
) != n
)
1648 return AVIERR_FILEREAD
;
1650 pStream
->sInfo
.fccType
= streamHdr
.fccType
;
1651 pStream
->sInfo
.fccHandler
= streamHdr
.fccHandler
;
1652 pStream
->sInfo
.dwFlags
= streamHdr
.dwFlags
;
1653 pStream
->sInfo
.wPriority
= streamHdr
.wPriority
;
1654 pStream
->sInfo
.wLanguage
= streamHdr
.wLanguage
;
1655 pStream
->sInfo
.dwInitialFrames
= streamHdr
.dwInitialFrames
;
1656 pStream
->sInfo
.dwScale
= streamHdr
.dwScale
;
1657 pStream
->sInfo
.dwRate
= streamHdr
.dwRate
;
1658 pStream
->sInfo
.dwStart
= streamHdr
.dwStart
;
1659 pStream
->sInfo
.dwLength
= streamHdr
.dwLength
;
1660 pStream
->sInfo
.dwSuggestedBufferSize
=
1661 streamHdr
.dwSuggestedBufferSize
;
1662 pStream
->sInfo
.dwQuality
= streamHdr
.dwQuality
;
1663 pStream
->sInfo
.dwSampleSize
= streamHdr
.dwSampleSize
;
1664 pStream
->sInfo
.rcFrame
.left
= streamHdr
.rcFrame
.left
;
1665 pStream
->sInfo
.rcFrame
.top
= streamHdr
.rcFrame
.top
;
1666 pStream
->sInfo
.rcFrame
.right
= streamHdr
.rcFrame
.right
;
1667 pStream
->sInfo
.rcFrame
.bottom
= streamHdr
.rcFrame
.bottom
;
1668 pStream
->sInfo
.dwEditCount
= 0;
1669 pStream
->sInfo
.dwFormatChangeCount
= 0;
1671 /* generate description for stream like "filename.avi Type #n" */
1672 if (streamHdr
.fccType
== streamtypeVIDEO
)
1673 LoadStringW(AVIFILE_hModule
, IDS_VIDEO
, szType
, sizeof(szType
));
1674 else if (streamHdr
.fccType
== streamtypeAUDIO
)
1675 LoadStringW(AVIFILE_hModule
, IDS_AUDIO
, szType
, sizeof(szType
));
1677 wsprintfW(szType
, streamTypeFmt
, (char*)&streamHdr
.fccType
);
1679 /* get count of this streamtype up to this stream */
1681 for (n
= nStream
; 0 <= n
; n
--) {
1682 if (This
->ppStreams
[n
]->sInfo
.fccHandler
== streamHdr
.fccType
)
1686 memset(pStream
->sInfo
.szName
, 0, sizeof(pStream
->sInfo
.szName
));
1688 LoadStringW(AVIFILE_hModule
, IDS_AVISTREAMFORMAT
, streamNameFmt
, sizeof(streamNameFmt
));
1690 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1691 wsprintfW(pStream
->sInfo
.szName
, streamNameFmt
,
1692 AVIFILE_BasenameW(This
->szFileName
), szType
, count
);
1695 case ckidSTREAMNAME
:
1696 { /* streamname will be saved as ASCII string */
1697 LPSTR str
= (LPSTR
)LocalAlloc(LMEM_FIXED
, ck
.cksize
);
1699 return AVIERR_MEMORY
;
1701 if (mmioRead(This
->hmmio
, (HPSTR
)str
, ck
.cksize
) != ck
.cksize
)
1702 return AVIERR_FILEREAD
;
1704 MultiByteToWideChar(CP_ACP
, 0, str
, -1, pStream
->sInfo
.szName
,
1705 sizeof(pStream
->sInfo
.szName
)/sizeof(pStream
->sInfo
.szName
[0]));
1707 LocalFree((HLOCAL
)str
);
1710 case ckidAVIPADDING
:
1711 case mmioFOURCC('p','a','d','d'):
1714 WARN(": found extra chunk 0x%08lX\n", ck
.ckid
);
1715 hr
= ReadChunkIntoExtra(&pStream
->extra
, This
->hmmio
, &ck
);
1720 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1721 return AVIERR_FILEREAD
;
1724 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1725 hr
= ReadChunkIntoExtra(&This
->fileextra
, This
->hmmio
, &ckLIST2
);
1728 if (mmioAscend(This
->hmmio
, &ckLIST2
, 0) != S_OK
)
1729 return AVIERR_FILEREAD
;
1733 /* read any extra headers in "LIST","hdrl" */
1734 FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckLIST1
, 0);
1735 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
1736 return AVIERR_FILEREAD
;
1738 /* search "LIST","movi" chunk in "RIFF","AVI " */
1739 ckLIST1
.fccType
= listtypeAVIMOVIE
;
1740 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ckLIST1
, &ckRIFF
,
1745 This
->dwMoviChunkPos
= ckLIST1
.dwDataOffset
- 2 * sizeof(DWORD
);
1746 This
->dwIdxChunkPos
= ckLIST1
.cksize
+ ckLIST1
.dwDataOffset
;
1747 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
1748 return AVIERR_FILEREAD
;
1750 /* try to find an index */
1751 ck
.ckid
= ckidAVINEWINDEX
;
1752 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
,
1753 &ck
, &ckRIFF
, MMIO_FINDCHUNK
);
1754 if (SUCCEEDED(hr
) && ck
.cksize
> 0) {
1755 if (FAILED(AVIFILE_LoadIndex(This
, ck
.cksize
, ckLIST1
.dwDataOffset
)))
1756 This
->fInfo
.dwFlags
&= ~AVIFILEINFO_HASINDEX
;
1759 /* when we haven't found an index or it's bad, then build one
1760 * by parsing 'movi' chunk */
1761 if ((This
->fInfo
.dwFlags
& AVIFILEINFO_HASINDEX
) == 0) {
1762 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++)
1763 This
->ppStreams
[nStream
]->lLastFrame
= -1;
1765 if (mmioSeek(This
->hmmio
, ckLIST1
.dwDataOffset
+ sizeof(DWORD
), SEEK_SET
) == -1) {
1766 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1767 return AVIERR_FILEREAD
;
1770 /* seek through the 'movi' list until end */
1771 while (mmioDescend(This
->hmmio
, &ck
, &ckLIST1
, 0) == S_OK
) {
1772 if (ck
.ckid
!= FOURCC_LIST
) {
1773 if (mmioAscend(This
->hmmio
, &ck
, 0) == S_OK
) {
1774 nStream
= StreamFromFOURCC(ck
.ckid
);
1776 if (nStream
> This
->fInfo
.dwStreams
)
1777 return AVIERR_BADFORMAT
;
1779 AVIFILE_AddFrame(This
->ppStreams
[nStream
], ck
.ckid
, ck
.cksize
,
1780 ck
.dwDataOffset
- 2 * sizeof(DWORD
), 0);
1782 nStream
= StreamFromFOURCC(ck
.ckid
);
1783 WARN(": file seems to be truncated!\n");
1784 if (nStream
<= This
->fInfo
.dwStreams
&&
1785 This
->ppStreams
[nStream
]->sInfo
.dwSampleSize
> 0) {
1786 ck
.cksize
= mmioSeek(This
->hmmio
, 0, SEEK_END
);
1787 if (ck
.cksize
!= -1) {
1788 ck
.cksize
-= ck
.dwDataOffset
;
1789 ck
.cksize
&= ~(This
->ppStreams
[nStream
]->sInfo
.dwSampleSize
- 1);
1791 AVIFILE_AddFrame(This
->ppStreams
[nStream
], ck
.ckid
, ck
.cksize
,
1792 ck
.dwDataOffset
- 2 * sizeof(DWORD
), 0);
1800 /* find other chunks */
1801 FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckRIFF
, 0);
1806 static HRESULT
AVIFILE_LoadIndex(IAVIFileImpl
*This
, DWORD size
, DWORD offset
)
1810 HRESULT hr
= AVIERR_OK
;
1811 BOOL bAbsolute
= TRUE
;
1813 lp
= (AVIINDEXENTRY
*)GlobalAllocPtr(GMEM_MOVEABLE
,
1814 IDX_PER_BLOCK
* sizeof(AVIINDEXENTRY
));
1816 return AVIERR_MEMORY
;
1818 /* adjust limits for index tables, so that inserting will be faster */
1819 for (n
= 0; n
< This
->fInfo
.dwStreams
; n
++) {
1820 IAVIStreamImpl
*pStream
= This
->ppStreams
[n
];
1822 pStream
->lLastFrame
= -1;
1824 if (pStream
->idxFrames
!= NULL
) {
1825 GlobalFreePtr(pStream
->idxFrames
);
1826 pStream
->idxFrames
= NULL
;
1827 pStream
->nIdxFrames
= 0;
1830 if (pStream
->sInfo
.dwSampleSize
!= 0) {
1831 if (n
> 0 && This
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) {
1832 pStream
->nIdxFrames
= This
->ppStreams
[0]->nIdxFrames
;
1833 } else if (pStream
->sInfo
.dwSuggestedBufferSize
) {
1834 pStream
->nIdxFrames
=
1835 pStream
->sInfo
.dwLength
/ pStream
->sInfo
.dwSuggestedBufferSize
;
1838 pStream
->nIdxFrames
= pStream
->sInfo
.dwLength
;
1840 pStream
->idxFrames
=
1841 (AVIINDEXENTRY
*)GlobalAllocPtr(GHND
, pStream
->nIdxFrames
* sizeof(AVIINDEXENTRY
));
1842 if (pStream
->idxFrames
== NULL
&& pStream
->nIdxFrames
> 0) {
1843 pStream
->nIdxFrames
= 0;
1844 return AVIERR_MEMORY
;
1850 LONG read
= min(IDX_PER_BLOCK
* sizeof(AVIINDEXENTRY
), size
);
1852 if (mmioRead(This
->hmmio
, (HPSTR
)lp
, read
) != read
) {
1853 hr
= AVIERR_FILEREAD
;
1858 if (pos
== (DWORD
)-1)
1859 pos
= offset
- lp
->dwChunkOffset
+ sizeof(DWORD
);
1861 AVIFILE_ParseIndex(This
, lp
, read
/ sizeof(AVIINDEXENTRY
),
1871 static HRESULT
AVIFILE_ParseIndex(IAVIFileImpl
*This
, AVIINDEXENTRY
*lp
,
1872 LONG count
, DWORD pos
, BOOL
*bAbsolute
)
1875 return AVIERR_BADPARAM
;
1877 for (; count
> 0; count
--, lp
++) {
1878 WORD nStream
= StreamFromFOURCC(lp
->ckid
);
1880 if (lp
->ckid
== listtypeAVIRECORD
|| nStream
== 0x7F)
1881 continue; /* skip these */
1883 if (nStream
> This
->fInfo
.dwStreams
)
1884 return AVIERR_BADFORMAT
;
1886 if (*bAbsolute
== TRUE
&& lp
->dwChunkOffset
< This
->dwMoviChunkPos
)
1890 lp
->dwChunkOffset
+= sizeof(DWORD
);
1892 lp
->dwChunkOffset
+= pos
;
1894 if (FAILED(AVIFILE_AddFrame(This
->ppStreams
[nStream
], lp
->ckid
, lp
->dwChunkLength
, lp
->dwChunkOffset
, lp
->dwFlags
)))
1895 return AVIERR_MEMORY
;
1901 static HRESULT
AVIFILE_ReadBlock(IAVIStreamImpl
*This
, DWORD pos
,
1902 LPVOID buffer
, LONG size
)
1904 /* pre-conditions */
1905 assert(This
!= NULL
);
1906 assert(This
->paf
!= NULL
);
1907 assert(This
->paf
->hmmio
!= NULL
);
1908 assert(This
->sInfo
.dwStart
<= pos
&& pos
< This
->sInfo
.dwLength
);
1909 assert(pos
<= This
->lLastFrame
);
1911 /* should we read as much as block gives us? */
1912 if (size
== 0 || size
> This
->idxFrames
[pos
].dwChunkLength
)
1913 size
= This
->idxFrames
[pos
].dwChunkLength
;
1915 /* read into out own buffer or given one? */
1916 if (buffer
== NULL
) {
1917 /* we also read the chunk */
1918 size
+= 2 * sizeof(DWORD
);
1920 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
1921 if (This
->lpBuffer
== NULL
|| size
< This
->cbBuffer
) {
1923 (LPDWORD
)GlobalReAllocPtr(This
->lpBuffer
, max(size
, This
->sInfo
.dwSuggestedBufferSize
), GMEM_MOVEABLE
);
1924 if (This
->lpBuffer
== NULL
)
1925 return AVIERR_MEMORY
;
1926 This
->cbBuffer
= max(size
, This
->sInfo
.dwSuggestedBufferSize
);
1929 /* now read the complete chunk into our buffer */
1930 if (mmioSeek(This
->paf
->hmmio
, This
->idxFrames
[pos
].dwChunkOffset
, SEEK_SET
) == -1)
1931 return AVIERR_FILEREAD
;
1932 if (mmioRead(This
->paf
->hmmio
, (HPSTR
)This
->lpBuffer
, size
) != size
)
1933 return AVIERR_FILEREAD
;
1935 /* check if it was the correct block which we have read */
1936 if (This
->lpBuffer
[0] != This
->idxFrames
[pos
].ckid
||
1937 This
->lpBuffer
[1] != This
->idxFrames
[pos
].dwChunkLength
) {
1938 ERR(": block %ld not found at 0x%08lX\n", pos
, This
->idxFrames
[pos
].dwChunkOffset
);
1939 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1940 (char*)&This
->idxFrames
[pos
].ckid
, This
->idxFrames
[pos
].ckid
,
1941 This
->idxFrames
[pos
].dwChunkLength
);
1942 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
1943 (char*)&This
->lpBuffer
[0], This
->lpBuffer
[0], This
->lpBuffer
[1]);
1944 return AVIERR_FILEREAD
;
1947 if (mmioSeek(This
->paf
->hmmio
, This
->idxFrames
[pos
].dwChunkOffset
+ 2 * sizeof(DWORD
), SEEK_SET
) == -1)
1948 return AVIERR_FILEREAD
;
1949 if (mmioRead(This
->paf
->hmmio
, (HPSTR
)buffer
, size
) != size
)
1950 return AVIERR_FILEREAD
;
1956 static void AVIFILE_SamplesToBlock(IAVIStreamImpl
*This
, LPLONG pos
,
1961 /* pre-conditions */
1962 assert(This
!= NULL
);
1963 assert(pos
!= NULL
);
1964 assert(offset
!= NULL
);
1965 assert(This
->sInfo
.dwSampleSize
!= 0);
1966 assert(*pos
>= This
->sInfo
.dwStart
);
1968 /* convert start sample to start bytes */
1969 (*offset
) = (*pos
) - This
->sInfo
.dwStart
;
1970 (*offset
) *= This
->sInfo
.dwSampleSize
;
1972 /* convert bytes to block number */
1973 for (block
= 0; block
<= This
->lLastFrame
; block
++) {
1974 if (This
->idxFrames
[block
].dwChunkLength
<= *offset
)
1975 (*offset
) -= This
->idxFrames
[block
].dwChunkLength
;
1983 static HRESULT
AVIFILE_SaveFile(IAVIFileImpl
*This
)
1985 MainAVIHeader MainAVIHdr
;
1986 IAVIStreamImpl
* pStream
;
1995 /* initialize some things */
1996 if (This
->dwMoviChunkPos
== 0)
1997 AVIFILE_ComputeMoviStart(This
);
1999 AVIFILE_UpdateInfo(This
);
2001 assert(This
->fInfo
.dwScale
!= 0);
2003 memset(&MainAVIHdr
, 0, sizeof(MainAVIHdr
));
2004 MainAVIHdr
.dwMicroSecPerFrame
= MulDiv(This
->fInfo
.dwRate
, 1000000,
2005 This
->fInfo
.dwScale
);
2006 MainAVIHdr
.dwMaxBytesPerSec
= This
->fInfo
.dwMaxBytesPerSec
;
2007 MainAVIHdr
.dwPaddingGranularity
= AVI_HEADERSIZE
;
2008 MainAVIHdr
.dwFlags
= This
->fInfo
.dwFlags
;
2009 MainAVIHdr
.dwTotalFrames
= This
->fInfo
.dwLength
;
2010 MainAVIHdr
.dwInitialFrames
= 0;
2011 MainAVIHdr
.dwStreams
= This
->fInfo
.dwStreams
;
2012 MainAVIHdr
.dwSuggestedBufferSize
= This
->fInfo
.dwSuggestedBufferSize
;
2013 MainAVIHdr
.dwWidth
= This
->fInfo
.dwWidth
;
2014 MainAVIHdr
.dwHeight
= This
->fInfo
.dwHeight
;
2015 for (nStream
= 0; nStream
< MainAVIHdr
.dwStreams
; nStream
++) {
2016 pStream
= This
->ppStreams
[nStream
];
2018 if (MainAVIHdr
.dwInitialFrames
< pStream
->sInfo
.dwInitialFrames
)
2019 MainAVIHdr
.dwInitialFrames
= pStream
->sInfo
.dwInitialFrames
;
2022 /* now begin writing ... */
2023 mmioSeek(This
->hmmio
, 0, SEEK_SET
);
2027 ckRIFF
.fccType
= formtypeAVI
;
2028 if (mmioCreateChunk(This
->hmmio
, &ckRIFF
, MMIO_CREATERIFF
) != S_OK
)
2029 return AVIERR_FILEWRITE
;
2031 /* AVI headerlist */
2033 ckLIST1
.fccType
= listtypeAVIHEADER
;
2034 if (mmioCreateChunk(This
->hmmio
, &ckLIST1
, MMIO_CREATELIST
) != S_OK
)
2035 return AVIERR_FILEWRITE
;
2038 ck
.ckid
= ckidAVIMAINHDR
;
2039 ck
.cksize
= sizeof(MainAVIHdr
);
2041 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2042 return AVIERR_FILEWRITE
;
2043 if (mmioWrite(This
->hmmio
, (HPSTR
)&MainAVIHdr
, ck
.cksize
) != ck
.cksize
)
2044 return AVIERR_FILEWRITE
;
2045 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2046 return AVIERR_FILEWRITE
;
2048 /* write the headers of each stream into a seperate streamheader list */
2049 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2050 AVIStreamHeader strHdr
;
2052 pStream
= This
->ppStreams
[nStream
];
2054 /* begin the new streamheader list */
2056 ckLIST2
.fccType
= listtypeSTREAMHEADER
;
2057 if (mmioCreateChunk(This
->hmmio
, &ckLIST2
, MMIO_CREATELIST
) != S_OK
)
2058 return AVIERR_FILEWRITE
;
2060 /* create an AVIStreamHeader from the AVSTREAMINFO */
2061 strHdr
.fccType
= pStream
->sInfo
.fccType
;
2062 strHdr
.fccHandler
= pStream
->sInfo
.fccHandler
;
2063 strHdr
.dwFlags
= pStream
->sInfo
.dwFlags
;
2064 strHdr
.wPriority
= pStream
->sInfo
.wPriority
;
2065 strHdr
.wLanguage
= pStream
->sInfo
.wLanguage
;
2066 strHdr
.dwInitialFrames
= pStream
->sInfo
.dwInitialFrames
;
2067 strHdr
.dwScale
= pStream
->sInfo
.dwScale
;
2068 strHdr
.dwRate
= pStream
->sInfo
.dwRate
;
2069 strHdr
.dwStart
= pStream
->sInfo
.dwStart
;
2070 strHdr
.dwLength
= pStream
->sInfo
.dwLength
;
2071 strHdr
.dwSuggestedBufferSize
= pStream
->sInfo
.dwSuggestedBufferSize
;
2072 strHdr
.dwQuality
= pStream
->sInfo
.dwQuality
;
2073 strHdr
.dwSampleSize
= pStream
->sInfo
.dwSampleSize
;
2074 strHdr
.rcFrame
.left
= pStream
->sInfo
.rcFrame
.left
;
2075 strHdr
.rcFrame
.top
= pStream
->sInfo
.rcFrame
.top
;
2076 strHdr
.rcFrame
.right
= pStream
->sInfo
.rcFrame
.right
;
2077 strHdr
.rcFrame
.bottom
= pStream
->sInfo
.rcFrame
.bottom
;
2079 /* now write the AVIStreamHeader */
2080 ck
.ckid
= ckidSTREAMHEADER
;
2081 ck
.cksize
= sizeof(strHdr
);
2082 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2083 return AVIERR_FILEWRITE
;
2084 if (mmioWrite(This
->hmmio
, (HPSTR
)&strHdr
, ck
.cksize
) != ck
.cksize
)
2085 return AVIERR_FILEWRITE
;
2086 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2087 return AVIERR_FILEWRITE
;
2089 /* ... the hopefull ever present streamformat ... */
2090 ck
.ckid
= ckidSTREAMFORMAT
;
2091 ck
.cksize
= pStream
->cbFormat
;
2092 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2093 return AVIERR_FILEWRITE
;
2094 if (pStream
->lpFormat
!= NULL
&& ck
.cksize
> 0) {
2095 if (mmioWrite(This
->hmmio
, (HPSTR
)pStream
->lpFormat
, ck
.cksize
) != ck
.cksize
)
2096 return AVIERR_FILEWRITE
;
2098 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2099 return AVIERR_FILEWRITE
;
2101 /* ... some optional existing handler data ... */
2102 if (pStream
->lpHandlerData
!= NULL
&& pStream
->cbHandlerData
> 0) {
2103 ck
.ckid
= ckidSTREAMHANDLERDATA
;
2104 ck
.cksize
= pStream
->cbHandlerData
;
2105 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2106 return AVIERR_FILEWRITE
;
2107 if (mmioWrite(This
->hmmio
, (HPSTR
)pStream
->lpHandlerData
, ck
.cksize
) != ck
.cksize
)
2108 return AVIERR_FILEWRITE
;
2109 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2110 return AVIERR_FILEWRITE
;
2113 /* ... some optional additional extra chunk for this stream ... */
2114 if (pStream
->extra
.lp
!= NULL
&& pStream
->extra
.cb
> 0) {
2115 /* the chunk header(s) are already in the strucuture */
2116 if (mmioWrite(This
->hmmio
, (HPSTR
)pStream
->extra
.lp
, pStream
->extra
.cb
) != pStream
->extra
.cb
)
2117 return AVIERR_FILEWRITE
;
2120 /* ... an optional name for this stream ... */
2121 if (lstrlenW(pStream
->sInfo
.szName
) > 0) {
2124 ck
.ckid
= ckidSTREAMNAME
;
2125 ck
.cksize
= lstrlenW(pStream
->sInfo
.szName
) + 1;
2126 if (ck
.cksize
& 1) /* align */
2128 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2129 return AVIERR_FILEWRITE
;
2131 /* the streamname must be saved in ASCII not Unicode */
2132 str
= (LPSTR
)LocalAlloc(LPTR
, ck
.cksize
);
2134 return AVIERR_MEMORY
;
2135 WideCharToMultiByte(CP_ACP
, 0, pStream
->sInfo
.szName
, -1, str
,
2136 ck
.cksize
, NULL
, NULL
);
2138 if (mmioWrite(This
->hmmio
, (HPSTR
)str
, ck
.cksize
) != ck
.cksize
) {
2139 LocalFree((HLOCAL
)str
);
2140 return AVIERR_FILEWRITE
;
2143 LocalFree((HLOCAL
)str
);
2144 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2145 return AVIERR_FILEWRITE
;
2148 /* close streamheader list for this stream */
2149 if (mmioAscend(This
->hmmio
, &ckLIST2
, 0) != S_OK
)
2150 return AVIERR_FILEWRITE
;
2151 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2153 /* close the aviheader list */
2154 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
2155 return AVIERR_FILEWRITE
;
2157 /* check for padding to pre-guessed 'movi'-chunk position */
2158 dwPos
= ckLIST1
.dwDataOffset
+ ckLIST1
.cksize
;
2159 if (This
->dwMoviChunkPos
- 2 * sizeof(DWORD
) > dwPos
) {
2160 ck
.ckid
= ckidAVIPADDING
;
2161 ck
.cksize
= This
->dwMoviChunkPos
- dwPos
- 4 * sizeof(DWORD
);
2162 assert((LONG
)ck
.cksize
>= 0);
2164 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2165 return AVIERR_FILEWRITE
;
2166 if (mmioSeek(This
->hmmio
, ck
.cksize
, SEEK_CUR
) == -1)
2167 return AVIERR_FILEWRITE
;
2168 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2169 return AVIERR_FILEWRITE
;
2172 /* now write the 'movi' chunk */
2173 mmioSeek(This
->hmmio
, This
->dwMoviChunkPos
- 2 * sizeof(DWORD
), SEEK_SET
);
2175 ckLIST1
.fccType
= listtypeAVIMOVIE
;
2176 if (mmioCreateChunk(This
->hmmio
, &ckLIST1
, MMIO_CREATELIST
) != S_OK
)
2177 return AVIERR_FILEWRITE
;
2178 if (mmioSeek(This
->hmmio
, This
->dwNextFramePos
, SEEK_SET
) == -1)
2179 return AVIERR_FILEWRITE
;
2180 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
2181 return AVIERR_FILEWRITE
;
2183 /* write 'idx1' chunk */
2184 hr
= AVIFILE_SaveIndex(This
);
2188 /* write optional extra file chunks */
2189 if (This
->fileextra
.lp
!= NULL
&& This
->fileextra
.cb
> 0) {
2190 /* as for the streams, are the chunk header(s) in the structure */
2191 if (mmioWrite(This
->hmmio
, (HPSTR
)This
->fileextra
.lp
, This
->fileextra
.cb
) != This
->fileextra
.cb
)
2192 return AVIERR_FILEWRITE
;
2195 /* close RIFF chunk */
2196 if (mmioAscend(This
->hmmio
, &ckRIFF
, 0) != S_OK
)
2197 return AVIERR_FILEWRITE
;
2199 /* add some JUNK at end for bad parsers */
2200 memset(&ckRIFF
, 0, sizeof(ckRIFF
));
2201 mmioWrite(This
->hmmio
, (HPSTR
)&ckRIFF
, sizeof(ckRIFF
));
2202 mmioFlush(This
->hmmio
, 0);
2207 static HRESULT
AVIFILE_SaveIndex(IAVIFileImpl
*This
)
2209 IAVIStreamImpl
*pStream
;
2215 ck
.ckid
= ckidAVINEWINDEX
;
2217 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2218 return AVIERR_FILEWRITE
;
2220 if (This
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) {
2221 /* is interleaved -- write block of coresponding frames */
2222 LONG lInitialFrames
= 0;
2226 if (This
->ppStreams
[0]->sInfo
.dwSampleSize
== 0)
2229 stepsize
= AVIStreamTimeToSample((PAVISTREAM
)This
->ppStreams
[0], 1000000);
2231 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2232 if (lInitialFrames
< This
->ppStreams
[nStream
]->sInfo
.dwInitialFrames
)
2233 lInitialFrames
= This
->ppStreams
[nStream
]->sInfo
.dwInitialFrames
;
2236 for (i
= -lInitialFrames
; i
< (LONG
)This
->fInfo
.dwLength
- lInitialFrames
;
2238 DWORD nFrame
= lInitialFrames
+ i
;
2240 assert(nFrame
< This
->nIdxRecords
);
2242 idx
.ckid
= listtypeAVIRECORD
;
2243 idx
.dwFlags
= AVIIF_LIST
;
2244 idx
.dwChunkLength
= This
->idxRecords
[nFrame
].dwChunkLength
;
2245 idx
.dwChunkOffset
= This
->idxRecords
[nFrame
].dwChunkOffset
2246 - This
->dwMoviChunkPos
;
2247 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2248 return AVIERR_FILEWRITE
;
2250 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; n
++) {
2251 pStream
= This
->ppStreams
[nStream
];
2253 /* heave we reached start of this stream? */
2254 if (-(LONG
)pStream
->sInfo
.dwInitialFrames
> i
)
2257 if (pStream
->sInfo
.dwInitialFrames
< lInitialFrames
)
2258 nFrame
-= (lInitialFrames
- pStream
->sInfo
.dwInitialFrames
);
2260 /* reached end of this stream? */
2261 if (pStream
->lLastFrame
<= nFrame
)
2264 if ((pStream
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
2265 pStream
->sInfo
.dwFormatChangeCount
!= 0 &&
2266 pStream
->idxFmtChanges
!= NULL
) {
2269 for (pos
= 0; pos
< pStream
->sInfo
.dwFormatChangeCount
; pos
++) {
2270 if (pStream
->idxFmtChanges
[pos
].ckid
== nFrame
) {
2271 idx
.dwFlags
= AVIIF_NOTIME
;
2272 idx
.ckid
= MAKEAVICKID(cktypePALchange
, pStream
->nStream
);
2273 idx
.dwChunkLength
= pStream
->idxFmtChanges
[pos
].dwChunkLength
;
2274 idx
.dwChunkOffset
= pStream
->idxFmtChanges
[pos
].dwChunkOffset
2275 - This
->dwMoviChunkPos
;
2277 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2278 return AVIERR_FILEWRITE
;
2282 } /* if have formatchanges */
2284 idx
.ckid
= pStream
->idxFrames
[nFrame
].ckid
;
2285 idx
.dwFlags
= pStream
->idxFrames
[nFrame
].dwFlags
;
2286 idx
.dwChunkLength
= pStream
->idxFrames
[nFrame
].dwChunkLength
;
2287 idx
.dwChunkOffset
= pStream
->idxFrames
[nFrame
].dwChunkOffset
2288 - This
->dwMoviChunkPos
;
2289 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2290 return AVIERR_FILEWRITE
;
2294 /* not interleaved -- write index for each stream at once */
2295 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2296 pStream
= This
->ppStreams
[nStream
];
2298 if (pStream
->lLastFrame
== -1)
2299 pStream
->lLastFrame
= 0;
2301 for (n
= 0; n
< pStream
->lLastFrame
; n
++) {
2302 if ((pStream
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
2303 (pStream
->sInfo
.dwFormatChangeCount
!= 0)) {
2306 for (pos
= 0; pos
< pStream
->sInfo
.dwFormatChangeCount
; pos
++) {
2307 if (pStream
->idxFmtChanges
[pos
].ckid
== n
) {
2308 idx
.dwFlags
= AVIIF_NOTIME
;
2309 idx
.ckid
= MAKEAVICKID(cktypePALchange
, pStream
->nStream
);
2310 idx
.dwChunkLength
= pStream
->idxFmtChanges
[pos
].dwChunkLength
;
2312 pStream
->idxFmtChanges
[pos
].dwChunkOffset
- This
->dwMoviChunkPos
;
2313 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2314 return AVIERR_FILEWRITE
;
2318 } /* if have formatchanges */
2320 idx
.ckid
= pStream
->idxFrames
[n
].ckid
;
2321 idx
.dwFlags
= pStream
->idxFrames
[n
].dwFlags
;
2322 idx
.dwChunkLength
= pStream
->idxFrames
[n
].dwChunkLength
;
2323 idx
.dwChunkOffset
= pStream
->idxFrames
[n
].dwChunkOffset
2324 - This
->dwMoviChunkPos
;
2326 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2327 return AVIERR_FILEWRITE
;
2330 } /* if not interleaved */
2332 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2333 return AVIERR_FILEWRITE
;
2338 static ULONG
AVIFILE_SearchStream(IAVIFileImpl
*This
, DWORD fcc
, LONG lSkip
)
2347 /* search the number of the specified stream */
2348 nStream
= (ULONG
)-1;
2349 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
2350 assert(This
->ppStreams
[i
] != NULL
);
2352 if (This
->ppStreams
[i
]->sInfo
.fccType
== fcc
) {
2366 static void AVIFILE_UpdateInfo(IAVIFileImpl
*This
)
2370 /* pre-conditions */
2371 assert(This
!= NULL
);
2373 This
->fInfo
.dwMaxBytesPerSec
= 0;
2374 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
2375 This
->fInfo
.dwSuggestedBufferSize
= 0;
2376 This
->fInfo
.dwWidth
= 0;
2377 This
->fInfo
.dwHeight
= 0;
2378 This
->fInfo
.dwScale
= 0;
2379 This
->fInfo
.dwRate
= 0;
2380 This
->fInfo
.dwLength
= 0;
2382 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
2383 AVISTREAMINFOW
*psi
;
2386 /* pre-conditions */
2387 assert(This
->ppStreams
[i
] != NULL
);
2389 psi
= &This
->ppStreams
[i
]->sInfo
;
2390 assert(psi
->dwScale
!= 0);
2391 assert(psi
->dwRate
!= 0);
2394 /* use first stream timings as base */
2395 This
->fInfo
.dwScale
= psi
->dwScale
;
2396 This
->fInfo
.dwRate
= psi
->dwRate
;
2397 This
->fInfo
.dwLength
= psi
->dwLength
;
2399 n
= AVIStreamSampleToSample((PAVISTREAM
)This
->ppStreams
[0],
2400 (PAVISTREAM
)This
->ppStreams
[i
],psi
->dwLength
);
2401 if (This
->fInfo
.dwLength
< n
)
2402 This
->fInfo
.dwLength
= n
;
2405 if (This
->fInfo
.dwSuggestedBufferSize
< psi
->dwSuggestedBufferSize
)
2406 This
->fInfo
.dwSuggestedBufferSize
= psi
->dwSuggestedBufferSize
;
2408 if (psi
->dwSampleSize
!= 0) {
2409 /* fixed sample size -- exact computation */
2410 This
->fInfo
.dwMaxBytesPerSec
+= MulDiv(psi
->dwSampleSize
, psi
->dwRate
,
2413 /* variable sample size -- only upper limit */
2414 This
->fInfo
.dwMaxBytesPerSec
+= MulDiv(psi
->dwSuggestedBufferSize
,
2415 psi
->dwRate
, psi
->dwScale
);
2417 /* update dimensions */
2418 n
= psi
->rcFrame
.right
- psi
->rcFrame
.left
;
2419 if (This
->fInfo
.dwWidth
< n
)
2420 This
->fInfo
.dwWidth
= n
;
2421 n
= psi
->rcFrame
.bottom
- psi
->rcFrame
.top
;
2422 if (This
->fInfo
.dwHeight
< n
)
2423 This
->fInfo
.dwHeight
= n
;
2428 static HRESULT
AVIFILE_WriteBlock(IAVIStreamImpl
*This
, DWORD block
,
2429 FOURCC ckid
, DWORD flags
, LPVOID buffer
,
2438 /* if no frame/block is already written, we must compute start of movi chunk */
2439 if (This
->paf
->dwMoviChunkPos
== 0)
2440 AVIFILE_ComputeMoviStart(This
->paf
);
2442 if (mmioSeek(This
->paf
->hmmio
, This
->paf
->dwNextFramePos
, SEEK_SET
) == -1)
2443 return AVIERR_FILEWRITE
;
2445 if (mmioCreateChunk(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
2446 return AVIERR_FILEWRITE
;
2447 if (buffer
!= NULL
&& size
> 0) {
2448 if (mmioWrite(This
->paf
->hmmio
, (HPSTR
)buffer
, size
) != size
)
2449 return AVIERR_FILEWRITE
;
2451 if (mmioAscend(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
2452 return AVIERR_FILEWRITE
;
2454 This
->paf
->fDirty
= TRUE
;
2455 This
->paf
->dwNextFramePos
= mmioSeek(This
->paf
->hmmio
, 0, SEEK_CUR
);
2457 return AVIFILE_AddFrame(This
, ckid
, size
, ck
.dwDataOffset
, flags
);