2 * Copyright 2002 Michael Günnewig
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include "avifile_private.h"
34 #include "extrachunk.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(avifile
);
40 /***********************************************************************/
42 #define formtypeWAVE mmioFOURCC('W','A','V','E')
43 #define ckidWAVEFORMAT mmioFOURCC('f','m','t',' ')
44 #define ckidWAVEFACT mmioFOURCC('f','a','c','t')
45 #define ckidWAVEDATA mmioFOURCC('d','a','t','a')
47 /***********************************************************************/
49 #define ENDIAN_SWAPWORD(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8))
50 #define ENDIAN_SWAPDWORD(x) (ENDIAN_SWAPWORD((x >> 16) & 0xFFFF) | \
51 ENDIAN_SWAPWORD(x & 0xFFFF) << 16)
53 #ifdef WORDS_BIGENDIAN
54 #define BE2H_WORD(x) (x)
55 #define BE2H_DWORD(x) (x)
56 #define LE2H_WORD(x) ENDIAN_SWAPWORD(x)
57 #define LE2H_DWORD(x) ENDIAN_SWAPDWORD(x)
59 #define BE2H_WORD(x) ENDIAN_SWAPWORD(x)
60 #define BE2H_DWORD(x) ENDIAN_SWAPDWORD(x)
61 #define LE2H_WORD(x) (x)
62 #define LE2H_DWORD(x) (x)
74 #define AU_ENCODING_ULAW_8 1
75 #define AU_ENCODING_PCM_8 2
76 #define AU_ENCODING_PCM_16 3
77 #define AU_ENCODING_PCM_24 4
78 #define AU_ENCODING_PCM_32 5
79 #define AU_ENCODING_FLOAT 6
80 #define AU_ENCODING_DOUBLE 7
81 #define AU_ENCODING_ADPCM_G721_32 23
82 #define AU_ENCODING_ADPCM_G722 24
83 #define AU_ENCODING_ADPCM_G723_24 25
84 #define AU_ENCODING_ADPCM_G723_5 26
85 #define AU_ENCODING_ALAW_8 27
87 /***********************************************************************/
89 typedef struct _IAVIFileImpl
{
90 IUnknown IUnknown_inner
;
91 IAVIFile IAVIFile_iface
;
92 IPersistFile IPersistFile_iface
;
93 IAVIStream IAVIStream_iface
;
96 /* IAVIFile, IAVIStream stuff... */
100 LPWAVEFORMATEX lpFormat
;
107 /* IPersistFile stuff ... */
114 /***********************************************************************/
116 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
);
117 static HRESULT
AVIFILE_LoadSunFile(IAVIFileImpl
*This
);
118 static HRESULT
AVIFILE_SaveFile(const IAVIFileImpl
*This
);
120 static inline IAVIFileImpl
*impl_from_IUnknown(IUnknown
*iface
)
122 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IUnknown_inner
);
125 static HRESULT WINAPI
IUnknown_fnQueryInterface(IUnknown
*iface
, REFIID riid
, void **ret_iface
)
127 IAVIFileImpl
*This
= impl_from_IUnknown(iface
);
129 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ret_iface
);
131 if (IsEqualGUID(&IID_IUnknown
, riid
))
132 *ret_iface
= &This
->IUnknown_inner
;
133 else if (IsEqualGUID(&IID_IAVIFile
, riid
))
134 *ret_iface
= &This
->IAVIFile_iface
;
135 else if (IsEqualGUID(&IID_IAVIStream
, riid
))
136 *ret_iface
= &This
->IAVIStream_iface
;
137 else if (IsEqualGUID(&IID_IPersistFile
, riid
))
138 *ret_iface
= &This
->IPersistFile_iface
;
140 WARN("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ret_iface
);
142 return E_NOINTERFACE
;
145 /* Violation of the COM aggregation ref counting rule */
146 IUnknown_AddRef(&This
->IUnknown_inner
);
150 static ULONG WINAPI
IUnknown_fnAddRef(IUnknown
*iface
)
152 IAVIFileImpl
*This
= impl_from_IUnknown(iface
);
153 ULONG ref
= InterlockedIncrement(&This
->ref
);
155 TRACE("(%p) ref=%d\n", This
, ref
);
160 static ULONG WINAPI
IUnknown_fnRelease(IUnknown
*iface
)
162 IAVIFileImpl
*This
= impl_from_IUnknown(iface
);
163 ULONG ref
= InterlockedDecrement(&This
->ref
);
165 TRACE("(%p) ref=%d\n", This
, ref
);
168 /* need to write headers to file */
170 AVIFILE_SaveFile(This
);
172 HeapFree(GetProcessHeap(), 0, This
->lpFormat
);
173 This
->lpFormat
= NULL
;
175 HeapFree(GetProcessHeap(), 0, This
->extra
.lp
);
176 This
->extra
.lp
= NULL
;
178 HeapFree(GetProcessHeap(), 0, This
->szFileName
);
179 This
->szFileName
= NULL
;
181 mmioClose(This
->hmmio
, 0);
184 HeapFree(GetProcessHeap(), 0, This
);
190 static const IUnknownVtbl unk_vtbl
=
192 IUnknown_fnQueryInterface
,
197 static inline IAVIFileImpl
*impl_from_IAVIFile(IAVIFile
*iface
)
199 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IAVIFile_iface
);
202 static HRESULT WINAPI
IAVIFile_fnQueryInterface(IAVIFile
*iface
, REFIID riid
, void **ret_iface
)
204 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
206 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ret_iface
);
209 static ULONG WINAPI
IAVIFile_fnAddRef(IAVIFile
*iface
)
211 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
213 return IUnknown_AddRef(This
->outer_unk
);
216 static ULONG WINAPI
IAVIFile_fnRelease(IAVIFile
*iface
)
218 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
220 return IUnknown_Release(This
->outer_unk
);
223 static HRESULT WINAPI
IAVIFile_fnInfo(IAVIFile
*iface
, AVIFILEINFOW
*afi
, LONG size
)
225 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
227 TRACE("(%p,%p,%d)\n",iface
,afi
,size
);
230 return AVIERR_BADPARAM
;
232 return AVIERR_BADSIZE
;
234 /* update file info */
235 This
->fInfo
.dwFlags
= 0;
236 This
->fInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
237 if (This
->lpFormat
!= NULL
) {
238 assert(This
->sInfo
.dwScale
!= 0);
240 This
->fInfo
.dwStreams
= 1;
241 This
->fInfo
.dwScale
= This
->sInfo
.dwScale
;
242 This
->fInfo
.dwRate
= This
->sInfo
.dwRate
;
243 This
->fInfo
.dwLength
= This
->sInfo
.dwLength
;
244 This
->fInfo
.dwSuggestedBufferSize
= This
->ckData
.cksize
;
245 This
->fInfo
.dwMaxBytesPerSec
=
246 MulDiv(This
->sInfo
.dwSampleSize
,This
->sInfo
.dwRate
,This
->sInfo
.dwScale
);
249 memcpy(afi
, &This
->fInfo
, min((DWORD
)size
, sizeof(This
->fInfo
)));
251 if ((DWORD
)size
< sizeof(This
->fInfo
))
252 return AVIERR_BUFFERTOOSMALL
;
256 static HRESULT WINAPI
IAVIFile_fnGetStream(IAVIFile
*iface
, IAVIStream
**avis
, DWORD fccType
,
259 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
261 TRACE("(%p,%p,0x%08X,%d)\n", iface
, avis
, fccType
, lParam
);
263 /* check parameter */
265 return AVIERR_BADPARAM
;
269 /* Does our stream exists? */
270 if (lParam
!= 0 || This
->fInfo
.dwStreams
== 0)
271 return AVIERR_NODATA
;
272 if (fccType
!= 0 && fccType
!= streamtypeAUDIO
)
273 return AVIERR_NODATA
;
275 *avis
= &This
->IAVIStream_iface
;
276 IAVIStream_AddRef(*avis
);
281 static HRESULT WINAPI
IAVIFile_fnCreateStream(IAVIFile
*iface
, IAVIStream
**avis
,
284 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
286 TRACE("(%p,%p,%p)\n", iface
, avis
, asi
);
288 /* check parameters */
289 if (avis
== NULL
|| asi
== NULL
)
290 return AVIERR_BADPARAM
;
294 /* We only support one audio stream */
295 if (This
->fInfo
.dwStreams
!= 0 || This
->lpFormat
!= NULL
)
296 return AVIERR_UNSUPPORTED
;
297 if (asi
->fccType
!= streamtypeAUDIO
)
298 return AVIERR_UNSUPPORTED
;
300 /* Does the user have write permission? */
301 if ((This
->uMode
& MMIO_RWMODE
) == 0)
302 return AVIERR_READONLY
;
305 This
->lpFormat
= NULL
;
307 memcpy(&This
->sInfo
, asi
, sizeof(This
->sInfo
));
309 /* make sure streaminfo if okay for us */
310 This
->sInfo
.fccHandler
= 0;
311 This
->sInfo
.dwFlags
= 0;
312 This
->sInfo
.dwCaps
= AVIFILECAPS_CANREAD
|AVIFILECAPS_CANWRITE
;
313 This
->sInfo
.dwStart
= 0;
314 This
->sInfo
.dwInitialFrames
= 0;
315 This
->sInfo
.dwFormatChangeCount
= 0;
316 SetRectEmpty(&This
->sInfo
.rcFrame
);
318 This
->fInfo
.dwStreams
= 1;
319 This
->fInfo
.dwScale
= This
->sInfo
.dwScale
;
320 This
->fInfo
.dwRate
= This
->sInfo
.dwRate
;
321 This
->fInfo
.dwLength
= This
->sInfo
.dwLength
;
323 This
->ckData
.dwDataOffset
= 0;
324 This
->ckData
.cksize
= 0;
326 *avis
= &This
->IAVIStream_iface
;
327 IAVIStream_AddRef(*avis
);
332 static HRESULT WINAPI
IAVIFile_fnWriteData(IAVIFile
*iface
, DWORD ckid
, void *lpData
, LONG size
)
334 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
336 TRACE("(%p,0x%08X,%p,%d)\n", iface
, ckid
, lpData
, size
);
338 /* check parameters */
340 return AVIERR_BADPARAM
;
342 return AVIERR_BADSIZE
;
344 /* Do we have write permission? */
345 if ((This
->uMode
& MMIO_RWMODE
) == 0)
346 return AVIERR_READONLY
;
350 return WriteExtraChunk(&This
->extra
, ckid
, lpData
, size
);
353 static HRESULT WINAPI
IAVIFile_fnReadData(IAVIFile
*iface
, DWORD ckid
, void *lpData
, LONG
*size
)
355 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
357 TRACE("(%p,0x%08X,%p,%p)\n", iface
, ckid
, lpData
, size
);
359 return ReadExtraChunk(&This
->extra
, ckid
, lpData
, size
);
362 static HRESULT WINAPI
IAVIFile_fnEndRecord(IAVIFile
*iface
)
364 TRACE("(%p)\n",iface
);
366 /* This is only needed for interleaved files.
367 * We have only one stream, which can't be interleaved.
372 static HRESULT WINAPI
IAVIFile_fnDeleteStream(IAVIFile
*iface
, DWORD fccType
, LONG lParam
)
374 IAVIFileImpl
*This
= impl_from_IAVIFile(iface
);
376 TRACE("(%p,0x%08X,%d)\n", iface
, fccType
, lParam
);
378 /* check parameter */
380 return AVIERR_BADPARAM
;
382 /* Do we have our audio stream? */
383 if (lParam
!= 0 || This
->fInfo
.dwStreams
== 0 ||
384 (fccType
!= 0 && fccType
!= streamtypeAUDIO
))
385 return AVIERR_NODATA
;
387 /* Have user write permissions? */
388 if ((This
->uMode
& MMIO_RWMODE
) == 0)
389 return AVIERR_READONLY
;
391 HeapFree(GetProcessHeap(), 0, This
->lpFormat
);
392 This
->lpFormat
= NULL
;
396 This
->ckData
.dwDataOffset
= 0;
397 This
->ckData
.cksize
= 0;
399 This
->sInfo
.dwScale
= 0;
400 This
->sInfo
.dwRate
= 0;
401 This
->sInfo
.dwLength
= 0;
402 This
->sInfo
.dwSuggestedBufferSize
= 0;
404 This
->fInfo
.dwStreams
= 0;
405 This
->fInfo
.dwEditCount
++;
412 static const struct IAVIFileVtbl iwavft
= {
413 IAVIFile_fnQueryInterface
,
417 IAVIFile_fnGetStream
,
418 IAVIFile_fnCreateStream
,
419 IAVIFile_fnWriteData
,
421 IAVIFile_fnEndRecord
,
422 IAVIFile_fnDeleteStream
425 /***********************************************************************/
427 static inline IAVIFileImpl
*impl_from_IPersistFile(IPersistFile
*iface
)
429 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IPersistFile_iface
);
432 static HRESULT WINAPI
IPersistFile_fnQueryInterface(IPersistFile
*iface
, REFIID riid
,
435 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
437 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ret_iface
);
440 static ULONG WINAPI
IPersistFile_fnAddRef(IPersistFile
*iface
)
442 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
444 return IUnknown_AddRef(This
->outer_unk
);
447 static ULONG WINAPI
IPersistFile_fnRelease(IPersistFile
*iface
)
449 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
451 return IUnknown_Release(This
->outer_unk
);
454 static HRESULT WINAPI
IPersistFile_fnGetClassID(IPersistFile
*iface
,
457 TRACE("(%p,%p)\n", iface
, pClassID
);
459 if (pClassID
== NULL
)
460 return AVIERR_BADPARAM
;
462 *pClassID
= CLSID_WAVFile
;
467 static HRESULT WINAPI
IPersistFile_fnIsDirty(IPersistFile
*iface
)
469 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
471 TRACE("(%p)\n", iface
);
473 return (This
->fDirty
? S_OK
: S_FALSE
);
476 static HRESULT WINAPI
IPersistFile_fnLoad(IPersistFile
*iface
, LPCOLESTR pszFileName
, DWORD dwMode
)
478 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
479 WCHAR wszStreamFmt
[50];
482 TRACE("(%p,%s,0x%08X)\n", iface
, debugstr_w(pszFileName
), dwMode
);
484 /* check parameter */
485 if (pszFileName
== NULL
)
486 return AVIERR_BADPARAM
;
488 if (This
->hmmio
!= NULL
)
489 return AVIERR_ERROR
; /* No reuse of this object for another file! */
491 /* remember mode and name */
492 This
->uMode
= dwMode
;
494 len
= lstrlenW(pszFileName
) + 1;
495 This
->szFileName
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
496 if (This
->szFileName
== NULL
)
497 return AVIERR_MEMORY
;
498 lstrcpyW(This
->szFileName
, pszFileName
);
500 /* try to open the file */
501 This
->hmmio
= mmioOpenW(This
->szFileName
, NULL
, MMIO_ALLOCBUF
| dwMode
);
502 if (This
->hmmio
== NULL
) {
503 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
505 len
= WideCharToMultiByte(CP_ACP
, 0, This
->szFileName
, -1,
506 NULL
, 0, NULL
, NULL
);
507 szFileName
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(CHAR
));
508 if (szFileName
== NULL
)
509 return AVIERR_MEMORY
;
511 WideCharToMultiByte(CP_ACP
, 0, This
->szFileName
, -1, szFileName
,
514 This
->hmmio
= mmioOpenA(szFileName
, NULL
, MMIO_ALLOCBUF
| dwMode
);
515 HeapFree(GetProcessHeap(), 0, szFileName
);
516 if (This
->hmmio
== NULL
)
517 return AVIERR_FILEOPEN
;
520 memset(& This
->fInfo
, 0, sizeof(This
->fInfo
));
521 memset(& This
->sInfo
, 0, sizeof(This
->sInfo
));
523 LoadStringW(AVIFILE_hModule
, IDS_WAVEFILETYPE
, This
->fInfo
.szFileType
,
524 ARRAY_SIZE(This
->fInfo
.szFileType
));
525 if (LoadStringW(AVIFILE_hModule
, IDS_WAVESTREAMFORMAT
,
526 wszStreamFmt
, ARRAY_SIZE(wszStreamFmt
)) > 0) {
527 wsprintfW(This
->sInfo
.szName
, wszStreamFmt
,
528 AVIFILE_BasenameW(This
->szFileName
));
531 /* should we create a new file? */
532 if (dwMode
& OF_CREATE
) {
533 /* nothing more to do */
536 return AVIFILE_LoadFile(This
);
539 static HRESULT WINAPI
IPersistFile_fnSave(IPersistFile
*iface
,
540 LPCOLESTR pszFileName
,BOOL fRemember
)
542 TRACE("(%p,%s,%d)\n", iface
, debugstr_w(pszFileName
), fRemember
);
544 /* We write directly to disk, so nothing to do. */
549 static HRESULT WINAPI
IPersistFile_fnSaveCompleted(IPersistFile
*iface
,
550 LPCOLESTR pszFileName
)
552 TRACE("(%p,%s)\n", iface
, debugstr_w(pszFileName
));
554 /* We write directly to disk, so nothing to do. */
559 static HRESULT WINAPI
IPersistFile_fnGetCurFile(IPersistFile
*iface
, LPOLESTR
*ppszFileName
)
561 IAVIFileImpl
*This
= impl_from_IPersistFile(iface
);
563 TRACE("(%p,%p)\n", iface
, ppszFileName
);
565 if (ppszFileName
== NULL
)
566 return AVIERR_BADPARAM
;
568 *ppszFileName
= NULL
;
570 if (This
->szFileName
) {
571 int len
= lstrlenW(This
->szFileName
) + 1;
573 *ppszFileName
= CoTaskMemAlloc(len
* sizeof(WCHAR
));
574 if (*ppszFileName
== NULL
)
575 return AVIERR_MEMORY
;
577 lstrcpyW(*ppszFileName
, This
->szFileName
);
583 static const struct IPersistFileVtbl iwavpft
= {
584 IPersistFile_fnQueryInterface
,
585 IPersistFile_fnAddRef
,
586 IPersistFile_fnRelease
,
587 IPersistFile_fnGetClassID
,
588 IPersistFile_fnIsDirty
,
591 IPersistFile_fnSaveCompleted
,
592 IPersistFile_fnGetCurFile
595 /***********************************************************************/
597 static inline IAVIFileImpl
*impl_from_IAVIStream(IAVIStream
*iface
)
599 return CONTAINING_RECORD(iface
, IAVIFileImpl
, IAVIStream_iface
);
602 static HRESULT WINAPI
IAVIStream_fnQueryInterface(IAVIStream
*iface
, REFIID riid
, void **ret_iface
)
604 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
606 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ret_iface
);
609 static ULONG WINAPI
IAVIStream_fnAddRef(IAVIStream
*iface
)
611 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
613 return IUnknown_AddRef(This
->outer_unk
);
616 static ULONG WINAPI
IAVIStream_fnRelease(IAVIStream
* iface
)
618 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
620 return IUnknown_Release(This
->outer_unk
);
623 static HRESULT WINAPI
IAVIStream_fnCreate(IAVIStream
*iface
, LPARAM lParam1
,
626 TRACE("(%p,0x%08lX,0x%08lX)\n", iface
, lParam1
, lParam2
);
628 /* This IAVIStream interface needs an WAVFile */
629 return AVIERR_UNSUPPORTED
;
632 static HRESULT WINAPI
IAVIStream_fnInfo(IAVIStream
*iface
, AVISTREAMINFOW
*psi
, LONG size
)
634 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
636 TRACE("(%p,%p,%d)\n", iface
, psi
, size
);
639 return AVIERR_BADPARAM
;
641 return AVIERR_BADSIZE
;
643 memcpy(psi
, &This
->sInfo
, min((DWORD
)size
, sizeof(This
->sInfo
)));
645 if ((DWORD
)size
< sizeof(This
->sInfo
))
646 return AVIERR_BUFFERTOOSMALL
;
650 static LONG WINAPI
IAVIStream_fnFindSample(IAVIStream
*iface
, LONG pos
, LONG flags
)
652 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
654 TRACE("(%p,%d,0x%08X)\n",iface
,pos
,flags
);
656 /* Do we have data? */
657 if (This
->lpFormat
== NULL
)
660 /* We don't have an index */
661 if (flags
& FIND_INDEX
)
664 if (flags
& FIND_FROM_START
) {
665 pos
= This
->sInfo
.dwStart
;
666 flags
&= ~(FIND_FROM_START
|FIND_PREV
);
670 if (flags
& FIND_FORMAT
) {
671 if ((flags
& FIND_NEXT
) && pos
> 0)
677 if ((flags
& FIND_RET
) == FIND_LENGTH
||
678 (flags
& FIND_RET
) == FIND_SIZE
)
679 return This
->sInfo
.dwSampleSize
;
680 if ((flags
& FIND_RET
) == FIND_OFFSET
)
681 return This
->ckData
.dwDataOffset
+ pos
* This
->sInfo
.dwSampleSize
;
686 static HRESULT WINAPI
IAVIStream_fnReadFormat(IAVIStream
*iface
, LONG pos
, void *format
,
689 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
691 TRACE("(%p,%d,%p,%p)\n", iface
, pos
, format
, formatsize
);
693 if (formatsize
== NULL
)
694 return AVIERR_BADPARAM
;
696 /* only interested in needed buffersize? */
697 if (format
== NULL
|| *formatsize
<= 0) {
698 *formatsize
= This
->cbFormat
;
703 /* copy initial format (only as much as will fit) */
704 memcpy(format
, This
->lpFormat
, min(*formatsize
, This
->cbFormat
));
705 if (*formatsize
< This
->cbFormat
) {
706 *formatsize
= This
->cbFormat
;
707 return AVIERR_BUFFERTOOSMALL
;
710 *formatsize
= This
->cbFormat
;
714 static HRESULT WINAPI
IAVIStream_fnSetFormat(IAVIStream
*iface
, LONG pos
, void *format
,
717 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
719 TRACE("(%p,%d,%p,%d)\n", iface
, pos
, format
, formatsize
);
721 /* check parameters */
722 if (format
== NULL
|| formatsize
<= sizeof(PCMWAVEFORMAT
))
723 return AVIERR_BADPARAM
;
725 /* We can only do this to an empty wave file, but ignore call
726 * if still same format */
727 if (This
->lpFormat
!= NULL
) {
728 if (formatsize
!= This
->cbFormat
||
729 memcmp(format
, This
->lpFormat
, formatsize
) != 0)
730 return AVIERR_UNSUPPORTED
;
735 /* only support start at position 0 */
737 return AVIERR_UNSUPPORTED
;
739 /* Do we have write permission? */
740 if ((This
->uMode
& MMIO_RWMODE
) == 0)
741 return AVIERR_READONLY
;
743 /* get memory for format and copy it */
744 This
->lpFormat
= HeapAlloc(GetProcessHeap(), 0, formatsize
);
745 if (This
->lpFormat
== NULL
)
746 return AVIERR_MEMORY
;
748 This
->cbFormat
= formatsize
;
749 memcpy(This
->lpFormat
, format
, formatsize
);
751 /* update info's about 'data' chunk */
752 This
->ckData
.dwDataOffset
= formatsize
+ 7 * sizeof(DWORD
);
753 This
->ckData
.cksize
= 0;
755 /* for non-pcm format we need also a 'fact' chunk */
756 if (This
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
)
757 This
->ckData
.dwDataOffset
+= 3 * sizeof(DWORD
);
759 /* update stream and file info */
760 This
->sInfo
.dwSampleSize
= This
->lpFormat
->nBlockAlign
;
761 This
->sInfo
.dwScale
= This
->lpFormat
->nBlockAlign
;
762 This
->sInfo
.dwRate
= This
->lpFormat
->nAvgBytesPerSec
;
763 This
->sInfo
.dwLength
= 0;
764 This
->sInfo
.dwSuggestedBufferSize
= 0;
769 static HRESULT WINAPI
IAVIStream_fnRead(IAVIStream
*iface
, LONG start
, LONG samples
, void *buffer
,
770 LONG buffersize
, LONG
*bytesread
, LONG
*samplesread
)
772 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
774 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface
, start
, samples
, buffer
,
775 buffersize
, bytesread
, samplesread
);
777 /* clear return parameters if given */
778 if (bytesread
!= NULL
)
780 if (samplesread
!= NULL
)
783 /* positions without data */
784 if (start
< 0 || (DWORD
)start
> This
->sInfo
.dwLength
)
790 if (buffersize
> 0) {
792 samples
= min((DWORD
)samples
, buffersize
/ This
->sInfo
.dwSampleSize
);
794 samples
= buffersize
/ This
->sInfo
.dwSampleSize
;
797 /* limit to end of stream */
798 if ((DWORD
)(start
+ samples
) > This
->sInfo
.dwLength
)
799 samples
= This
->sInfo
.dwLength
- start
;
801 /* request only the sizes? */
802 if (buffer
== NULL
|| buffersize
<= 0) {
803 /* then I need at least one parameter for it */
804 if (bytesread
== NULL
&& samplesread
== NULL
)
805 return AVIERR_BADPARAM
;
807 if (bytesread
!= NULL
)
808 *bytesread
= samples
* This
->sInfo
.dwSampleSize
;
809 if (samplesread
!= NULL
)
810 *samplesread
= samples
;
815 /* nothing to read? */
819 /* Can I read at least one sample? */
820 if ((DWORD
)buffersize
< This
->sInfo
.dwSampleSize
)
821 return AVIERR_BUFFERTOOSMALL
;
823 buffersize
= samples
* This
->sInfo
.dwSampleSize
;
825 if (mmioSeek(This
->hmmio
, This
->ckData
.dwDataOffset
826 + start
* This
->sInfo
.dwSampleSize
, SEEK_SET
) == -1)
827 return AVIERR_FILEREAD
;
828 if (mmioRead(This
->hmmio
, buffer
, buffersize
) != buffersize
)
829 return AVIERR_FILEREAD
;
831 /* fill out return parameters if given */
832 if (bytesread
!= NULL
)
833 *bytesread
= buffersize
;
834 if (samplesread
!= NULL
)
835 *samplesread
= samples
;
840 static HRESULT WINAPI
IAVIStream_fnWrite(IAVIStream
*iface
, LONG start
, LONG samples
, void *buffer
,
841 LONG buffersize
, DWORD flags
, LONG
*sampwritten
, LONG
*byteswritten
)
843 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
845 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface
, start
, samples
,
846 buffer
, buffersize
, flags
, sampwritten
, byteswritten
);
848 /* clear return parameters if given */
849 if (sampwritten
!= NULL
)
851 if (byteswritten
!= NULL
)
854 /* check parameters */
855 if (buffer
== NULL
&& (buffersize
> 0 || samples
> 0))
856 return AVIERR_BADPARAM
;
858 /* Do we have write permission? */
859 if ((This
->uMode
& MMIO_RWMODE
) == 0)
860 return AVIERR_READONLY
;
862 /* < 0 means "append" */
864 start
= This
->sInfo
.dwStart
+ This
->sInfo
.dwLength
;
866 /* check buffersize -- must multiple of samplesize */
867 if (buffersize
& ~(This
->sInfo
.dwSampleSize
- 1))
868 return AVIERR_BADSIZE
;
870 /* do we have anything to write? */
871 if (buffer
!= NULL
&& buffersize
> 0) {
874 if (mmioSeek(This
->hmmio
, This
->ckData
.dwDataOffset
+
875 start
* This
->sInfo
.dwSampleSize
, SEEK_SET
) == -1)
876 return AVIERR_FILEWRITE
;
877 if (mmioWrite(This
->hmmio
, buffer
, buffersize
) != buffersize
)
878 return AVIERR_FILEWRITE
;
880 This
->sInfo
.dwLength
= max(This
->sInfo
.dwLength
, (DWORD
)start
+ samples
);
881 This
->ckData
.cksize
= max(This
->ckData
.cksize
,
882 start
* This
->sInfo
.dwSampleSize
+ buffersize
);
884 /* fill out return parameters if given */
885 if (sampwritten
!= NULL
)
886 *sampwritten
= samples
;
887 if (byteswritten
!= NULL
)
888 *byteswritten
= buffersize
;
894 static HRESULT WINAPI
IAVIStream_fnDelete(IAVIStream
*iface
, LONG start
, LONG samples
)
896 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
898 TRACE("(%p,%d,%d)\n", iface
, start
, samples
);
900 /* check parameters */
901 if (start
< 0 || samples
< 0)
902 return AVIERR_BADPARAM
;
904 /* Delete before start of stream? */
905 if ((DWORD
)(start
+ samples
) < This
->sInfo
.dwStart
)
908 /* Delete after end of stream? */
909 if ((DWORD
)start
> This
->sInfo
.dwLength
)
912 /* For the rest we need write permissions */
913 if ((This
->uMode
& MMIO_RWMODE
) == 0)
914 return AVIERR_READONLY
;
916 if ((DWORD
)(start
+ samples
) >= This
->sInfo
.dwLength
) {
917 /* deletion at end */
918 samples
= This
->sInfo
.dwLength
- start
;
919 This
->sInfo
.dwLength
-= samples
;
920 This
->ckData
.cksize
-= samples
* This
->sInfo
.dwSampleSize
;
921 } else if ((DWORD
)start
<= This
->sInfo
.dwStart
) {
922 /* deletion at start */
923 samples
= This
->sInfo
.dwStart
- start
;
924 start
= This
->sInfo
.dwStart
;
925 This
->ckData
.dwDataOffset
+= samples
* This
->sInfo
.dwSampleSize
;
926 This
->ckData
.cksize
-= samples
* This
->sInfo
.dwSampleSize
;
928 /* deletion inside stream -- needs playlist and cue's */
929 FIXME(": deletion inside of stream not supported!\n");
931 return AVIERR_UNSUPPORTED
;
939 static HRESULT WINAPI
IAVIStream_fnReadData(IAVIStream
*iface
, DWORD fcc
, void *lp
, LONG
*lpread
)
941 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
943 return IAVIFile_ReadData(&This
->IAVIFile_iface
, fcc
, lp
, lpread
);
946 static HRESULT WINAPI
IAVIStream_fnWriteData(IAVIStream
*iface
, DWORD fcc
, void *lp
, LONG size
)
948 IAVIFileImpl
*This
= impl_from_IAVIStream(iface
);
950 return IAVIFile_WriteData(&This
->IAVIFile_iface
, fcc
, lp
, size
);
953 static HRESULT WINAPI
IAVIStream_fnSetInfo(IAVIStream
*iface
,
954 LPAVISTREAMINFOW info
, LONG infolen
)
956 FIXME("(%p,%p,%d): stub\n", iface
, info
, infolen
);
961 static const struct IAVIStreamVtbl iwavst
= {
962 IAVIStream_fnQueryInterface
,
964 IAVIStream_fnRelease
,
967 IAVIStream_fnFindSample
,
968 IAVIStream_fnReadFormat
,
969 IAVIStream_fnSetFormat
,
973 IAVIStream_fnReadData
,
974 IAVIStream_fnWriteData
,
978 HRESULT
AVIFILE_CreateWAVFile(IUnknown
*outer_unk
, REFIID riid
, void **ret_iface
)
985 pfile
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pfile
));
987 return AVIERR_MEMORY
;
989 pfile
->IUnknown_inner
.lpVtbl
= &unk_vtbl
;
990 pfile
->IAVIFile_iface
.lpVtbl
= &iwavft
;
991 pfile
->IPersistFile_iface
.lpVtbl
= &iwavpft
;
992 pfile
->IAVIStream_iface
.lpVtbl
= &iwavst
;
995 pfile
->outer_unk
= outer_unk
;
997 pfile
->outer_unk
= &pfile
->IUnknown_inner
;
999 hr
= IUnknown_QueryInterface(&pfile
->IUnknown_inner
, riid
, ret_iface
);
1000 IUnknown_Release(&pfile
->IUnknown_inner
);
1005 /***********************************************************************/
1007 static HRESULT
AVIFILE_LoadFile(IAVIFileImpl
*This
)
1012 This
->sInfo
.dwLength
= 0; /* just to be sure */
1013 This
->fDirty
= FALSE
;
1015 /* search for RIFF chunk */
1016 ckRIFF
.fccType
= 0; /* find any */
1017 if (mmioDescend(This
->hmmio
, &ckRIFF
, NULL
, MMIO_FINDRIFF
) != S_OK
) {
1018 return AVIFILE_LoadSunFile(This
);
1021 if (ckRIFF
.fccType
!= formtypeWAVE
)
1022 return AVIERR_BADFORMAT
;
1024 /* search WAVE format chunk */
1025 ck
.ckid
= ckidWAVEFORMAT
;
1026 if (FindChunkAndKeepExtras(&This
->extra
, This
->hmmio
, &ck
,
1027 &ckRIFF
, MMIO_FINDCHUNK
) != S_OK
)
1028 return AVIERR_FILEREAD
;
1030 /* get memory for format and read it */
1031 This
->lpFormat
= HeapAlloc(GetProcessHeap(), 0, ck
.cksize
);
1032 if (This
->lpFormat
== NULL
)
1033 return AVIERR_FILEREAD
;
1034 This
->cbFormat
= ck
.cksize
;
1036 if (mmioRead(This
->hmmio
, (HPSTR
)This
->lpFormat
, ck
.cksize
) != ck
.cksize
)
1037 return AVIERR_FILEREAD
;
1038 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1039 return AVIERR_FILEREAD
;
1041 /* Non-pcm formats have a fact chunk.
1042 * We don't need it, so simply add it to the extra chunks.
1045 /* find the big data chunk */
1046 This
->ckData
.ckid
= ckidWAVEDATA
;
1047 if (FindChunkAndKeepExtras(&This
->extra
, This
->hmmio
, &This
->ckData
,
1048 &ckRIFF
, MMIO_FINDCHUNK
) != S_OK
)
1049 return AVIERR_FILEREAD
;
1051 memset(&This
->sInfo
, 0, sizeof(This
->sInfo
));
1052 This
->sInfo
.fccType
= streamtypeAUDIO
;
1053 This
->sInfo
.dwRate
= This
->lpFormat
->nAvgBytesPerSec
;
1054 This
->sInfo
.dwSampleSize
=
1055 This
->sInfo
.dwScale
= This
->lpFormat
->nBlockAlign
;
1056 This
->sInfo
.dwLength
= This
->ckData
.cksize
/ This
->lpFormat
->nBlockAlign
;
1057 This
->sInfo
.dwSuggestedBufferSize
= This
->ckData
.cksize
;
1059 This
->fInfo
.dwStreams
= 1;
1061 if (mmioAscend(This
->hmmio
, &This
->ckData
, 0) != S_OK
) {
1062 /* seems to be truncated */
1063 WARN(": file seems to be truncated!\n");
1064 This
->ckData
.cksize
= mmioSeek(This
->hmmio
, 0, SEEK_END
) -
1065 This
->ckData
.dwDataOffset
;
1066 This
->sInfo
.dwLength
= This
->ckData
.cksize
/ This
->lpFormat
->nBlockAlign
;
1067 This
->sInfo
.dwSuggestedBufferSize
= This
->ckData
.cksize
;
1071 FindChunkAndKeepExtras(&This
->extra
, This
->hmmio
, &ck
, &ckRIFF
, 0);
1076 static HRESULT
AVIFILE_LoadSunFile(IAVIFileImpl
*This
)
1078 SUNAUDIOHEADER auhdr
;
1080 mmioSeek(This
->hmmio
, 0, SEEK_SET
);
1081 if (mmioRead(This
->hmmio
, (HPSTR
)&auhdr
, sizeof(auhdr
)) != sizeof(auhdr
))
1082 return AVIERR_FILEREAD
;
1084 if (auhdr
.fccType
== 0x0064732E) {
1085 /* header in little endian */
1086 This
->ckData
.dwDataOffset
= LE2H_DWORD(auhdr
.offset
);
1087 This
->ckData
.cksize
= LE2H_DWORD(auhdr
.size
);
1089 auhdr
.encoding
= LE2H_DWORD(auhdr
.encoding
);
1090 auhdr
.sampleRate
= LE2H_DWORD(auhdr
.sampleRate
);
1091 auhdr
.channels
= LE2H_DWORD(auhdr
.channels
);
1092 } else if (auhdr
.fccType
== mmioFOURCC('.','s','n','d')) {
1093 /* header in big endian */
1094 This
->ckData
.dwDataOffset
= BE2H_DWORD(auhdr
.offset
);
1095 This
->ckData
.cksize
= BE2H_DWORD(auhdr
.size
);
1097 auhdr
.encoding
= BE2H_DWORD(auhdr
.encoding
);
1098 auhdr
.sampleRate
= BE2H_DWORD(auhdr
.sampleRate
);
1099 auhdr
.channels
= BE2H_DWORD(auhdr
.channels
);
1101 return AVIERR_FILEREAD
;
1103 if (auhdr
.channels
< 1)
1104 return AVIERR_BADFORMAT
;
1106 /* get size of header */
1107 switch(auhdr
.encoding
) {
1108 case AU_ENCODING_ADPCM_G721_32
:
1109 This
->cbFormat
= sizeof(G721_ADPCMWAVEFORMAT
); break;
1110 case AU_ENCODING_ADPCM_G723_24
:
1111 This
->cbFormat
= sizeof(G723_ADPCMWAVEFORMAT
); break;
1112 case AU_ENCODING_ADPCM_G722
:
1113 case AU_ENCODING_ADPCM_G723_5
:
1114 WARN("unsupported Sun audio format %d\n", auhdr
.encoding
);
1115 return AVIERR_UNSUPPORTED
; /* FIXME */
1117 This
->cbFormat
= sizeof(WAVEFORMATEX
); break;
1120 This
->lpFormat
= HeapAlloc(GetProcessHeap(), 0, This
->cbFormat
);
1121 if (This
->lpFormat
== NULL
)
1122 return AVIERR_MEMORY
;
1124 This
->lpFormat
->nChannels
= auhdr
.channels
;
1125 This
->lpFormat
->nSamplesPerSec
= auhdr
.sampleRate
;
1126 switch(auhdr
.encoding
) {
1127 case AU_ENCODING_ULAW_8
:
1128 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_MULAW
;
1129 This
->lpFormat
->wBitsPerSample
= 8;
1131 case AU_ENCODING_PCM_8
:
1132 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
1133 This
->lpFormat
->wBitsPerSample
= 8;
1135 case AU_ENCODING_PCM_16
:
1136 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
1137 This
->lpFormat
->wBitsPerSample
= 16;
1139 case AU_ENCODING_PCM_24
:
1140 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
1141 This
->lpFormat
->wBitsPerSample
= 24;
1143 case AU_ENCODING_PCM_32
:
1144 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
1145 This
->lpFormat
->wBitsPerSample
= 32;
1147 case AU_ENCODING_ALAW_8
:
1148 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_ALAW
;
1149 This
->lpFormat
->wBitsPerSample
= 8;
1151 case AU_ENCODING_ADPCM_G721_32
:
1152 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_G721_ADPCM
;
1153 This
->lpFormat
->wBitsPerSample
= (3*5*8);
1154 This
->lpFormat
->nBlockAlign
= 15*15*8;
1155 This
->lpFormat
->cbSize
= sizeof(WORD
);
1156 ((LPG721_ADPCMWAVEFORMAT
)This
->lpFormat
)->nAuxBlockSize
= 0;
1158 case AU_ENCODING_ADPCM_G723_24
:
1159 This
->lpFormat
->wFormatTag
= WAVE_FORMAT_G723_ADPCM
;
1160 This
->lpFormat
->wBitsPerSample
= (3*5*8);
1161 This
->lpFormat
->nBlockAlign
= 15*15*8;
1162 This
->lpFormat
->cbSize
= 2*sizeof(WORD
);
1163 ((LPG723_ADPCMWAVEFORMAT
)This
->lpFormat
)->cbExtraSize
= 0;
1164 ((LPG723_ADPCMWAVEFORMAT
)This
->lpFormat
)->nAuxBlockSize
= 0;
1167 WARN("unsupported Sun audio format %d\n", auhdr
.encoding
);
1168 return AVIERR_UNSUPPORTED
;
1171 This
->lpFormat
->nBlockAlign
=
1172 (This
->lpFormat
->nChannels
* This
->lpFormat
->wBitsPerSample
) / 8;
1173 if (This
->lpFormat
->nBlockAlign
== 0 && This
->lpFormat
->wBitsPerSample
< 8)
1174 This
->lpFormat
->nBlockAlign
++;
1175 This
->lpFormat
->nAvgBytesPerSec
=
1176 This
->lpFormat
->nBlockAlign
* This
->lpFormat
->nSamplesPerSec
;
1178 This
->fDirty
= FALSE
;
1180 This
->sInfo
.fccType
= streamtypeAUDIO
;
1181 This
->sInfo
.fccHandler
= 0;
1182 This
->sInfo
.dwFlags
= 0;
1183 This
->sInfo
.wPriority
= 0;
1184 This
->sInfo
.wLanguage
= 0;
1185 This
->sInfo
.dwInitialFrames
= 0;
1186 This
->sInfo
.dwScale
= This
->lpFormat
->nBlockAlign
;
1187 This
->sInfo
.dwRate
= This
->lpFormat
->nAvgBytesPerSec
;
1188 This
->sInfo
.dwStart
= 0;
1189 This
->sInfo
.dwLength
=
1190 This
->ckData
.cksize
/ This
->lpFormat
->nBlockAlign
;
1191 This
->sInfo
.dwSuggestedBufferSize
= This
->sInfo
.dwLength
;
1192 This
->sInfo
.dwSampleSize
= This
->lpFormat
->nBlockAlign
;
1194 This
->fInfo
.dwStreams
= 1;
1195 This
->fInfo
.dwScale
= 1;
1196 This
->fInfo
.dwRate
= This
->lpFormat
->nSamplesPerSec
;
1197 This
->fInfo
.dwLength
=
1198 MulDiv(This
->ckData
.cksize
, This
->lpFormat
->nSamplesPerSec
,
1199 This
->lpFormat
->nAvgBytesPerSec
);
1204 static HRESULT
AVIFILE_SaveFile(const IAVIFileImpl
*This
)
1209 mmioSeek(This
->hmmio
, 0, SEEK_SET
);
1211 /* create the RIFF chunk with formtype WAVE */
1212 ckRIFF
.fccType
= formtypeWAVE
;
1214 if (mmioCreateChunk(This
->hmmio
, &ckRIFF
, MMIO_CREATERIFF
) != S_OK
)
1215 return AVIERR_FILEWRITE
;
1217 /* the next chunk is the format */
1218 ck
.ckid
= ckidWAVEFORMAT
;
1219 ck
.cksize
= This
->cbFormat
;
1220 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
1221 return AVIERR_FILEWRITE
;
1222 if (This
->lpFormat
!= NULL
&& This
->cbFormat
> 0) {
1223 if (mmioWrite(This
->hmmio
, (HPSTR
)This
->lpFormat
, ck
.cksize
) != ck
.cksize
)
1224 return AVIERR_FILEWRITE
;
1226 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1227 return AVIERR_FILEWRITE
;
1229 /* fact chunk is needed for non-pcm waveforms */
1230 if (This
->lpFormat
!= NULL
&& This
->cbFormat
> sizeof(PCMWAVEFORMAT
) &&
1231 This
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
) {
1236 /* try to open an appropriate audio codec to figure out
1237 * data for fact-chunk */
1238 wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
1239 if (acmFormatSuggest(NULL
, This
->lpFormat
, &wfx
,
1240 sizeof(wfx
), ACM_FORMATSUGGESTF_WFORMATTAG
)) {
1241 acmStreamOpen(&has
, NULL
, This
->lpFormat
, &wfx
, NULL
,
1242 0, 0, ACM_STREAMOPENF_NONREALTIME
);
1243 acmStreamSize(has
, This
->ckData
.cksize
, &dwFactLength
,
1244 ACM_STREAMSIZEF_SOURCE
);
1245 dwFactLength
/= wfx
.nBlockAlign
;
1246 acmStreamClose(has
, 0);
1248 /* create the fact chunk */
1249 ck
.ckid
= ckidWAVEFACT
;
1250 ck
.cksize
= sizeof(dwFactLength
);
1252 /* test for enough space before data chunk */
1253 if (mmioSeek(This
->hmmio
, 0, SEEK_CUR
) > This
->ckData
.dwDataOffset
1254 - ck
.cksize
- 4 * sizeof(DWORD
))
1255 return AVIERR_FILEWRITE
;
1256 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
1257 return AVIERR_FILEWRITE
;
1258 if (mmioWrite(This
->hmmio
, (HPSTR
)&dwFactLength
, ck
.cksize
) != ck
.cksize
)
1259 return AVIERR_FILEWRITE
;
1260 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1261 return AVIERR_FILEWRITE
;
1263 ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n");
1266 /* if there was extra stuff, we need to fill it with JUNK */
1267 if (mmioSeek(This
->hmmio
, 0, SEEK_CUR
) + 2 * sizeof(DWORD
) < This
->ckData
.dwDataOffset
) {
1268 ck
.ckid
= ckidAVIPADDING
;
1270 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
1271 return AVIERR_FILEWRITE
;
1273 if (mmioSeek(This
->hmmio
, This
->ckData
.dwDataOffset
1274 - 2 * sizeof(DWORD
), SEEK_SET
) == -1)
1275 return AVIERR_FILEWRITE
;
1276 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1277 return AVIERR_FILEWRITE
;
1280 /* create the data chunk */
1281 ck
.ckid
= ckidWAVEDATA
;
1282 ck
.cksize
= This
->ckData
.cksize
;
1283 if (mmioCreateChunk(This
->hmmio
, &ck
, 0) != S_OK
)
1284 return AVIERR_FILEWRITE
;
1285 if (mmioSeek(This
->hmmio
, This
->ckData
.cksize
, SEEK_CUR
) == -1)
1286 return AVIERR_FILEWRITE
;
1287 if (mmioAscend(This
->hmmio
, &ck
, 0) != S_OK
)
1288 return AVIERR_FILEWRITE
;
1290 /* some optional extra chunks? */
1291 if (This
->extra
.lp
!= NULL
&& This
->extra
.cb
> 0) {
1292 /* chunk headers are already in structure */
1293 if (mmioWrite(This
->hmmio
, This
->extra
.lp
, This
->extra
.cb
) != This
->extra
.cb
)
1294 return AVIERR_FILEWRITE
;
1297 /* close RIFF chunk */
1298 if (mmioAscend(This
->hmmio
, &ckRIFF
, 0) != S_OK
)
1299 return AVIERR_FILEWRITE
;
1300 if (mmioFlush(This
->hmmio
, 0) != S_OK
)
1301 return AVIERR_FILEWRITE
;