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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #define COM_NO_WINDOWS_H
47 #include "avifile_private.h"
48 #include "extrachunk.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
56 #define IDX_PER_BLOCK 2730
59 /***********************************************************************/
61 static HRESULT WINAPI
IAVIFile_fnQueryInterface(IAVIFile
* iface
,REFIID refiid
,LPVOID
*obj
);
62 static ULONG WINAPI
IAVIFile_fnAddRef(IAVIFile
* iface
);
63 static ULONG WINAPI
IAVIFile_fnRelease(IAVIFile
* iface
);
64 static HRESULT WINAPI
IAVIFile_fnInfo(IAVIFile
*iface
,AVIFILEINFOW
*afi
,LONG size
);
65 static HRESULT WINAPI
IAVIFile_fnGetStream(IAVIFile
*iface
,PAVISTREAM
*avis
,DWORD fccType
,LONG lParam
);
66 static HRESULT WINAPI
IAVIFile_fnCreateStream(IAVIFile
*iface
,PAVISTREAM
*avis
,AVISTREAMINFOW
*asi
);
67 static HRESULT WINAPI
IAVIFile_fnWriteData(IAVIFile
*iface
,DWORD ckid
,LPVOID lpData
,LONG size
);
68 static HRESULT WINAPI
IAVIFile_fnReadData(IAVIFile
*iface
,DWORD ckid
,LPVOID lpData
,LONG
*size
);
69 static HRESULT WINAPI
IAVIFile_fnEndRecord(IAVIFile
*iface
);
70 static HRESULT WINAPI
IAVIFile_fnDeleteStream(IAVIFile
*iface
,DWORD fccType
,LONG lParam
);
72 struct IAVIFileVtbl iavift
= {
73 IAVIFile_fnQueryInterface
,
78 IAVIFile_fnCreateStream
,
82 IAVIFile_fnDeleteStream
85 static HRESULT WINAPI
IPersistFile_fnQueryInterface(IPersistFile
*iface
,REFIID refiid
,LPVOID
*obj
);
86 static ULONG WINAPI
IPersistFile_fnAddRef(IPersistFile
*iface
);
87 static ULONG WINAPI
IPersistFile_fnRelease(IPersistFile
*iface
);
88 static HRESULT WINAPI
IPersistFile_fnGetClassID(IPersistFile
*iface
,CLSID
*pClassID
);
89 static HRESULT WINAPI
IPersistFile_fnIsDirty(IPersistFile
*iface
);
90 static HRESULT WINAPI
IPersistFile_fnLoad(IPersistFile
*iface
,LPCOLESTR pszFileName
,DWORD dwMode
);
91 static HRESULT WINAPI
IPersistFile_fnSave(IPersistFile
*iface
,LPCOLESTR pszFileName
,BOOL fRemember
);
92 static HRESULT WINAPI
IPersistFile_fnSaveCompleted(IPersistFile
*iface
,LPCOLESTR pszFileName
);
93 static HRESULT WINAPI
IPersistFile_fnGetCurFile(IPersistFile
*iface
,LPOLESTR
*ppszFileName
);
95 struct IPersistFileVtbl ipersistft
= {
96 IPersistFile_fnQueryInterface
,
97 IPersistFile_fnAddRef
,
98 IPersistFile_fnRelease
,
99 IPersistFile_fnGetClassID
,
100 IPersistFile_fnIsDirty
,
103 IPersistFile_fnSaveCompleted
,
104 IPersistFile_fnGetCurFile
107 static HRESULT WINAPI
IAVIStream_fnQueryInterface(IAVIStream
*iface
,REFIID refiid
,LPVOID
*obj
);
108 static ULONG WINAPI
IAVIStream_fnAddRef(IAVIStream
*iface
);
109 static ULONG WINAPI
IAVIStream_fnRelease(IAVIStream
* iface
);
110 static HRESULT WINAPI
IAVIStream_fnCreate(IAVIStream
*iface
,LPARAM lParam1
,LPARAM lParam2
);
111 static HRESULT WINAPI
IAVIStream_fnInfo(IAVIStream
*iface
,AVISTREAMINFOW
*psi
,LONG size
);
112 static LONG WINAPI
IAVIStream_fnFindSample(IAVIStream
*iface
,LONG pos
,LONG flags
);
113 static HRESULT WINAPI
IAVIStream_fnReadFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG
*formatsize
);
114 static HRESULT WINAPI
IAVIStream_fnSetFormat(IAVIStream
*iface
,LONG pos
,LPVOID format
,LONG formatsize
);
115 static HRESULT WINAPI
IAVIStream_fnRead(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,LONG
*bytesread
,LONG
*samplesread
);
116 static HRESULT WINAPI
IAVIStream_fnWrite(IAVIStream
*iface
,LONG start
,LONG samples
,LPVOID buffer
,LONG buffersize
,DWORD flags
,LONG
*sampwritten
,LONG
*byteswritten
);
117 static HRESULT WINAPI
IAVIStream_fnDelete(IAVIStream
*iface
,LONG start
,LONG samples
);
118 static HRESULT WINAPI
IAVIStream_fnReadData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG
*lpread
);
119 static HRESULT WINAPI
IAVIStream_fnWriteData(IAVIStream
*iface
,DWORD fcc
,LPVOID lp
,LONG size
);
120 static HRESULT WINAPI
IAVIStream_fnSetInfo(IAVIStream
*iface
,AVISTREAMINFOW
*info
,LONG infolen
);
122 struct IAVIStreamVtbl iavist
= {
123 IAVIStream_fnQueryInterface
,
125 IAVIStream_fnRelease
,
128 IAVIStream_fnFindSample
,
129 IAVIStream_fnReadFormat
,
130 IAVIStream_fnSetFormat
,
134 IAVIStream_fnReadData
,
135 IAVIStream_fnWriteData
,
139 typedef struct _IAVIFileImpl IAVIFileImpl
;
141 typedef struct _IPersistFileImpl
{
143 IPersistFileVtbl
*lpVtbl
;
145 /* IPersistFile stuff */
149 typedef struct _IAVIStreamImpl
{
151 IAVIStreamVtbl
*lpVtbl
;
154 /* IAVIStream stuff */
156 DWORD nStream
; /* the n-th stream in file */
157 AVISTREAMINFOW sInfo
;
162 LPVOID lpHandlerData
;
168 DWORD cbBuffer
; /* size of lpBuffer */
169 DWORD dwCurrentFrame
; /* frame/block currently in lpBuffer */
171 LONG lLastFrame
; /* last correct index in idxFrames */
172 AVIINDEXENTRY
*idxFrames
;
173 DWORD nIdxFrames
; /* upper index limit of idxFrames */
174 AVIINDEXENTRY
*idxFmtChanges
;
175 DWORD nIdxFmtChanges
; /* upper index limit of idxFmtChanges */
178 struct _IAVIFileImpl
{
180 IAVIFileVtbl
*lpVtbl
;
183 /* IAVIFile stuff... */
184 IPersistFileImpl iPersistFile
;
187 IAVIStreamImpl
*ppStreams
[MAX_AVISTREAMS
];
189 EXTRACHUNKS fileextra
;
191 DWORD dwMoviChunkPos
; /* some stuff for saving ... */
193 DWORD dwNextFramePos
;
194 DWORD dwInitialFrames
;
196 MMCKINFO ckLastRecord
;
197 AVIINDEXENTRY
*idxRecords
; /* won't be updated while loading */
198 DWORD nIdxRecords
; /* current fill level */
199 DWORD cbIdxRecords
; /* size of idxRecords */
201 /* IPersistFile stuff ... */
208 /***********************************************************************/
210 static HRESULT
AVIFILE_AddFrame(IAVIStreamImpl
*This
, DWORD ckid
, DWORD size
,
211 DWORD offset
, DWORD flags
);
212 static HRESULT
AVIFILE_AddRecord(IAVIFileImpl
*This
);
213 static DWORD
AVIFILE_ComputeMoviStart(IAVIFileImpl
*This
);
214 static void AVIFILE_ConstructAVIStream(IAVIFileImpl
*paf
, DWORD nr
,
215 LPAVISTREAMINFOW asi
);
216 static void AVIFILE_DestructAVIStream(IAVIStreamImpl
*This
);
217 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
);
218 static HRESULT
AVIFILE_LoadIndex(IAVIFileImpl
*This
, DWORD size
, DWORD offset
);
219 static HRESULT
AVIFILE_ParseIndex(IAVIFileImpl
*This
, AVIINDEXENTRY
*lp
,
220 LONG count
, DWORD pos
, BOOL
*bAbsolute
);
221 static HRESULT
AVIFILE_ReadBlock(IAVIStreamImpl
*This
, DWORD start
,
222 LPVOID buffer
, LONG size
);
223 static void AVIFILE_SamplesToBlock(IAVIStreamImpl
*This
, LPLONG pos
,
225 static HRESULT
AVIFILE_SaveFile(IAVIFileImpl
*This
);
226 static HRESULT
AVIFILE_SaveIndex(IAVIFileImpl
*This
);
227 static ULONG
AVIFILE_SearchStream(IAVIFileImpl
*This
, DWORD fccType
,
229 static void AVIFILE_UpdateInfo(IAVIFileImpl
*This
);
230 static HRESULT
AVIFILE_WriteBlock(IAVIStreamImpl
*This
, DWORD block
,
231 FOURCC ckid
, DWORD flags
, LPVOID buffer
,
234 HRESULT
AVIFILE_CreateAVIFile(REFIID riid
, LPVOID
*ppv
)
239 assert(riid
!= NULL
&& ppv
!= NULL
);
243 pfile
= (IAVIFileImpl
*)LocalAlloc(LPTR
, sizeof(IAVIFileImpl
));
245 return AVIERR_MEMORY
;
247 pfile
->lpVtbl
= &iavift
;
249 pfile
->iPersistFile
.lpVtbl
= &ipersistft
;
250 pfile
->iPersistFile
.paf
= pfile
;
252 hr
= IUnknown_QueryInterface((IUnknown
*)pfile
, riid
, ppv
);
254 LocalFree((HLOCAL
)pfile
);
259 static HRESULT WINAPI
IAVIFile_fnQueryInterface(IAVIFile
*iface
, REFIID refiid
,
262 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
264 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(refiid
), obj
);
266 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
267 IsEqualGUID(&IID_IAVIFile
, refiid
)) {
269 IAVIFile_AddRef(iface
);
272 } else if (IsEqualGUID(&IID_IPersistFile
, refiid
)) {
273 *obj
= &This
->iPersistFile
;
274 IAVIFile_AddRef(iface
);
279 return OLE_E_ENUM_NOMORE
;
282 static ULONG WINAPI
IAVIFile_fnAddRef(IAVIFile
*iface
)
284 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
286 TRACE("(%p) -> %ld\n", iface
, This
->ref
+ 1);
287 return ++(This
->ref
);
290 static ULONG WINAPI
IAVIFile_fnRelease(IAVIFile
*iface
)
292 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
295 TRACE("(%p) -> %ld\n", iface
, This
->ref
- 1);
297 if (!--(This
->ref
)) {
299 /* need to write headers to file */
300 AVIFILE_SaveFile(This
);
303 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
304 if (This
->ppStreams
[i
] != NULL
) {
305 if (This
->ppStreams
[i
]->ref
!= 0) {
306 ERR(": someone has still %lu reference to stream %u (%p)!\n",
307 This
->ppStreams
[i
]->ref
, i
, This
->ppStreams
[i
]);
309 AVIFILE_DestructAVIStream(This
->ppStreams
[i
]);
310 LocalFree((HLOCAL
)This
->ppStreams
[i
]);
311 This
->ppStreams
[i
] = NULL
;
315 if (This
->idxRecords
!= NULL
) {
316 GlobalFreePtr(This
->idxRecords
);
317 This
->idxRecords
= NULL
;
318 This
->nIdxRecords
= 0;
321 if (This
->fileextra
.lp
!= NULL
) {
322 GlobalFreePtr(This
->fileextra
.lp
);
323 This
->fileextra
.lp
= NULL
;
324 This
->fileextra
.cb
= 0;
327 if (This
->szFileName
!= NULL
) {
328 LocalFree((HLOCAL
)This
->szFileName
);
329 This
->szFileName
= NULL
;
331 if (This
->hmmio
!= NULL
) {
332 mmioClose(This
->hmmio
, 0);
336 LocalFree((HLOCAL
)This
);
342 static HRESULT WINAPI
IAVIFile_fnInfo(IAVIFile
*iface
, LPAVIFILEINFOW afi
,
345 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
347 TRACE("(%p,%p,%ld)\n",iface
,afi
,size
);
350 return AVIERR_BADPARAM
;
352 return AVIERR_BADSIZE
;
354 AVIFILE_UpdateInfo(This
);
356 memcpy(afi
, &This
->fInfo
, min((DWORD
)size
, sizeof(This
->fInfo
)));
358 if ((DWORD
)size
< sizeof(This
->fInfo
))
359 return AVIERR_BUFFERTOOSMALL
;
363 static HRESULT WINAPI
IAVIFile_fnGetStream(IAVIFile
*iface
, PAVISTREAM
*avis
,
364 DWORD fccType
, LONG lParam
)
366 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
370 TRACE("(%p,%p,0x%08lX,%ld)\n", iface
, avis
, fccType
, lParam
);
372 if (avis
== NULL
|| lParam
< 0)
373 return AVIERR_BADPARAM
;
375 nStream
= AVIFILE_SearchStream(This
, fccType
, lParam
);
377 /* Does the requested stream exist? */
378 if (nStream
< This
->fInfo
.dwStreams
&&
379 This
->ppStreams
[nStream
] != NULL
) {
380 *avis
= (PAVISTREAM
)This
->ppStreams
[nStream
];
381 IAVIStream_AddRef(*avis
);
386 /* Sorry, but the specified stream doesn't exist */
387 return AVIERR_NODATA
;
390 static HRESULT WINAPI
IAVIFile_fnCreateStream(IAVIFile
*iface
,PAVISTREAM
*avis
,
391 LPAVISTREAMINFOW asi
)
393 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
397 TRACE("(%p,%p,%p)\n", iface
, avis
, asi
);
399 /* check parameters */
400 if (avis
== NULL
|| asi
== NULL
)
401 return AVIERR_BADPARAM
;
405 /* Does the user have write permission? */
406 if ((This
->uMode
& MMIO_RWMODE
) == 0)
407 return AVIERR_READONLY
;
409 /* Can we add another stream? */
410 n
= This
->fInfo
.dwStreams
;
411 if (n
>= MAX_AVISTREAMS
|| This
->dwMoviChunkPos
!= 0) {
412 /* already reached max nr of streams
413 * or have already written frames to disk */
414 return AVIERR_UNSUPPORTED
;
417 /* check AVISTREAMINFO for some really needed things */
418 if (asi
->fccType
== 0 || asi
->dwScale
== 0 || asi
->dwRate
== 0)
419 return AVIERR_BADFORMAT
;
421 /* now it seems to be save to add the stream */
422 assert(This
->ppStreams
[n
] == NULL
);
423 This
->ppStreams
[n
] = (IAVIStreamImpl
*)LocalAlloc(LPTR
,
424 sizeof(IAVIStreamImpl
));
425 if (This
->ppStreams
[n
] == NULL
)
426 return AVIERR_MEMORY
;
428 /* initialize the new allocated stream */
429 AVIFILE_ConstructAVIStream(This
, n
, asi
);
431 This
->fInfo
.dwStreams
++;
434 /* update our AVIFILEINFO structure */
435 AVIFILE_UpdateInfo(This
);
438 *avis
= (PAVISTREAM
)This
->ppStreams
[n
];
439 IAVIStream_AddRef(*avis
);
444 static HRESULT WINAPI
IAVIFile_fnWriteData(IAVIFile
*iface
, DWORD ckid
,
445 LPVOID lpData
, LONG size
)
447 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
449 TRACE("(%p,0x%08lX,%p,%ld)\n", iface
, ckid
, lpData
, size
);
451 /* check parameters */
453 return AVIERR_BADPARAM
;
455 return AVIERR_BADSIZE
;
457 /* Do we have write permission? */
458 if ((This
->uMode
& MMIO_RWMODE
) == 0)
459 return AVIERR_READONLY
;
463 return WriteExtraChunk(&This
->fileextra
, ckid
, lpData
, size
);
466 static HRESULT WINAPI
IAVIFile_fnReadData(IAVIFile
*iface
, DWORD ckid
,
467 LPVOID lpData
, LONG
*size
)
469 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
471 TRACE("(%p,0x%08lX,%p,%p)\n", iface
, ckid
, lpData
, size
);
473 return ReadExtraChunk(&This
->fileextra
, ckid
, lpData
, size
);
476 static HRESULT WINAPI
IAVIFile_fnEndRecord(IAVIFile
*iface
)
478 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
480 TRACE("(%p)\n",iface
);
482 if ((This
->uMode
& MMIO_RWMODE
) == 0)
483 return AVIERR_READONLY
;
487 /* no frames written to any stream? -- compute start of 'movi'-chunk */
488 if (This
->dwMoviChunkPos
== 0)
489 AVIFILE_ComputeMoviStart(This
);
491 This
->fInfo
.dwFlags
|= AVIFILEINFO_ISINTERLEAVED
;
493 /* already written frames to any stream, ... */
494 if (This
->ckLastRecord
.dwFlags
& MMIO_DIRTY
) {
495 /* close last record */
496 if (mmioAscend(This
->hmmio
, &This
->ckLastRecord
, 0) != 0)
497 return AVIERR_FILEWRITE
;
499 AVIFILE_AddRecord(This
);
501 if (This
->fInfo
.dwSuggestedBufferSize
< This
->ckLastRecord
.cksize
+ 3 * sizeof(DWORD
))
502 This
->fInfo
.dwSuggestedBufferSize
= This
->ckLastRecord
.cksize
+ 3 * sizeof(DWORD
);
505 /* write out a new record into file, but don't close it */
506 This
->ckLastRecord
.cksize
= 0;
507 This
->ckLastRecord
.fccType
= listtypeAVIRECORD
;
508 if (mmioSeek(This
->hmmio
, This
->dwNextFramePos
, SEEK_SET
) == -1)
509 return AVIERR_FILEWRITE
;
510 if (mmioCreateChunk(This
->hmmio
, &This
->ckLastRecord
, MMIO_CREATELIST
) != 0)
511 return AVIERR_FILEWRITE
;
512 This
->dwNextFramePos
+= 3 * sizeof(DWORD
);
517 static HRESULT WINAPI
IAVIFile_fnDeleteStream(IAVIFile
*iface
, DWORD fccType
,
520 IAVIFileImpl
*This
= (IAVIFileImpl
*)iface
;
524 TRACE("(%p,0x%08lX,%ld)\n", iface
, fccType
, lParam
);
526 /* check parameter */
528 return AVIERR_BADPARAM
;
530 /* Have user write permissions? */
531 if ((This
->uMode
& MMIO_RWMODE
) == 0)
532 return AVIERR_READONLY
;
534 nStream
= AVIFILE_SearchStream(This
, fccType
, lParam
);
536 /* Does the requested stream exist? */
537 if (nStream
< This
->fInfo
.dwStreams
&&
538 This
->ppStreams
[nStream
] != NULL
) {
539 /* ... so delete it now */
540 LocalFree((HLOCAL
)This
->ppStreams
[nStream
]);
542 if (This
->fInfo
.dwStreams
- nStream
> 0)
543 memcpy(This
->ppStreams
+ nStream
, This
->ppStreams
+ nStream
+ 1,
544 (This
->fInfo
.dwStreams
- nStream
) * sizeof(IAVIStreamImpl
*));
546 This
->ppStreams
[This
->fInfo
.dwStreams
] = NULL
;
547 This
->fInfo
.dwStreams
--;
550 /* This->fInfo will be updated further when asked for */
553 return AVIERR_NODATA
;
556 /***********************************************************************/
558 static HRESULT WINAPI
IPersistFile_fnQueryInterface(IPersistFile
*iface
,
559 REFIID refiid
, LPVOID
*obj
)
561 IPersistFileImpl
*This
= (IPersistFileImpl
*)iface
;
563 assert(This
->paf
!= NULL
);
565 return IAVIFile_QueryInterface((PAVIFILE
)This
->paf
, refiid
, obj
);
568 static ULONG WINAPI
IPersistFile_fnAddRef(IPersistFile
*iface
)
570 IPersistFileImpl
*This
= (IPersistFileImpl
*)iface
;
572 assert(This
->paf
!= NULL
);
574 return IAVIFile_AddRef((PAVIFILE
)This
->paf
);
577 static ULONG WINAPI
IPersistFile_fnRelease(IPersistFile
*iface
)
579 IPersistFileImpl
*This
= (IPersistFileImpl
*)iface
;
581 assert(This
->paf
!= NULL
);
583 return IAVIFile_Release((PAVIFILE
)This
->paf
);
586 static HRESULT WINAPI
IPersistFile_fnGetClassID(IPersistFile
*iface
,
589 TRACE("(%p,%p)\n", iface
, pClassID
);
591 if (pClassID
== NULL
)
592 return AVIERR_BADPARAM
;
594 memcpy(pClassID
, &CLSID_AVIFile
, sizeof(CLSID_AVIFile
));
599 static HRESULT WINAPI
IPersistFile_fnIsDirty(IPersistFile
*iface
)
601 IPersistFileImpl
*This
= (IPersistFileImpl
*)iface
;
603 TRACE("(%p)\n", iface
);
605 assert(This
->paf
!= NULL
);
607 return (This
->paf
->fDirty
? S_OK
: S_FALSE
);
610 static HRESULT WINAPI
IPersistFile_fnLoad(IPersistFile
*iface
,
611 LPCOLESTR pszFileName
, DWORD dwMode
)
613 IPersistFileImpl
*This
= (IPersistFileImpl
*)iface
;
617 TRACE("(%p,%s,0x%08lX)\n", iface
, debugstr_w(pszFileName
), dwMode
);
619 /* check parameter */
620 if (pszFileName
== NULL
)
621 return AVIERR_BADPARAM
;
623 assert(This
->paf
!= NULL
);
624 if (This
->paf
->hmmio
!= NULL
)
625 return AVIERR_ERROR
; /* No reuse of this object for another file! */
627 /* remeber mode and name */
628 This
->paf
->uMode
= dwMode
;
630 len
= lstrlenW(pszFileName
) + 1;
631 This
->paf
->szFileName
= (LPWSTR
)LocalAlloc(LPTR
, len
* sizeof(WCHAR
));
632 if (This
->paf
->szFileName
== NULL
)
633 return AVIERR_MEMORY
;
634 lstrcpyW(This
->paf
->szFileName
, pszFileName
);
636 /* try to open the file */
637 This
->paf
->hmmio
= mmioOpenW(This
->paf
->szFileName
, NULL
,
638 MMIO_ALLOCBUF
| dwMode
);
639 if (This
->paf
->hmmio
== NULL
) {
640 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
643 len
= WideCharToMultiByte(CP_ACP
, 0, This
->paf
->szFileName
, -1,
644 NULL
, 0, NULL
, NULL
);
645 szFileName
= LocalAlloc(LPTR
, len
* sizeof(CHAR
));
646 if (szFileName
== NULL
)
647 return AVIERR_MEMORY
;
649 WideCharToMultiByte(CP_ACP
, 0, This
->paf
->szFileName
, -1, szFileName
,
652 This
->paf
->hmmio
= mmioOpenA(szFileName
, NULL
, MMIO_ALLOCBUF
| dwMode
);
653 LocalFree((HLOCAL
)szFileName
);
654 if (This
->paf
->hmmio
== NULL
)
655 return AVIERR_FILEOPEN
;
658 /* should we create a new file? */
659 if (dwMode
& OF_CREATE
) {
660 memset(& This
->paf
->fInfo
, 0, sizeof(This
->paf
->fInfo
));
661 This
->paf
->fInfo
.dwFlags
= AVIFILEINFO_HASINDEX
| AVIFILEINFO_TRUSTCKTYPE
;
665 return AVIFILE_LoadFile(This
->paf
);
668 static HRESULT WINAPI
IPersistFile_fnSave(IPersistFile
*iface
,
669 LPCOLESTR pszFileName
,BOOL fRemember
)
671 TRACE("(%p,%s,%d)\n", iface
, debugstr_w(pszFileName
), fRemember
);
673 /* We write directly to disk, so nothing to do. */
678 static HRESULT WINAPI
IPersistFile_fnSaveCompleted(IPersistFile
*iface
,
679 LPCOLESTR pszFileName
)
681 TRACE("(%p,%s)\n", iface
, debugstr_w(pszFileName
));
683 /* We write directly to disk, so nothing to do. */
688 static HRESULT WINAPI
IPersistFile_fnGetCurFile(IPersistFile
*iface
,
689 LPOLESTR
*ppszFileName
)
691 IPersistFileImpl
*This
= (IPersistFileImpl
*)iface
;
693 TRACE("(%p,%p)\n", iface
, ppszFileName
);
695 if (ppszFileName
== NULL
)
696 return AVIERR_BADPARAM
;
698 *ppszFileName
= NULL
;
700 assert(This
->paf
!= NULL
);
702 if (This
->paf
->szFileName
!= NULL
) {
703 int len
= lstrlenW(This
->paf
->szFileName
) + 1;
705 *ppszFileName
= (LPOLESTR
)GlobalAllocPtr(GHND
, len
* sizeof(WCHAR
));
706 if (*ppszFileName
== NULL
)
707 return AVIERR_MEMORY
;
709 strcpyW(*ppszFileName
, This
->paf
->szFileName
);
715 /***********************************************************************/
717 static HRESULT WINAPI
IAVIStream_fnQueryInterface(IAVIStream
*iface
,
718 REFIID refiid
, LPVOID
*obj
)
720 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
722 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(refiid
), obj
);
724 if (IsEqualGUID(&IID_IUnknown
, refiid
) ||
725 IsEqualGUID(&IID_IAVIStream
, refiid
)) {
727 IAVIStream_AddRef(iface
);
731 /* FIXME: IAVIStreaming interface */
733 return OLE_E_ENUM_NOMORE
;
736 static ULONG WINAPI
IAVIStream_fnAddRef(IAVIStream
*iface
)
738 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
740 TRACE("(%p) -> %ld\n", iface
, This
->ref
+ 1);
742 /* also add ref to parent, so that it doesn't kill us */
743 if (This
->paf
!= NULL
)
744 IAVIFile_AddRef((PAVIFILE
)This
->paf
);
746 return ++(This
->ref
);
749 static ULONG WINAPI
IAVIStream_fnRelease(IAVIStream
* iface
)
751 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
753 TRACE("(%p) -> %ld\n", iface
, This
->ref
- 1);
755 /* we belong to the AVIFile, which must free us! */
756 if (This
->ref
== 0) {
757 ERR(": already released!\n");
763 if (This
->paf
!= NULL
)
764 IAVIFile_Release((PAVIFILE
)This
->paf
);
769 static HRESULT WINAPI
IAVIStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
772 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
774 /* This IAVIStream interface needs an AVIFile */
775 return AVIERR_UNSUPPORTED
;
778 static HRESULT WINAPI
IAVIStream_fnInfo(IAVIStream
*iface
,LPAVISTREAMINFOW psi
,
781 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
783 TRACE("(%p,%p,%ld)\n", iface
, psi
, size
);
786 return AVIERR_BADPARAM
;
788 return AVIERR_BADSIZE
;
790 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
792 if ((DWORD
)size
< sizeof(This
->sInfo
))
793 return AVIERR_BUFFERTOOSMALL
;
797 static LONG WINAPI
IAVIStream_fnFindSample(IAVIStream
*iface
, LONG pos
,
800 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
804 TRACE("(%p,%ld,0x%08lX)\n",iface
,pos
,flags
);
806 if (flags
& FIND_FROM_START
) {
807 pos
= This
->sInfo
.dwStart
;
808 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
812 if (This
->sInfo
.dwSampleSize
!= 0) {
813 /* convert samples into block number with offset */
814 AVIFILE_SamplesToBlock(This
, &pos
, &offset
);
817 if (flags
& FIND_TYPE
) {
818 if (flags
& FIND_KEY
) {
819 while (0 <= pos
&& pos
<= This
->lLastFrame
) {
820 if (This
->idxFrames
[pos
].dwFlags
& AVIIF_KEYFRAME
)
823 if (flags
& FIND_NEXT
)
828 } else if (flags
& FIND_ANY
) {
829 while (0 <= pos
&& pos
<= This
->lLastFrame
) {
830 if (This
->idxFrames
[pos
].dwChunkLength
> 0)
833 if (flags
& FIND_NEXT
)
839 } else if ((flags
& FIND_FORMAT
) && This
->idxFmtChanges
!= NULL
&&
840 This
->sInfo
.fccType
== streamtypeVIDEO
) {
841 if (flags
& FIND_NEXT
) {
844 for (n
= 0; n
< This
->sInfo
.dwFormatChangeCount
; n
++)
845 if (This
->idxFmtChanges
[n
].ckid
>= pos
) {
846 pos
= This
->idxFmtChanges
[n
].ckid
;
852 for (n
= (LONG
)This
->sInfo
.dwFormatChangeCount
; n
>= 0; n
--) {
853 if (This
->idxFmtChanges
[n
].ckid
<= pos
) {
854 pos
= This
->idxFmtChanges
[n
].ckid
;
859 if (pos
> (LONG
)This
->sInfo
.dwStart
)
860 return 0; /* format changes always for first frame */
868 if (pos
< (LONG
)This
->sInfo
.dwStart
)
871 switch (flags
& FIND_RET
) {
874 pos
= This
->idxFrames
[pos
].dwChunkLength
;
877 /* physical position */
878 pos
= This
->idxFrames
[pos
].dwChunkOffset
+ 2 * sizeof(DWORD
)
879 + offset
* This
->sInfo
.dwSampleSize
;
883 if (This
->sInfo
.dwSampleSize
)
884 pos
= This
->sInfo
.dwSampleSize
;
889 FIXME(": FIND_INDEX flag is not supported!\n");
890 /* This is an index in the index-table on disc. */
892 }; /* else logical position */
897 static HRESULT WINAPI
IAVIStream_fnReadFormat(IAVIStream
*iface
, LONG pos
,
898 LPVOID format
, LONG
*formatsize
)
900 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
902 TRACE("(%p,%ld,%p,%p)\n", iface
, pos
, format
, formatsize
);
904 if (formatsize
== NULL
)
905 return AVIERR_BADPARAM
;
907 /* only interested in needed buffersize? */
908 if (format
== NULL
|| *formatsize
<= 0) {
909 *formatsize
= This
->cbFormat
;
914 /* copy initial format (only as much as will fit) */
915 memcpy(format
, This
->lpFormat
, min(*(DWORD
*)formatsize
, This
->cbFormat
));
916 if (*(DWORD
*)formatsize
< This
->cbFormat
) {
917 *formatsize
= This
->cbFormat
;
918 return AVIERR_BUFFERTOOSMALL
;
921 /* Could format change? When yes will it change? */
922 if ((This
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
923 pos
> This
->sInfo
.dwStart
) {
926 lLastFmt
= IAVIStream_fnFindSample(iface
, pos
, FIND_FORMAT
|FIND_PREV
);
928 FIXME(": need to read formatchange for %ld -- unimplemented!\n",lLastFmt
);
932 *formatsize
= This
->cbFormat
;
936 static HRESULT WINAPI
IAVIStream_fnSetFormat(IAVIStream
*iface
, LONG pos
,
937 LPVOID format
, LONG formatsize
)
939 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
941 LPBITMAPINFOHEADER lpbiNew
= (LPBITMAPINFOHEADER
)format
;
943 TRACE("(%p,%ld,%p,%ld)\n", iface
, pos
, format
, formatsize
);
945 /* check parameters */
946 if (format
== NULL
|| formatsize
<= 0)
947 return AVIERR_BADPARAM
;
949 /* Do we have write permission? */
950 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
951 return AVIERR_READONLY
;
953 /* can only set format before frame is written! */
954 if (This
->lLastFrame
> pos
)
955 return AVIERR_UNSUPPORTED
;
957 /* initial format or a formatchange? */
958 if (This
->lpFormat
== NULL
) {
960 if (This
->paf
->dwMoviChunkPos
!= 0)
961 return AVIERR_ERROR
; /* user has used API in wrong sequnece! */
963 This
->lpFormat
= GlobalAllocPtr(GMEM_MOVEABLE
, formatsize
);
964 if (This
->lpFormat
== NULL
)
965 return AVIERR_MEMORY
;
966 This
->cbFormat
= formatsize
;
968 memcpy(This
->lpFormat
, format
, formatsize
);
970 /* update some infos about stream */
971 if (This
->sInfo
.fccType
== streamtypeVIDEO
) {
974 lDim
= This
->sInfo
.rcFrame
.right
- This
->sInfo
.rcFrame
.left
;
975 if (lDim
< lpbiNew
->biWidth
)
976 This
->sInfo
.rcFrame
.right
= This
->sInfo
.rcFrame
.left
+ lpbiNew
->biWidth
;
977 lDim
= This
->sInfo
.rcFrame
.bottom
- This
->sInfo
.rcFrame
.top
;
978 if (lDim
< lpbiNew
->biHeight
)
979 This
->sInfo
.rcFrame
.bottom
= This
->sInfo
.rcFrame
.top
+ lpbiNew
->biHeight
;
980 } else if (This
->sInfo
.fccType
== streamtypeAUDIO
)
981 This
->sInfo
.dwSampleSize
= ((LPWAVEFORMATEX
)This
->lpFormat
)->nBlockAlign
;
986 LPBITMAPINFOHEADER lpbiOld
= (LPBITMAPINFOHEADER
)This
->lpFormat
;
987 RGBQUAD
*rgbNew
= (RGBQUAD
*)((LPBYTE
)lpbiNew
+ lpbiNew
->biSize
);
988 AVIPALCHANGE
*lppc
= NULL
;
991 /* perhaps format change, check it ... */
992 if (This
->cbFormat
!= formatsize
)
993 return AVIERR_UNSUPPORTED
;
995 /* no format change, only the initial one */
996 if (memcmp(This
->lpFormat
, format
, formatsize
) == 0)
999 /* check that's only the palette, which changes */
1000 if (lpbiOld
->biSize
!= lpbiNew
->biSize
||
1001 lpbiOld
->biWidth
!= lpbiNew
->biWidth
||
1002 lpbiOld
->biHeight
!= lpbiNew
->biHeight
||
1003 lpbiOld
->biPlanes
!= lpbiNew
->biPlanes
||
1004 lpbiOld
->biBitCount
!= lpbiNew
->biBitCount
||
1005 lpbiOld
->biCompression
!= lpbiNew
->biCompression
||
1006 lpbiOld
->biClrUsed
!= lpbiNew
->biClrUsed
)
1007 return AVIERR_UNSUPPORTED
;
1009 This
->sInfo
.dwFlags
|= AVISTREAMINFO_FORMATCHANGES
;
1011 /* simply say all colors have changed */
1012 ck
.ckid
= MAKEAVICKID(cktypePALchange
, This
->nStream
);
1013 ck
.cksize
= 2 * sizeof(WORD
) + lpbiOld
->biClrUsed
* sizeof(PALETTEENTRY
);
1014 lppc
= (AVIPALCHANGE
*)GlobalAllocPtr(GMEM_MOVEABLE
, ck
.cksize
);
1016 return AVIERR_MEMORY
;
1018 lppc
->bFirstEntry
= 0;
1019 lppc
->bNumEntries
= (lpbiOld
->biClrUsed
< 256 ? lpbiOld
->biClrUsed
: 0);
1021 for (n
= 0; n
< lpbiOld
->biClrUsed
; n
++) {
1022 lppc
->peNew
[n
].peRed
= rgbNew
[n
].rgbRed
;
1023 lppc
->peNew
[n
].peGreen
= rgbNew
[n
].rgbGreen
;
1024 lppc
->peNew
[n
].peBlue
= rgbNew
[n
].rgbBlue
;
1025 lppc
->peNew
[n
].peFlags
= 0;
1028 if (mmioSeek(This
->paf
->hmmio
, This
->paf
->dwNextFramePos
, SEEK_SET
) == -1)
1029 return AVIERR_FILEWRITE
;
1030 if (mmioCreateChunk(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
1031 return AVIERR_FILEWRITE
;
1032 if (mmioWrite(This
->paf
->hmmio
, (HPSTR
)lppc
, ck
.cksize
) != ck
.cksize
)
1033 return AVIERR_FILEWRITE
;
1034 if (mmioAscend(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
1035 return AVIERR_FILEWRITE
;
1036 This
->paf
->dwNextFramePos
+= ck
.cksize
+ 2 * sizeof(DWORD
);
1038 GlobalFreePtr(lppc
);
1040 return AVIFILE_AddFrame(This
, cktypePALchange
, n
, ck
.dwDataOffset
, 0);
1044 static HRESULT WINAPI
IAVIStream_fnRead(IAVIStream
*iface
, LONG start
,
1045 LONG samples
, LPVOID buffer
,
1046 LONG buffersize
, LPLONG bytesread
,
1049 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
1054 TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface
, start
, samples
, buffer
,
1055 buffersize
, bytesread
, samplesread
);
1057 /* clear return parameters if given */
1058 if (bytesread
!= NULL
)
1060 if (samplesread
!= NULL
)
1063 /* check parameters */
1064 if ((LONG
)This
->sInfo
.dwStart
> start
)
1065 return AVIERR_NODATA
; /* couldn't read before start of stream */
1066 if (This
->sInfo
.dwStart
+ This
->sInfo
.dwLength
< (DWORD
)start
)
1067 return AVIERR_NODATA
; /* start is past end of stream */
1069 /* should we read as much as possible? */
1070 if (samples
== -1) {
1071 /* User should know how much we have read */
1072 if (bytesread
== NULL
&& samplesread
== NULL
)
1073 return AVIERR_BADPARAM
;
1075 if (This
->sInfo
.dwSampleSize
!= 0)
1076 samples
= buffersize
/ This
->sInfo
.dwSampleSize
;
1081 /* limit to end of stream */
1082 if ((LONG
)This
->sInfo
.dwLength
< samples
)
1083 samples
= This
->sInfo
.dwLength
;
1084 if ((start
- This
->sInfo
.dwStart
) > (This
->sInfo
.dwLength
- samples
))
1085 samples
= This
->sInfo
.dwLength
- (start
- This
->sInfo
.dwStart
);
1087 /* nothing to read? Then leave ... */
1091 if (This
->sInfo
.dwSampleSize
!= 0) {
1092 /* fixed samplesize -- we can read over frame/block boundaries */
1093 ULONG block
= start
;
1096 /* convert start sample to block,offset pair */
1097 AVIFILE_SamplesToBlock(This
, &block
, &offset
);
1099 /* convert samples to bytes */
1100 samples
*= This
->sInfo
.dwSampleSize
;
1102 while (samples
> 0 && buffersize
> 0) {
1103 if (block
!= This
->dwCurrentFrame
) {
1104 hr
= AVIFILE_ReadBlock(This
, block
, NULL
, 0);
1109 size
= min((DWORD
)samples
, (DWORD
)buffersize
);
1110 size
= min(size
, This
->cbBuffer
- offset
);
1111 memcpy(buffer
, ((BYTE
*)&This
->lpBuffer
[2]) + offset
, size
);
1115 buffer
= ((LPBYTE
)buffer
)+size
;
1119 /* fill out return parameters if given */
1120 if (bytesread
!= NULL
)
1122 if (samplesread
!= NULL
)
1123 *samplesread
+= size
/ This
->sInfo
.dwSampleSize
;
1129 return AVIERR_BUFFERTOOSMALL
;
1131 /* variable samplesize -- we can only read one full frame/block */
1135 assert(start
<= This
->lLastFrame
);
1136 size
= This
->idxFrames
[start
].dwChunkLength
;
1137 if (buffer
!= NULL
&& buffersize
>= size
) {
1138 hr
= AVIFILE_ReadBlock(This
, start
, buffer
, size
);
1141 } else if (buffer
!= NULL
)
1142 return AVIERR_BUFFERTOOSMALL
;
1144 /* fill out return parameters if given */
1145 if (bytesread
!= NULL
)
1147 if (samplesread
!= NULL
)
1148 *samplesread
= samples
;
1154 static HRESULT WINAPI
IAVIStream_fnWrite(IAVIStream
*iface
, LONG start
,
1155 LONG samples
, LPVOID buffer
,
1156 LONG buffersize
, DWORD flags
,
1158 LPLONG byteswritten
)
1160 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
1165 TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface
, start
, samples
,
1166 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
1168 /* clear return parameters if given */
1169 if (sampwritten
!= NULL
)
1171 if (byteswritten
!= NULL
)
1174 /* check parameters */
1175 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
1176 return AVIERR_BADPARAM
;
1178 /* Have we write permission? */
1179 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1180 return AVIERR_READONLY
;
1182 switch (This
->sInfo
.fccType
) {
1183 case streamtypeAUDIO
:
1184 ckid
= MAKEAVICKID(cktypeWAVEbytes
, This
->nStream
);
1187 if ((flags
& AVIIF_KEYFRAME
) && buffersize
!= 0)
1188 ckid
= MAKEAVICKID(cktypeDIBbits
, This
->nStream
);
1190 ckid
= MAKEAVICKID(cktypeDIBcompressed
, This
->nStream
);
1194 /* append to end of stream? */
1196 if (This
->lLastFrame
== -1)
1197 start
= This
->sInfo
.dwStart
;
1199 start
= This
->sInfo
.dwLength
;
1200 } else if (This
->lLastFrame
== -1)
1201 This
->sInfo
.dwStart
= start
;
1203 if (This
->sInfo
.dwSampleSize
!= 0) {
1204 /* fixed sample size -- audio like */
1205 if (samples
* This
->sInfo
.dwSampleSize
!= buffersize
)
1206 return AVIERR_BADPARAM
;
1208 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1209 if (This
->sInfo
.dwLength
!= start
)
1210 return AVIERR_UNSUPPORTED
;
1212 /* Convert position to frame/block */
1213 start
= This
->lLastFrame
+ 1;
1215 if ((This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) == 0) {
1216 FIXME(": not interleaved, could collect audio data!\n");
1219 /* variable sample size -- video like */
1221 return AVIERR_UNSUPPORTED
;
1223 /* must we fill up with empty frames? */
1224 if (This
->lLastFrame
!= -1) {
1225 FOURCC ckid2
= MAKEAVICKID(cktypeDIBcompressed
, This
->nStream
);
1227 while (start
> This
->lLastFrame
+ 1) {
1228 hr
= AVIFILE_WriteBlock(This
, This
->lLastFrame
+ 1, ckid2
, 0, NULL
, 0);
1235 /* write the block now */
1236 hr
= AVIFILE_WriteBlock(This
, start
, ckid
, flags
, buffer
, buffersize
);
1237 if (SUCCEEDED(hr
)) {
1238 /* fill out return parameters if given */
1239 if (sampwritten
!= NULL
)
1240 *sampwritten
= samples
;
1241 if (byteswritten
!= NULL
)
1242 *byteswritten
= buffersize
;
1248 static HRESULT WINAPI
IAVIStream_fnDelete(IAVIStream
*iface
, LONG start
,
1251 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
1253 FIXME("(%p,%ld,%ld): stub\n", iface
, start
, samples
);
1255 /* check parameters */
1256 if (start
< 0 || samples
< 0)
1257 return AVIERR_BADPARAM
;
1259 /* Delete before start of stream? */
1260 if (start
+ samples
< This
->sInfo
.dwStart
)
1263 /* Delete after end of stream? */
1264 if (start
> This
->sInfo
.dwLength
)
1267 /* For the rest we need write permissions */
1268 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1269 return AVIERR_READONLY
;
1271 /* 1. overwrite the data with JUNK
1273 * if ISINTERLEAVED {
1274 * 2. concat all neighboured JUNK-blocks in this record to one
1275 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1276 * to start of this record, repeat this.
1278 * 2. concat all neighboured JUNK-blocks.
1279 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1280 * start of this block.
1284 return AVIERR_UNSUPPORTED
;
1287 static HRESULT WINAPI
IAVIStream_fnReadData(IAVIStream
*iface
, DWORD fcc
,
1288 LPVOID lp
, LPLONG lpread
)
1290 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
1292 TRACE("(%p,0x%08lX,%p,%p)\n", iface
, fcc
, lp
, lpread
);
1294 if (fcc
== ckidSTREAMHANDLERDATA
) {
1295 if (This
->lpHandlerData
!= NULL
&& This
->cbHandlerData
> 0) {
1296 if (lp
== NULL
|| *lpread
<= 0) {
1297 *lpread
= This
->cbHandlerData
;
1301 memcpy(lp
, This
->lpHandlerData
, min(This
->cbHandlerData
, *lpread
));
1302 if (*lpread
< This
->cbHandlerData
)
1303 return AVIERR_BUFFERTOOSMALL
;
1306 return AVIERR_NODATA
;
1308 return ReadExtraChunk(&This
->extra
, fcc
, lp
, lpread
);
1311 static HRESULT WINAPI
IAVIStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
,
1312 LPVOID lp
, LONG size
)
1314 IAVIStreamImpl
*This
= (IAVIStreamImpl
*)iface
;
1316 TRACE("(%p,0x%08lx,%p,%ld)\n", iface
, fcc
, lp
, size
);
1318 /* check parameters */
1320 return AVIERR_BADPARAM
;
1322 return AVIERR_BADSIZE
;
1324 /* need write permission */
1325 if ((This
->paf
->uMode
& MMIO_RWMODE
) == 0)
1326 return AVIERR_READONLY
;
1328 /* already written something to this file? */
1329 if (This
->paf
->dwMoviChunkPos
!= 0) {
1330 /* the data will be inserted before the 'movi' chunk, so check for
1332 DWORD dwPos
= AVIFILE_ComputeMoviStart(This
->paf
);
1334 /* ckid,size => 2 * sizeof(DWORD) */
1335 dwPos
+= 2 * sizeof(DWORD
) + size
;
1336 if (size
>= This
->paf
->dwMoviChunkPos
- 2 * sizeof(DWORD
))
1337 return AVIERR_UNSUPPORTED
; /* not enough space left */
1340 This
->paf
->fDirty
= TRUE
;
1342 if (fcc
== ckidSTREAMHANDLERDATA
) {
1343 if (This
->lpHandlerData
!= NULL
) {
1344 FIXME(": handler data already set -- overwirte?\n");
1345 return AVIERR_UNSUPPORTED
;
1348 This
->lpHandlerData
= GlobalAllocPtr(GMEM_MOVEABLE
, size
);
1349 if (This
->lpHandlerData
== NULL
)
1350 return AVIERR_MEMORY
;
1351 This
->cbHandlerData
= size
;
1352 memcpy(This
->lpHandlerData
, lp
, size
);
1356 return WriteExtraChunk(&This
->extra
, fcc
, lp
, size
);
1359 static HRESULT WINAPI
IAVIStream_fnSetInfo(IAVIStream
*iface
,
1360 LPAVISTREAMINFOW info
, LONG infolen
)
1362 FIXME("(%p,%p,%ld): stub\n", iface
, info
, infolen
);
1367 /***********************************************************************/
1369 static HRESULT
AVIFILE_AddFrame(IAVIStreamImpl
*This
, DWORD ckid
, DWORD size
, DWORD offset
, DWORD flags
)
1371 /* pre-conditions */
1372 assert(This
!= NULL
);
1374 switch (TWOCCFromFOURCC(ckid
)) {
1376 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1377 flags
|= AVIIF_KEYFRAME
;
1379 case cktypeDIBcompressed
:
1380 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1381 flags
&= ~AVIIF_KEYFRAME
;
1383 case cktypePALchange
:
1384 if (This
->sInfo
.fccType
!= streamtypeVIDEO
) {
1385 ERR(": found palette change in non-video stream!\n");
1386 return AVIERR_BADFORMAT
;
1388 This
->sInfo
.dwFlags
|= AVISTREAMINFO_FORMATCHANGES
;
1389 This
->sInfo
.dwFormatChangeCount
++;
1391 if (This
->idxFmtChanges
== NULL
|| This
->sInfo
.dwFormatChangeCount
< This
->nIdxFmtChanges
) {
1392 UINT n
= This
->sInfo
.dwFormatChangeCount
;
1394 This
->nIdxFmtChanges
+= 16;
1395 if (This
->idxFmtChanges
== NULL
)
1396 This
->idxFmtChanges
=
1397 GlobalAllocPtr(GHND
, This
->nIdxFmtChanges
* sizeof(AVIINDEXENTRY
));
1399 This
->idxFmtChanges
=
1400 GlobalReAllocPtr(This
->idxFmtChanges
,
1401 This
->nIdxFmtChanges
* sizeof(AVIINDEXENTRY
), GHND
);
1402 if (This
->idxFmtChanges
== NULL
)
1403 return AVIERR_MEMORY
;
1405 This
->idxFmtChanges
[n
].ckid
= This
->lLastFrame
;
1406 This
->idxFmtChanges
[n
].dwFlags
= 0;
1407 This
->idxFmtChanges
[n
].dwChunkOffset
= offset
;
1408 This
->idxFmtChanges
[n
].dwChunkLength
= size
;
1413 case cktypeWAVEbytes
:
1414 if (This
->paf
->fInfo
.dwFlags
& AVIFILEINFO_TRUSTCKTYPE
)
1415 flags
|= AVIIF_KEYFRAME
;
1418 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid
));
1422 /* first frame is alwasy a keyframe */
1423 if (This
->lLastFrame
== -1)
1424 flags
|= AVIIF_KEYFRAME
;
1426 if (This
->sInfo
.dwSuggestedBufferSize
< size
)
1427 This
->sInfo
.dwSuggestedBufferSize
= size
;
1429 /* get memory for index */
1430 if (This
->idxFrames
== NULL
|| This
->lLastFrame
+ 1 >= This
->nIdxFrames
) {
1431 This
->nIdxFrames
+= 512;
1432 if (This
->idxFrames
== NULL
)
1434 GlobalAllocPtr(GHND
, This
->nIdxFrames
* sizeof(AVIINDEXENTRY
));
1437 GlobalReAllocPtr(This
->idxFrames
,
1438 This
->nIdxFrames
* sizeof(AVIINDEXENTRY
), GHND
);
1439 if (This
->idxFrames
== NULL
)
1440 return AVIERR_MEMORY
;
1444 This
->idxFrames
[This
->lLastFrame
].ckid
= ckid
;
1445 This
->idxFrames
[This
->lLastFrame
].dwFlags
= flags
;
1446 This
->idxFrames
[This
->lLastFrame
].dwChunkOffset
= offset
;
1447 This
->idxFrames
[This
->lLastFrame
].dwChunkLength
= size
;
1449 /* update AVISTREAMINFO structure if necessary */
1450 if (This
->sInfo
.dwLength
<= This
->lLastFrame
)
1451 This
->sInfo
.dwLength
= This
->lLastFrame
+ 1;
1456 static HRESULT
AVIFILE_AddRecord(IAVIFileImpl
*This
)
1458 /* pre-conditions */
1459 assert(This
!= NULL
&& This
->ppStreams
[0] != NULL
);
1461 if (This
->idxRecords
== NULL
|| This
->cbIdxRecords
== 0) {
1462 This
->cbIdxRecords
+= 1024 * sizeof(AVIINDEXENTRY
);
1463 This
->idxRecords
= GlobalAllocPtr(GHND
, This
->cbIdxRecords
);
1464 if (This
->idxRecords
== NULL
)
1465 return AVIERR_MEMORY
;
1468 assert(This
->nIdxRecords
< This
->cbIdxRecords
/sizeof(AVIINDEXENTRY
));
1470 This
->idxRecords
[This
->nIdxRecords
].ckid
= listtypeAVIRECORD
;
1471 This
->idxRecords
[This
->nIdxRecords
].dwFlags
= AVIIF_LIST
;
1472 This
->idxRecords
[This
->nIdxRecords
].dwChunkOffset
=
1473 This
->ckLastRecord
.dwDataOffset
- 2 * sizeof(DWORD
);
1474 This
->idxRecords
[This
->nIdxRecords
].dwChunkLength
=
1475 This
->ckLastRecord
.cksize
;
1476 This
->nIdxRecords
++;
1481 static DWORD
AVIFILE_ComputeMoviStart(IAVIFileImpl
*This
)
1486 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1487 dwPos
= 11 * sizeof(DWORD
) + sizeof(MainAVIHeader
);
1489 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
1490 IAVIStreamImpl
*pStream
= This
->ppStreams
[nStream
];
1492 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1493 dwPos
+= 7 * sizeof(DWORD
) + sizeof(AVIStreamHeader
);
1494 dwPos
+= ((pStream
->cbFormat
+ 1) & ~1U);
1495 if (pStream
->lpHandlerData
!= NULL
&& pStream
->cbHandlerData
> 0)
1496 dwPos
+= 2 * sizeof(DWORD
) + ((pStream
->cbHandlerData
+ 1) & ~1U);
1497 if (lstrlenW(pStream
->sInfo
.szName
) > 0)
1498 dwPos
+= 2 * sizeof(DWORD
) + ((lstrlenW(pStream
->sInfo
.szName
) + 1) & ~1U);
1501 if (This
->dwMoviChunkPos
== 0) {
1502 This
->dwNextFramePos
= dwPos
;
1504 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1505 if (((dwPos
+ AVI_HEADERSIZE
) & ~(AVI_HEADERSIZE
- 1)) - dwPos
> 2 * sizeof(DWORD
))
1506 This
->dwNextFramePos
= (dwPos
+ AVI_HEADERSIZE
) & ~(AVI_HEADERSIZE
- 1);
1508 This
->dwMoviChunkPos
= This
->dwNextFramePos
- sizeof(DWORD
);
1514 static void AVIFILE_ConstructAVIStream(IAVIFileImpl
*paf
, DWORD nr
, LPAVISTREAMINFOW asi
)
1516 IAVIStreamImpl
*pstream
;
1518 /* pre-conditions */
1519 assert(paf
!= NULL
);
1520 assert(nr
< MAX_AVISTREAMS
);
1521 assert(paf
->ppStreams
[nr
] != NULL
);
1523 pstream
= paf
->ppStreams
[nr
];
1525 pstream
->lpVtbl
= &iavist
;
1528 pstream
->nStream
= nr
;
1529 pstream
->dwCurrentFrame
= (DWORD
)-1;
1530 pstream
->lLastFrame
= -1;
1533 memcpy(&pstream
->sInfo
, asi
, sizeof(pstream
->sInfo
));
1535 if (asi
->dwLength
> 0) {
1536 /* pre-allocate mem for frame-index structure */
1537 pstream
->idxFrames
=
1538 (AVIINDEXENTRY
*)GlobalAllocPtr(GHND
, asi
->dwLength
* sizeof(AVIINDEXENTRY
));
1539 if (pstream
->idxFrames
!= NULL
)
1540 pstream
->nIdxFrames
= asi
->dwLength
;
1542 if (asi
->dwFormatChangeCount
> 0) {
1543 /* pre-allocate mem for formatchange-index structure */
1544 pstream
->idxFmtChanges
=
1545 (AVIINDEXENTRY
*)GlobalAllocPtr(GHND
, asi
->dwFormatChangeCount
* sizeof(AVIINDEXENTRY
));
1546 if (pstream
->idxFmtChanges
!= NULL
)
1547 pstream
->nIdxFmtChanges
= asi
->dwFormatChangeCount
;
1550 /* These values will be computed */
1551 pstream
->sInfo
.dwLength
= 0;
1552 pstream
->sInfo
.dwSuggestedBufferSize
= 0;
1553 pstream
->sInfo
.dwFormatChangeCount
= 0;
1554 pstream
->sInfo
.dwEditCount
= 1;
1555 if (pstream
->sInfo
.dwSampleSize
> 0)
1556 SetRectEmpty(&pstream
->sInfo
.rcFrame
);
1559 pstream
->sInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
1562 static void AVIFILE_DestructAVIStream(IAVIStreamImpl
*This
)
1564 /* pre-conditions */
1565 assert(This
!= NULL
);
1567 This
->dwCurrentFrame
= (DWORD
)-1;
1568 This
->lLastFrame
= -1;
1570 if (This
->idxFrames
!= NULL
) {
1571 GlobalFreePtr(This
->idxFrames
);
1572 This
->idxFrames
= NULL
;
1573 This
->nIdxFrames
= 0;
1575 if (This
->idxFmtChanges
!= NULL
) {
1576 GlobalFreePtr(This
->idxFmtChanges
);
1577 This
->idxFmtChanges
= NULL
;
1579 if (This
->lpBuffer
!= NULL
) {
1580 GlobalFreePtr(This
->lpBuffer
);
1581 This
->lpBuffer
= NULL
;
1584 if (This
->lpHandlerData
!= NULL
) {
1585 GlobalFreePtr(This
->lpHandlerData
);
1586 This
->lpHandlerData
= NULL
;
1587 This
->cbHandlerData
= 0;
1589 if (This
->extra
.lp
!= NULL
) {
1590 GlobalFreePtr(This
->extra
.lp
);
1591 This
->extra
.lp
= NULL
;
1594 if (This
->lpFormat
!= NULL
) {
1595 GlobalFreePtr(This
->lpFormat
);
1596 This
->lpFormat
= NULL
;
1601 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
)
1603 MainAVIHeader MainAVIHdr
;
1608 IAVIStreamImpl
*pStream
;
1612 if (This
->hmmio
== NULL
)
1613 return AVIERR_FILEOPEN
;
1615 /* initialize stream ptr's */
1616 memset(This
->ppStreams
, 0, sizeof(This
->ppStreams
));
1618 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1619 ckRIFF
.fccType
= formtypeAVI
;
1620 if (mmioDescend(This
->hmmio
, &ckRIFF
, NULL
, MMIO_FINDRIFF
) != S_OK
) {
1621 ERR(": not an AVI!\n");
1622 return AVIERR_FILEREAD
;
1625 /* get "LIST" "hdrl" */
1626 ckLIST1
.fccType
= listtypeAVIHEADER
;
1627 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ckLIST1
, &ckRIFF
, MMIO_FINDLIST
);
1631 /* get "avih" chunk */
1632 ck
.ckid
= ckidAVIMAINHDR
;
1633 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckLIST1
, MMIO_FINDCHUNK
);
1637 if (ck
.cksize
!= sizeof(MainAVIHdr
)) {
1638 ERR(": invalid size of %ld for MainAVIHeader!\n", ck
.cksize
);
1639 return AVIERR_BADFORMAT
;
1641 if (mmioRead(This
->hmmio
, (HPSTR
)&MainAVIHdr
, ck
.cksize
) != ck
.cksize
)
1642 return AVIERR_FILEREAD
;
1644 /* check for MAX_AVISTREAMS limit */
1645 if (MainAVIHdr
.dwStreams
> MAX_AVISTREAMS
) {
1646 WARN("file contains %lu streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr
.dwStreams
, MAX_AVISTREAMS
);
1647 return AVIERR_UNSUPPORTED
;
1650 /* adjust permissions if copyrighted material in file */
1651 if (MainAVIHdr
.dwFlags
& AVIFILEINFO_COPYRIGHTED
) {
1652 This
->uMode
&= ~MMIO_RWMODE
;
1653 This
->uMode
|= MMIO_READ
;
1656 /* convert MainAVIHeader into AVIFILINFOW */
1657 memset(&This
->fInfo
, 0, sizeof(This
->fInfo
));
1658 This
->fInfo
.dwRate
= MainAVIHdr
.dwMicroSecPerFrame
;
1659 This
->fInfo
.dwScale
= 1000000;
1660 This
->fInfo
.dwMaxBytesPerSec
= MainAVIHdr
.dwMaxBytesPerSec
;
1661 This
->fInfo
.dwFlags
= MainAVIHdr
.dwFlags
;
1662 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
1663 This
->fInfo
.dwLength
= MainAVIHdr
.dwTotalFrames
;
1664 This
->fInfo
.dwStreams
= MainAVIHdr
.dwStreams
;
1665 This
->fInfo
.dwSuggestedBufferSize
= MainAVIHdr
.dwSuggestedBufferSize
;
1666 This
->fInfo
.dwWidth
= MainAVIHdr
.dwWidth
;
1667 This
->fInfo
.dwHeight
= MainAVIHdr
.dwHeight
;
1668 LoadStringW(AVIFILE_hModule
, IDS_AVIFILETYPE
, This
->fInfo
.szFileType
,
1669 sizeof(This
->fInfo
.szFileType
));
1671 /* go back to into header list */
1672 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1673 return AVIERR_FILEREAD
;
1675 /* foreach stream exists a "LIST","strl" chunk */
1676 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
1677 /* get next nested chunk in this "LIST","strl" */
1678 if (mmioDescend(This
->hmmio
, &ckLIST2
, &ckLIST1
, 0) != S_OK
)
1679 return AVIERR_FILEREAD
;
1681 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1682 if (ckLIST2
.ckid
== FOURCC_LIST
&&
1683 ckLIST2
.fccType
== listtypeSTREAMHEADER
) {
1684 pStream
= This
->ppStreams
[nStream
] =
1685 (IAVIStreamImpl
*)LocalAlloc(LPTR
, sizeof(IAVIStreamImpl
));
1686 if (pStream
== NULL
)
1687 return AVIERR_MEMORY
;
1688 AVIFILE_ConstructAVIStream(This
, nStream
, NULL
);
1691 while (mmioDescend(This
->hmmio
, &ck
, &ckLIST2
, 0) == S_OK
) {
1693 case ckidSTREAMHANDLERDATA
:
1694 if (pStream
->lpHandlerData
!= NULL
)
1695 return AVIERR_BADFORMAT
;
1696 pStream
->lpHandlerData
= GlobalAllocPtr(GMEM_DDESHARE
|GMEM_MOVEABLE
,
1698 if (pStream
->lpHandlerData
== NULL
)
1699 return AVIERR_MEMORY
;
1700 pStream
->cbHandlerData
= ck
.cksize
;
1702 if (mmioRead(This
->hmmio
, (HPSTR
)pStream
->lpHandlerData
, ck
.cksize
) != ck
.cksize
)
1703 return AVIERR_FILEREAD
;
1705 case ckidSTREAMFORMAT
:
1706 if (pStream
->lpFormat
!= NULL
)
1707 return AVIERR_BADFORMAT
;
1711 pStream
->lpFormat
= GlobalAllocPtr(GMEM_DDESHARE
|GMEM_MOVEABLE
,
1713 if (pStream
->lpFormat
== NULL
)
1714 return AVIERR_MEMORY
;
1715 pStream
->cbFormat
= ck
.cksize
;
1717 if (mmioRead(This
->hmmio
, (HPSTR
)pStream
->lpFormat
, ck
.cksize
) != ck
.cksize
)
1718 return AVIERR_FILEREAD
;
1720 if (pStream
->sInfo
.fccType
== streamtypeVIDEO
) {
1721 LPBITMAPINFOHEADER lpbi
= (LPBITMAPINFOHEADER
)pStream
->lpFormat
;
1723 /* some corrections to the video format */
1724 if (lpbi
->biClrUsed
== 0 && lpbi
->biBitCount
<= 8)
1725 lpbi
->biClrUsed
= 1u << lpbi
->biBitCount
;
1726 if (lpbi
->biCompression
== BI_RGB
&& lpbi
->biSizeImage
== 0)
1727 lpbi
->biSizeImage
= DIBWIDTHBYTES(*lpbi
) * lpbi
->biHeight
;
1728 if (lpbi
->biCompression
!= BI_RGB
&& lpbi
->biBitCount
== 8) {
1729 if (pStream
->sInfo
.fccHandler
== mmioFOURCC('R','L','E','0') ||
1730 pStream
->sInfo
.fccHandler
== mmioFOURCC('R','L','E',' '))
1731 lpbi
->biCompression
= BI_RLE8
;
1733 if (lpbi
->biCompression
== BI_RGB
&&
1734 (pStream
->sInfo
.fccHandler
== 0 ||
1735 pStream
->sInfo
.fccHandler
== mmioFOURCC('N','O','N','E')))
1736 pStream
->sInfo
.fccHandler
= comptypeDIB
;
1738 /* init rcFrame if it's empty */
1739 if (IsRectEmpty(&pStream
->sInfo
.rcFrame
))
1740 SetRect(&pStream
->sInfo
.rcFrame
, 0, 0, lpbi
->biWidth
, lpbi
->biHeight
);
1743 case ckidSTREAMHEADER
:
1745 static const WCHAR streamTypeFmt
[] = {'%','4','.','4','h','s',0};
1747 AVIStreamHeader streamHdr
;
1749 WCHAR streamNameFmt
[25];
1753 if (ck
.cksize
> sizeof(streamHdr
))
1754 n
= sizeof(streamHdr
);
1756 if (mmioRead(This
->hmmio
, (HPSTR
)&streamHdr
, n
) != n
)
1757 return AVIERR_FILEREAD
;
1759 pStream
->sInfo
.fccType
= streamHdr
.fccType
;
1760 pStream
->sInfo
.fccHandler
= streamHdr
.fccHandler
;
1761 pStream
->sInfo
.dwFlags
= streamHdr
.dwFlags
;
1762 pStream
->sInfo
.wPriority
= streamHdr
.wPriority
;
1763 pStream
->sInfo
.wLanguage
= streamHdr
.wLanguage
;
1764 pStream
->sInfo
.dwInitialFrames
= streamHdr
.dwInitialFrames
;
1765 pStream
->sInfo
.dwScale
= streamHdr
.dwScale
;
1766 pStream
->sInfo
.dwRate
= streamHdr
.dwRate
;
1767 pStream
->sInfo
.dwStart
= streamHdr
.dwStart
;
1768 pStream
->sInfo
.dwLength
= streamHdr
.dwLength
;
1769 pStream
->sInfo
.dwSuggestedBufferSize
=
1770 streamHdr
.dwSuggestedBufferSize
;
1771 pStream
->sInfo
.dwQuality
= streamHdr
.dwQuality
;
1772 pStream
->sInfo
.dwSampleSize
= streamHdr
.dwSampleSize
;
1773 pStream
->sInfo
.rcFrame
.left
= streamHdr
.rcFrame
.left
;
1774 pStream
->sInfo
.rcFrame
.top
= streamHdr
.rcFrame
.top
;
1775 pStream
->sInfo
.rcFrame
.right
= streamHdr
.rcFrame
.right
;
1776 pStream
->sInfo
.rcFrame
.bottom
= streamHdr
.rcFrame
.bottom
;
1777 pStream
->sInfo
.dwEditCount
= 0;
1778 pStream
->sInfo
.dwFormatChangeCount
= 0;
1780 /* generate description for stream like "filename.avi Type #n" */
1781 if (streamHdr
.fccType
== streamtypeVIDEO
)
1782 LoadStringW(AVIFILE_hModule
, IDS_VIDEO
, szType
, sizeof(szType
));
1783 else if (streamHdr
.fccType
== streamtypeAUDIO
)
1784 LoadStringW(AVIFILE_hModule
, IDS_AUDIO
, szType
, sizeof(szType
));
1786 wsprintfW(szType
, streamTypeFmt
, (char*)&streamHdr
.fccType
);
1788 /* get count of this streamtype up to this stream */
1790 for (n
= nStream
; 0 <= n
; n
--) {
1791 if (This
->ppStreams
[n
]->sInfo
.fccHandler
== streamHdr
.fccType
)
1795 memset(pStream
->sInfo
.szName
, 0, sizeof(pStream
->sInfo
.szName
));
1797 LoadStringW(AVIFILE_hModule
, IDS_AVISTREAMFORMAT
, streamNameFmt
, sizeof(streamNameFmt
));
1799 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1800 wsprintfW(pStream
->sInfo
.szName
, streamNameFmt
,
1801 AVIFILE_BasenameW(This
->szFileName
), szType
, count
);
1804 case ckidSTREAMNAME
:
1805 { /* streamname will be saved as ASCII string */
1806 LPSTR str
= (LPSTR
)LocalAlloc(LMEM_FIXED
, ck
.cksize
);
1808 return AVIERR_MEMORY
;
1810 if (mmioRead(This
->hmmio
, (HPSTR
)str
, ck
.cksize
) != ck
.cksize
)
1811 return AVIERR_FILEREAD
;
1813 MultiByteToWideChar(CP_ACP
, 0, str
, -1, pStream
->sInfo
.szName
,
1814 sizeof(pStream
->sInfo
.szName
)/sizeof(pStream
->sInfo
.szName
[0]));
1816 LocalFree((HLOCAL
)str
);
1819 case ckidAVIPADDING
:
1820 case mmioFOURCC('p','a','d','d'):
1823 WARN(": found extra chunk 0x%08lX\n", ck
.ckid
);
1824 hr
= ReadChunkIntoExtra(&pStream
->extra
, This
->hmmio
, &ck
);
1829 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1830 return AVIERR_FILEREAD
;
1833 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1834 hr
= ReadChunkIntoExtra(&This
->fileextra
, This
->hmmio
, &ckLIST2
);
1838 if (mmioAscend(This
->hmmio
, &ckLIST2
, 0) != S_OK
)
1839 return AVIERR_FILEREAD
;
1842 /* read any extra headers in "LIST","hdrl" */
1843 FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckLIST1
, 0);
1844 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
1845 return AVIERR_FILEREAD
;
1847 /* search "LIST","movi" chunk in "RIFF","AVI " */
1848 ckLIST1
.fccType
= listtypeAVIMOVIE
;
1849 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ckLIST1
, &ckRIFF
,
1854 This
->dwMoviChunkPos
= ckLIST1
.dwDataOffset
;
1855 This
->dwIdxChunkPos
= ckLIST1
.cksize
+ ckLIST1
.dwDataOffset
;
1856 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
1857 return AVIERR_FILEREAD
;
1859 /* try to find an index */
1860 ck
.ckid
= ckidAVINEWINDEX
;
1861 hr
= FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
,
1862 &ck
, &ckRIFF
, MMIO_FINDCHUNK
);
1863 if (SUCCEEDED(hr
) && ck
.cksize
> 0) {
1864 if (FAILED(AVIFILE_LoadIndex(This
, ck
.cksize
, ckLIST1
.dwDataOffset
)))
1865 This
->fInfo
.dwFlags
&= ~AVIFILEINFO_HASINDEX
;
1868 /* when we haven't found an index or it's bad, then build one
1869 * by parsing 'movi' chunk */
1870 if ((This
->fInfo
.dwFlags
& AVIFILEINFO_HASINDEX
) == 0) {
1871 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++)
1872 This
->ppStreams
[nStream
]->lLastFrame
= -1;
1874 if (mmioSeek(This
->hmmio
, ckLIST1
.dwDataOffset
+ sizeof(DWORD
), SEEK_SET
) == -1) {
1875 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1876 return AVIERR_FILEREAD
;
1879 /* seek through the 'movi' list until end */
1880 while (mmioDescend(This
->hmmio
, &ck
, &ckLIST1
, 0) == S_OK
) {
1881 if (ck
.ckid
!= FOURCC_LIST
) {
1882 if (mmioAscend(This
->hmmio
, &ck
, 0) == S_OK
) {
1883 nStream
= StreamFromFOURCC(ck
.ckid
);
1885 if (nStream
> This
->fInfo
.dwStreams
)
1886 return AVIERR_BADFORMAT
;
1888 AVIFILE_AddFrame(This
->ppStreams
[nStream
], ck
.ckid
, ck
.cksize
,
1889 ck
.dwDataOffset
- 2 * sizeof(DWORD
), 0);
1891 nStream
= StreamFromFOURCC(ck
.ckid
);
1892 WARN(": file seems to be truncated!\n");
1893 if (nStream
<= This
->fInfo
.dwStreams
&&
1894 This
->ppStreams
[nStream
]->sInfo
.dwSampleSize
> 0) {
1895 ck
.cksize
= mmioSeek(This
->hmmio
, 0, SEEK_END
);
1896 if (ck
.cksize
!= -1) {
1897 ck
.cksize
-= ck
.dwDataOffset
;
1898 ck
.cksize
&= ~(This
->ppStreams
[nStream
]->sInfo
.dwSampleSize
- 1);
1900 AVIFILE_AddFrame(This
->ppStreams
[nStream
], ck
.ckid
, ck
.cksize
,
1901 ck
.dwDataOffset
- 2 * sizeof(DWORD
), 0);
1909 /* find other chunks */
1910 FindChunkAndKeepExtras(&This
->fileextra
, This
->hmmio
, &ck
, &ckRIFF
, 0);
1915 static HRESULT
AVIFILE_LoadIndex(IAVIFileImpl
*This
, DWORD size
, DWORD offset
)
1919 HRESULT hr
= AVIERR_OK
;
1920 BOOL bAbsolute
= TRUE
;
1922 lp
= (AVIINDEXENTRY
*)GlobalAllocPtr(GMEM_MOVEABLE
,
1923 IDX_PER_BLOCK
* sizeof(AVIINDEXENTRY
));
1925 return AVIERR_MEMORY
;
1927 /* adjust limits for index tables, so that inserting will be faster */
1928 for (n
= 0; n
< This
->fInfo
.dwStreams
; n
++) {
1929 IAVIStreamImpl
*pStream
= This
->ppStreams
[n
];
1931 pStream
->lLastFrame
= -1;
1933 if (pStream
->idxFrames
!= NULL
) {
1934 GlobalFreePtr(pStream
->idxFrames
);
1935 pStream
->idxFrames
= NULL
;
1936 pStream
->nIdxFrames
= 0;
1939 if (pStream
->sInfo
.dwSampleSize
!= 0) {
1940 if (n
> 0 && This
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) {
1941 pStream
->nIdxFrames
= This
->ppStreams
[0]->nIdxFrames
;
1942 } else if (pStream
->sInfo
.dwSuggestedBufferSize
) {
1943 pStream
->nIdxFrames
=
1944 pStream
->sInfo
.dwLength
/ pStream
->sInfo
.dwSuggestedBufferSize
;
1947 pStream
->nIdxFrames
= pStream
->sInfo
.dwLength
;
1949 pStream
->idxFrames
=
1950 (AVIINDEXENTRY
*)GlobalAllocPtr(GHND
, pStream
->nIdxFrames
* sizeof(AVIINDEXENTRY
));
1951 if (pStream
->idxFrames
== NULL
&& pStream
->nIdxFrames
> 0) {
1952 pStream
->nIdxFrames
= 0;
1953 return AVIERR_MEMORY
;
1959 LONG read
= min(IDX_PER_BLOCK
* sizeof(AVIINDEXENTRY
), size
);
1961 if (mmioRead(This
->hmmio
, (HPSTR
)lp
, read
) != read
) {
1962 hr
= AVIERR_FILEREAD
;
1967 if (pos
== (DWORD
)-1)
1968 pos
= offset
- lp
->dwChunkOffset
+ sizeof(DWORD
);
1970 AVIFILE_ParseIndex(This
, lp
, read
/ sizeof(AVIINDEXENTRY
),
1978 for (n
= 0; n
< This
->fInfo
.dwStreams
; n
++) {
1979 IAVIStreamImpl
*pStream
= This
->ppStreams
[n
];
1981 if (pStream
->sInfo
.dwSampleSize
== 0 &&
1982 pStream
->sInfo
.dwLength
!= pStream
->lLastFrame
+1)
1983 ERR("stream %lu length mismatch: dwLength=%lu found=%ld\n",
1984 n
, pStream
->sInfo
.dwLength
, pStream
->lLastFrame
);
1990 static HRESULT
AVIFILE_ParseIndex(IAVIFileImpl
*This
, AVIINDEXENTRY
*lp
,
1991 LONG count
, DWORD pos
, BOOL
*bAbsolute
)
1994 return AVIERR_BADPARAM
;
1996 for (; count
> 0; count
--, lp
++) {
1997 WORD nStream
= StreamFromFOURCC(lp
->ckid
);
1999 if (lp
->ckid
== listtypeAVIRECORD
|| nStream
== 0x7F)
2000 continue; /* skip these */
2002 if (nStream
> This
->fInfo
.dwStreams
)
2003 return AVIERR_BADFORMAT
;
2005 if (*bAbsolute
== TRUE
&& lp
->dwChunkOffset
< This
->dwMoviChunkPos
)
2009 lp
->dwChunkOffset
+= sizeof(DWORD
);
2011 lp
->dwChunkOffset
+= pos
;
2013 if (FAILED(AVIFILE_AddFrame(This
->ppStreams
[nStream
], lp
->ckid
, lp
->dwChunkLength
, lp
->dwChunkOffset
, lp
->dwFlags
)))
2014 return AVIERR_MEMORY
;
2020 static HRESULT
AVIFILE_ReadBlock(IAVIStreamImpl
*This
, DWORD pos
,
2021 LPVOID buffer
, LONG size
)
2023 /* pre-conditions */
2024 assert(This
!= NULL
);
2025 assert(This
->paf
!= NULL
);
2026 assert(This
->paf
->hmmio
!= NULL
);
2027 assert(This
->sInfo
.dwStart
<= pos
&& pos
< This
->sInfo
.dwLength
);
2028 assert(pos
<= This
->lLastFrame
);
2030 /* should we read as much as block gives us? */
2031 if (size
== 0 || size
> This
->idxFrames
[pos
].dwChunkLength
)
2032 size
= This
->idxFrames
[pos
].dwChunkLength
;
2034 /* read into out own buffer or given one? */
2035 if (buffer
== NULL
) {
2036 /* we also read the chunk */
2037 size
+= 2 * sizeof(DWORD
);
2039 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2040 if (This
->lpBuffer
== NULL
|| size
< This
->cbBuffer
) {
2041 DWORD maxSize
= max(size
, This
->sInfo
.dwSuggestedBufferSize
);
2043 if (This
->lpBuffer
== NULL
)
2044 This
->lpBuffer
= (LPDWORD
)GlobalAllocPtr(GMEM_MOVEABLE
, maxSize
);
2047 (LPDWORD
)GlobalReAllocPtr(This
->lpBuffer
, maxSize
, GMEM_MOVEABLE
);
2048 if (This
->lpBuffer
== NULL
)
2049 return AVIERR_MEMORY
;
2050 This
->cbBuffer
= max(size
, This
->sInfo
.dwSuggestedBufferSize
);
2053 /* now read the complete chunk into our buffer */
2054 if (mmioSeek(This
->paf
->hmmio
, This
->idxFrames
[pos
].dwChunkOffset
, SEEK_SET
) == -1)
2055 return AVIERR_FILEREAD
;
2056 if (mmioRead(This
->paf
->hmmio
, (HPSTR
)This
->lpBuffer
, size
) != size
)
2057 return AVIERR_FILEREAD
;
2059 /* check if it was the correct block which we have read */
2060 if (This
->lpBuffer
[0] != This
->idxFrames
[pos
].ckid
||
2061 This
->lpBuffer
[1] != This
->idxFrames
[pos
].dwChunkLength
) {
2062 ERR(": block %ld not found at 0x%08lX\n", pos
, This
->idxFrames
[pos
].dwChunkOffset
);
2063 ERR(": Index says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2064 (char*)&This
->idxFrames
[pos
].ckid
, This
->idxFrames
[pos
].ckid
,
2065 This
->idxFrames
[pos
].dwChunkLength
);
2066 ERR(": Data says: '%4.4s'(0x%08lX) size 0x%08lX\n",
2067 (char*)&This
->lpBuffer
[0], This
->lpBuffer
[0], This
->lpBuffer
[1]);
2068 return AVIERR_FILEREAD
;
2071 if (mmioSeek(This
->paf
->hmmio
, This
->idxFrames
[pos
].dwChunkOffset
+ 2 * sizeof(DWORD
), SEEK_SET
) == -1)
2072 return AVIERR_FILEREAD
;
2073 if (mmioRead(This
->paf
->hmmio
, (HPSTR
)buffer
, size
) != size
)
2074 return AVIERR_FILEREAD
;
2080 static void AVIFILE_SamplesToBlock(IAVIStreamImpl
*This
, LPLONG pos
,
2085 /* pre-conditions */
2086 assert(This
!= NULL
);
2087 assert(pos
!= NULL
);
2088 assert(offset
!= NULL
);
2089 assert(This
->sInfo
.dwSampleSize
!= 0);
2090 assert(*pos
>= This
->sInfo
.dwStart
);
2092 /* convert start sample to start bytes */
2093 (*offset
) = (*pos
) - This
->sInfo
.dwStart
;
2094 (*offset
) *= This
->sInfo
.dwSampleSize
;
2096 /* convert bytes to block number */
2097 for (block
= 0; block
<= This
->lLastFrame
; block
++) {
2098 if (This
->idxFrames
[block
].dwChunkLength
<= *offset
)
2099 (*offset
) -= This
->idxFrames
[block
].dwChunkLength
;
2107 static HRESULT
AVIFILE_SaveFile(IAVIFileImpl
*This
)
2109 MainAVIHeader MainAVIHdr
;
2110 IAVIStreamImpl
* pStream
;
2119 /* initialize some things */
2120 if (This
->dwMoviChunkPos
== 0)
2121 AVIFILE_ComputeMoviStart(This
);
2123 /* written one record to much? */
2124 if (This
->ckLastRecord
.dwFlags
& MMIO_DIRTY
) {
2125 This
->dwNextFramePos
-= 3 * sizeof(DWORD
);
2126 if (This
->nIdxRecords
> 0)
2127 This
->nIdxRecords
--;
2130 AVIFILE_UpdateInfo(This
);
2132 assert(This
->fInfo
.dwScale
!= 0);
2134 memset(&MainAVIHdr
, 0, sizeof(MainAVIHdr
));
2135 MainAVIHdr
.dwMicroSecPerFrame
= MulDiv(This
->fInfo
.dwRate
, 1000000,
2136 This
->fInfo
.dwScale
);
2137 MainAVIHdr
.dwMaxBytesPerSec
= This
->fInfo
.dwMaxBytesPerSec
;
2138 MainAVIHdr
.dwPaddingGranularity
= AVI_HEADERSIZE
;
2139 MainAVIHdr
.dwFlags
= This
->fInfo
.dwFlags
;
2140 MainAVIHdr
.dwTotalFrames
= This
->fInfo
.dwLength
;
2141 MainAVIHdr
.dwInitialFrames
= 0;
2142 MainAVIHdr
.dwStreams
= This
->fInfo
.dwStreams
;
2143 MainAVIHdr
.dwSuggestedBufferSize
= This
->fInfo
.dwSuggestedBufferSize
;
2144 MainAVIHdr
.dwWidth
= This
->fInfo
.dwWidth
;
2145 MainAVIHdr
.dwHeight
= This
->fInfo
.dwHeight
;
2146 MainAVIHdr
.dwInitialFrames
= This
->dwInitialFrames
;
2148 /* now begin writing ... */
2149 mmioSeek(This
->hmmio
, 0, SEEK_SET
);
2153 ckRIFF
.fccType
= formtypeAVI
;
2154 if (mmioCreateChunk(This
->hmmio
, &ckRIFF
, MMIO_CREATERIFF
) != S_OK
)
2155 return AVIERR_FILEWRITE
;
2157 /* AVI headerlist */
2159 ckLIST1
.fccType
= listtypeAVIHEADER
;
2160 if (mmioCreateChunk(This
->hmmio
, &ckLIST1
, MMIO_CREATELIST
) != S_OK
)
2161 return AVIERR_FILEWRITE
;
2164 ck
.ckid
= ckidAVIMAINHDR
;
2165 ck
.cksize
= sizeof(MainAVIHdr
);
2167 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2168 return AVIERR_FILEWRITE
;
2169 if (mmioWrite(This
->hmmio
, (HPSTR
)&MainAVIHdr
, ck
.cksize
) != ck
.cksize
)
2170 return AVIERR_FILEWRITE
;
2171 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2172 return AVIERR_FILEWRITE
;
2174 /* write the headers of each stream into a separate streamheader list */
2175 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2176 AVIStreamHeader strHdr
;
2178 pStream
= This
->ppStreams
[nStream
];
2180 /* begin the new streamheader list */
2182 ckLIST2
.fccType
= listtypeSTREAMHEADER
;
2183 if (mmioCreateChunk(This
->hmmio
, &ckLIST2
, MMIO_CREATELIST
) != S_OK
)
2184 return AVIERR_FILEWRITE
;
2186 /* create an AVIStreamHeader from the AVSTREAMINFO */
2187 strHdr
.fccType
= pStream
->sInfo
.fccType
;
2188 strHdr
.fccHandler
= pStream
->sInfo
.fccHandler
;
2189 strHdr
.dwFlags
= pStream
->sInfo
.dwFlags
;
2190 strHdr
.wPriority
= pStream
->sInfo
.wPriority
;
2191 strHdr
.wLanguage
= pStream
->sInfo
.wLanguage
;
2192 strHdr
.dwInitialFrames
= pStream
->sInfo
.dwInitialFrames
;
2193 strHdr
.dwScale
= pStream
->sInfo
.dwScale
;
2194 strHdr
.dwRate
= pStream
->sInfo
.dwRate
;
2195 strHdr
.dwStart
= pStream
->sInfo
.dwStart
;
2196 strHdr
.dwLength
= pStream
->sInfo
.dwLength
;
2197 strHdr
.dwSuggestedBufferSize
= pStream
->sInfo
.dwSuggestedBufferSize
;
2198 strHdr
.dwQuality
= pStream
->sInfo
.dwQuality
;
2199 strHdr
.dwSampleSize
= pStream
->sInfo
.dwSampleSize
;
2200 strHdr
.rcFrame
.left
= pStream
->sInfo
.rcFrame
.left
;
2201 strHdr
.rcFrame
.top
= pStream
->sInfo
.rcFrame
.top
;
2202 strHdr
.rcFrame
.right
= pStream
->sInfo
.rcFrame
.right
;
2203 strHdr
.rcFrame
.bottom
= pStream
->sInfo
.rcFrame
.bottom
;
2205 /* now write the AVIStreamHeader */
2206 ck
.ckid
= ckidSTREAMHEADER
;
2207 ck
.cksize
= sizeof(strHdr
);
2208 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2209 return AVIERR_FILEWRITE
;
2210 if (mmioWrite(This
->hmmio
, (HPSTR
)&strHdr
, ck
.cksize
) != ck
.cksize
)
2211 return AVIERR_FILEWRITE
;
2212 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2213 return AVIERR_FILEWRITE
;
2215 /* ... the hopefully ever present streamformat ... */
2216 ck
.ckid
= ckidSTREAMFORMAT
;
2217 ck
.cksize
= pStream
->cbFormat
;
2218 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2219 return AVIERR_FILEWRITE
;
2220 if (pStream
->lpFormat
!= NULL
&& ck
.cksize
> 0) {
2221 if (mmioWrite(This
->hmmio
, (HPSTR
)pStream
->lpFormat
, ck
.cksize
) != ck
.cksize
)
2222 return AVIERR_FILEWRITE
;
2224 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2225 return AVIERR_FILEWRITE
;
2227 /* ... some optional existing handler data ... */
2228 if (pStream
->lpHandlerData
!= NULL
&& pStream
->cbHandlerData
> 0) {
2229 ck
.ckid
= ckidSTREAMHANDLERDATA
;
2230 ck
.cksize
= pStream
->cbHandlerData
;
2231 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2232 return AVIERR_FILEWRITE
;
2233 if (mmioWrite(This
->hmmio
, (HPSTR
)pStream
->lpHandlerData
, ck
.cksize
) != ck
.cksize
)
2234 return AVIERR_FILEWRITE
;
2235 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2236 return AVIERR_FILEWRITE
;
2239 /* ... some optional additional extra chunk for this stream ... */
2240 if (pStream
->extra
.lp
!= NULL
&& pStream
->extra
.cb
> 0) {
2241 /* the chunk header(s) are already in the strucuture */
2242 if (mmioWrite(This
->hmmio
, (HPSTR
)pStream
->extra
.lp
, pStream
->extra
.cb
) != pStream
->extra
.cb
)
2243 return AVIERR_FILEWRITE
;
2246 /* ... an optional name for this stream ... */
2247 if (lstrlenW(pStream
->sInfo
.szName
) > 0) {
2250 ck
.ckid
= ckidSTREAMNAME
;
2251 ck
.cksize
= lstrlenW(pStream
->sInfo
.szName
) + 1;
2252 if (ck
.cksize
& 1) /* align */
2254 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2255 return AVIERR_FILEWRITE
;
2257 /* the streamname must be saved in ASCII not Unicode */
2258 str
= (LPSTR
)LocalAlloc(LPTR
, ck
.cksize
);
2260 return AVIERR_MEMORY
;
2261 WideCharToMultiByte(CP_ACP
, 0, pStream
->sInfo
.szName
, -1, str
,
2262 ck
.cksize
, NULL
, NULL
);
2264 if (mmioWrite(This
->hmmio
, (HPSTR
)str
, ck
.cksize
) != ck
.cksize
) {
2265 LocalFree((HLOCAL
)str
);
2266 return AVIERR_FILEWRITE
;
2269 LocalFree((HLOCAL
)str
);
2270 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2271 return AVIERR_FILEWRITE
;
2274 /* close streamheader list for this stream */
2275 if (mmioAscend(This
->hmmio
, &ckLIST2
, 0) != S_OK
)
2276 return AVIERR_FILEWRITE
;
2277 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2279 /* close the aviheader list */
2280 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
2281 return AVIERR_FILEWRITE
;
2283 /* check for padding to pre-guessed 'movi'-chunk position */
2284 dwPos
= ckLIST1
.dwDataOffset
+ ckLIST1
.cksize
;
2285 if (This
->dwMoviChunkPos
- 2 * sizeof(DWORD
) > dwPos
) {
2286 ck
.ckid
= ckidAVIPADDING
;
2287 ck
.cksize
= This
->dwMoviChunkPos
- dwPos
- 4 * sizeof(DWORD
);
2288 assert((LONG
)ck
.cksize
>= 0);
2290 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2291 return AVIERR_FILEWRITE
;
2292 if (mmioSeek(This
->hmmio
, ck
.cksize
, SEEK_CUR
) == -1)
2293 return AVIERR_FILEWRITE
;
2294 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2295 return AVIERR_FILEWRITE
;
2298 /* now write the 'movi' chunk */
2299 mmioSeek(This
->hmmio
, This
->dwMoviChunkPos
- 2 * sizeof(DWORD
), SEEK_SET
);
2301 ckLIST1
.fccType
= listtypeAVIMOVIE
;
2302 if (mmioCreateChunk(This
->hmmio
, &ckLIST1
, MMIO_CREATELIST
) != S_OK
)
2303 return AVIERR_FILEWRITE
;
2304 if (mmioSeek(This
->hmmio
, This
->dwNextFramePos
, SEEK_SET
) == -1)
2305 return AVIERR_FILEWRITE
;
2306 if (mmioAscend(This
->hmmio
, &ckLIST1
, 0) != S_OK
)
2307 return AVIERR_FILEWRITE
;
2309 /* write 'idx1' chunk */
2310 hr
= AVIFILE_SaveIndex(This
);
2314 /* write optional extra file chunks */
2315 if (This
->fileextra
.lp
!= NULL
&& This
->fileextra
.cb
> 0) {
2316 /* as for the streams, are the chunk header(s) in the structure */
2317 if (mmioWrite(This
->hmmio
, (HPSTR
)This
->fileextra
.lp
, This
->fileextra
.cb
) != This
->fileextra
.cb
)
2318 return AVIERR_FILEWRITE
;
2321 /* close RIFF chunk */
2322 if (mmioAscend(This
->hmmio
, &ckRIFF
, 0) != S_OK
)
2323 return AVIERR_FILEWRITE
;
2325 /* add some JUNK at end for bad parsers */
2326 memset(&ckRIFF
, 0, sizeof(ckRIFF
));
2327 mmioWrite(This
->hmmio
, (HPSTR
)&ckRIFF
, sizeof(ckRIFF
));
2328 mmioFlush(This
->hmmio
, 0);
2333 static HRESULT
AVIFILE_SaveIndex(IAVIFileImpl
*This
)
2335 IAVIStreamImpl
*pStream
;
2341 ck
.ckid
= ckidAVINEWINDEX
;
2343 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
2344 return AVIERR_FILEWRITE
;
2346 if (This
->fInfo
.dwFlags
& AVIFILEINFO_ISINTERLEAVED
) {
2347 /* is interleaved -- write block of coresponding frames */
2348 LONG lInitialFrames
= 0;
2352 if (This
->ppStreams
[0]->sInfo
.dwSampleSize
== 0)
2355 stepsize
= AVIStreamTimeToSample((PAVISTREAM
)This
->ppStreams
[0], 1000000);
2357 assert(stepsize
> 0);
2359 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2360 if (lInitialFrames
< This
->ppStreams
[nStream
]->sInfo
.dwInitialFrames
)
2361 lInitialFrames
= This
->ppStreams
[nStream
]->sInfo
.dwInitialFrames
;
2364 for (i
= -lInitialFrames
; i
< (LONG
)This
->fInfo
.dwLength
- lInitialFrames
;
2366 DWORD nFrame
= lInitialFrames
+ i
;
2368 assert(nFrame
< This
->nIdxRecords
);
2370 idx
.ckid
= listtypeAVIRECORD
;
2371 idx
.dwFlags
= AVIIF_LIST
;
2372 idx
.dwChunkLength
= This
->idxRecords
[nFrame
].dwChunkLength
;
2373 idx
.dwChunkOffset
= This
->idxRecords
[nFrame
].dwChunkOffset
2374 - This
->dwMoviChunkPos
;
2375 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2376 return AVIERR_FILEWRITE
;
2378 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2379 pStream
= This
->ppStreams
[nStream
];
2381 /* heave we reached start of this stream? */
2382 if (-(LONG
)pStream
->sInfo
.dwInitialFrames
> i
)
2385 if (pStream
->sInfo
.dwInitialFrames
< lInitialFrames
)
2386 nFrame
-= (lInitialFrames
- pStream
->sInfo
.dwInitialFrames
);
2388 /* reached end of this stream? */
2389 if (pStream
->lLastFrame
<= nFrame
)
2392 if ((pStream
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
2393 pStream
->sInfo
.dwFormatChangeCount
!= 0 &&
2394 pStream
->idxFmtChanges
!= NULL
) {
2397 for (pos
= 0; pos
< pStream
->sInfo
.dwFormatChangeCount
; pos
++) {
2398 if (pStream
->idxFmtChanges
[pos
].ckid
== nFrame
) {
2399 idx
.dwFlags
= AVIIF_NOTIME
;
2400 idx
.ckid
= MAKEAVICKID(cktypePALchange
, pStream
->nStream
);
2401 idx
.dwChunkLength
= pStream
->idxFmtChanges
[pos
].dwChunkLength
;
2402 idx
.dwChunkOffset
= pStream
->idxFmtChanges
[pos
].dwChunkOffset
2403 - This
->dwMoviChunkPos
;
2405 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2406 return AVIERR_FILEWRITE
;
2410 } /* if have formatchanges */
2412 idx
.ckid
= pStream
->idxFrames
[nFrame
].ckid
;
2413 idx
.dwFlags
= pStream
->idxFrames
[nFrame
].dwFlags
;
2414 idx
.dwChunkLength
= pStream
->idxFrames
[nFrame
].dwChunkLength
;
2415 idx
.dwChunkOffset
= pStream
->idxFrames
[nFrame
].dwChunkOffset
2416 - This
->dwMoviChunkPos
;
2417 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2418 return AVIERR_FILEWRITE
;
2422 /* not interleaved -- write index for each stream at once */
2423 for (nStream
= 0; nStream
< This
->fInfo
.dwStreams
; nStream
++) {
2424 pStream
= This
->ppStreams
[nStream
];
2426 for (n
= 0; n
<= pStream
->lLastFrame
; n
++) {
2427 if ((pStream
->sInfo
.dwFlags
& AVISTREAMINFO_FORMATCHANGES
) &&
2428 (pStream
->sInfo
.dwFormatChangeCount
!= 0)) {
2431 for (pos
= 0; pos
< pStream
->sInfo
.dwFormatChangeCount
; pos
++) {
2432 if (pStream
->idxFmtChanges
[pos
].ckid
== n
) {
2433 idx
.dwFlags
= AVIIF_NOTIME
;
2434 idx
.ckid
= MAKEAVICKID(cktypePALchange
, pStream
->nStream
);
2435 idx
.dwChunkLength
= pStream
->idxFmtChanges
[pos
].dwChunkLength
;
2437 pStream
->idxFmtChanges
[pos
].dwChunkOffset
- This
->dwMoviChunkPos
;
2438 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2439 return AVIERR_FILEWRITE
;
2443 } /* if have formatchanges */
2445 idx
.ckid
= pStream
->idxFrames
[n
].ckid
;
2446 idx
.dwFlags
= pStream
->idxFrames
[n
].dwFlags
;
2447 idx
.dwChunkLength
= pStream
->idxFrames
[n
].dwChunkLength
;
2448 idx
.dwChunkOffset
= pStream
->idxFrames
[n
].dwChunkOffset
2449 - This
->dwMoviChunkPos
;
2451 if (mmioWrite(This
->hmmio
, (HPSTR
)&idx
, sizeof(idx
)) != sizeof(idx
))
2452 return AVIERR_FILEWRITE
;
2455 } /* if not interleaved */
2457 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
2458 return AVIERR_FILEWRITE
;
2463 static ULONG
AVIFILE_SearchStream(IAVIFileImpl
*This
, DWORD fcc
, LONG lSkip
)
2472 /* search the number of the specified stream */
2473 nStream
= (ULONG
)-1;
2474 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
2475 assert(This
->ppStreams
[i
] != NULL
);
2477 if (This
->ppStreams
[i
]->sInfo
.fccType
== fcc
) {
2491 static void AVIFILE_UpdateInfo(IAVIFileImpl
*This
)
2495 /* pre-conditions */
2496 assert(This
!= NULL
);
2498 This
->fInfo
.dwMaxBytesPerSec
= 0;
2499 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
2500 This
->fInfo
.dwSuggestedBufferSize
= 0;
2501 This
->fInfo
.dwWidth
= 0;
2502 This
->fInfo
.dwHeight
= 0;
2503 This
->fInfo
.dwScale
= 0;
2504 This
->fInfo
.dwRate
= 0;
2505 This
->fInfo
.dwLength
= 0;
2506 This
->dwInitialFrames
= 0;
2508 for (i
= 0; i
< This
->fInfo
.dwStreams
; i
++) {
2509 AVISTREAMINFOW
*psi
;
2512 /* pre-conditions */
2513 assert(This
->ppStreams
[i
] != NULL
);
2515 psi
= &This
->ppStreams
[i
]->sInfo
;
2516 assert(psi
->dwScale
!= 0);
2517 assert(psi
->dwRate
!= 0);
2520 /* use first stream timings as base */
2521 This
->fInfo
.dwScale
= psi
->dwScale
;
2522 This
->fInfo
.dwRate
= psi
->dwRate
;
2523 This
->fInfo
.dwLength
= psi
->dwLength
;
2525 n
= AVIStreamSampleToSample((PAVISTREAM
)This
->ppStreams
[0],
2526 (PAVISTREAM
)This
->ppStreams
[i
],psi
->dwLength
);
2527 if (This
->fInfo
.dwLength
< n
)
2528 This
->fInfo
.dwLength
= n
;
2531 if (This
->dwInitialFrames
< psi
->dwInitialFrames
)
2532 This
->dwInitialFrames
= psi
->dwInitialFrames
;
2534 if (This
->fInfo
.dwSuggestedBufferSize
< psi
->dwSuggestedBufferSize
)
2535 This
->fInfo
.dwSuggestedBufferSize
= psi
->dwSuggestedBufferSize
;
2537 if (psi
->dwSampleSize
!= 0) {
2538 /* fixed sample size -- exact computation */
2539 This
->fInfo
.dwMaxBytesPerSec
+= MulDiv(psi
->dwSampleSize
, psi
->dwRate
,
2542 /* variable sample size -- only upper limit */
2543 This
->fInfo
.dwMaxBytesPerSec
+= MulDiv(psi
->dwSuggestedBufferSize
,
2544 psi
->dwRate
, psi
->dwScale
);
2546 /* update dimensions */
2547 n
= psi
->rcFrame
.right
- psi
->rcFrame
.left
;
2548 if (This
->fInfo
.dwWidth
< n
)
2549 This
->fInfo
.dwWidth
= n
;
2550 n
= psi
->rcFrame
.bottom
- psi
->rcFrame
.top
;
2551 if (This
->fInfo
.dwHeight
< n
)
2552 This
->fInfo
.dwHeight
= n
;
2557 static HRESULT
AVIFILE_WriteBlock(IAVIStreamImpl
*This
, DWORD block
,
2558 FOURCC ckid
, DWORD flags
, LPVOID buffer
,
2567 /* if no frame/block is already written, we must compute start of movi chunk */
2568 if (This
->paf
->dwMoviChunkPos
== 0)
2569 AVIFILE_ComputeMoviStart(This
->paf
);
2571 if (mmioSeek(This
->paf
->hmmio
, This
->paf
->dwNextFramePos
, SEEK_SET
) == -1)
2572 return AVIERR_FILEWRITE
;
2574 if (mmioCreateChunk(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
2575 return AVIERR_FILEWRITE
;
2576 if (buffer
!= NULL
&& size
> 0) {
2577 if (mmioWrite(This
->paf
->hmmio
, (HPSTR
)buffer
, size
) != size
)
2578 return AVIERR_FILEWRITE
;
2580 if (mmioAscend(This
->paf
->hmmio
, &ck
, 0) != S_OK
)
2581 return AVIERR_FILEWRITE
;
2583 This
->paf
->fDirty
= TRUE
;
2584 This
->paf
->dwNextFramePos
= mmioSeek(This
->paf
->hmmio
, 0, SEEK_CUR
);
2586 return AVIFILE_AddFrame(This
, ckid
, size
,
2587 ck
.dwDataOffset
- 2 * sizeof(DWORD
), flags
);