| /* |
| * Copyright 2002 Michael Günnewig |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #define COBJMACROS |
| #include <assert.h> |
| #include <stdarg.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "winnls.h" |
| #include "winerror.h" |
| #include "mmsystem.h" |
| #include "vfw.h" |
| #include "msacm.h" |
| |
| #include "avifile_private.h" |
| #include "extrachunk.h" |
| |
| #include "wine/unicode.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(avifile); |
| |
| /***********************************************************************/ |
| |
| #define formtypeWAVE mmioFOURCC('W','A','V','E') |
| #define ckidWAVEFORMAT mmioFOURCC('f','m','t',' ') |
| #define ckidWAVEFACT mmioFOURCC('f','a','c','t') |
| #define ckidWAVEDATA mmioFOURCC('d','a','t','a') |
| |
| /***********************************************************************/ |
| |
| #define ENDIAN_SWAPWORD(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) |
| #define ENDIAN_SWAPDWORD(x) (ENDIAN_SWAPWORD((x >> 16) & 0xFFFF) | \ |
| ENDIAN_SWAPWORD(x & 0xFFFF) << 16) |
| |
| #ifdef WORDS_BIGENDIAN |
| #define BE2H_WORD(x) (x) |
| #define BE2H_DWORD(x) (x) |
| #define LE2H_WORD(x) ENDIAN_SWAPWORD(x) |
| #define LE2H_DWORD(x) ENDIAN_SWAPDWORD(x) |
| #else |
| #define BE2H_WORD(x) ENDIAN_SWAPWORD(x) |
| #define BE2H_DWORD(x) ENDIAN_SWAPDWORD(x) |
| #define LE2H_WORD(x) (x) |
| #define LE2H_DWORD(x) (x) |
| #endif |
| |
| typedef struct { |
| FOURCC fccType; |
| DWORD offset; |
| DWORD size; |
| INT encoding; |
| DWORD sampleRate; |
| DWORD channels; |
| } SUNAUDIOHEADER; |
| |
| #define AU_ENCODING_ULAW_8 1 |
| #define AU_ENCODING_PCM_8 2 |
| #define AU_ENCODING_PCM_16 3 |
| #define AU_ENCODING_PCM_24 4 |
| #define AU_ENCODING_PCM_32 5 |
| #define AU_ENCODING_FLOAT 6 |
| #define AU_ENCODING_DOUBLE 7 |
| #define AU_ENCODING_ADPCM_G721_32 23 |
| #define AU_ENCODING_ADPCM_G722 24 |
| #define AU_ENCODING_ADPCM_G723_24 25 |
| #define AU_ENCODING_ADPCM_G723_5 26 |
| #define AU_ENCODING_ALAW_8 27 |
| |
| /***********************************************************************/ |
| |
| typedef struct _IAVIFileImpl { |
| IUnknown IUnknown_inner; |
| IAVIFile IAVIFile_iface; |
| IPersistFile IPersistFile_iface; |
| IAVIStream IAVIStream_iface; |
| IUnknown *outer_unk; |
| LONG ref; |
| /* IAVIFile, IAVIStream stuff... */ |
| AVIFILEINFOW fInfo; |
| AVISTREAMINFOW sInfo; |
| |
| LPWAVEFORMATEX lpFormat; |
| LONG cbFormat; |
| |
| MMCKINFO ckData; |
| |
| EXTRACHUNKS extra; |
| |
| /* IPersistFile stuff ... */ |
| HMMIO hmmio; |
| LPWSTR szFileName; |
| UINT uMode; |
| BOOL fDirty; |
| } IAVIFileImpl; |
| |
| /***********************************************************************/ |
| |
| static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); |
| static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This); |
| static HRESULT AVIFILE_SaveFile(const IAVIFileImpl *This); |
| |
| static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface) |
| { |
| return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner); |
| } |
| |
| static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ret_iface) |
| { |
| IAVIFileImpl *This = impl_from_IUnknown(iface); |
| |
| TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ret_iface); |
| |
| if (IsEqualGUID(&IID_IUnknown, riid)) |
| *ret_iface = &This->IUnknown_inner; |
| else if (IsEqualGUID(&IID_IAVIFile, riid)) |
| *ret_iface = &This->IAVIFile_iface; |
| else if (IsEqualGUID(&IID_IAVIStream, riid)) |
| *ret_iface = &This->IAVIStream_iface; |
| else if (IsEqualGUID(&IID_IPersistFile, riid)) |
| *ret_iface = &This->IPersistFile_iface; |
| else { |
| WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ret_iface); |
| *ret_iface = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| /* Violation of the COM aggregation ref counting rule */ |
| IUnknown_AddRef(&This->IUnknown_inner); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface) |
| { |
| IAVIFileImpl *This = impl_from_IUnknown(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) ref=%d\n", This, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface) |
| { |
| IAVIFileImpl *This = impl_from_IUnknown(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) ref=%d\n", This, ref); |
| |
| if (!ref) { |
| /* need to write headers to file */ |
| if (This->fDirty) |
| AVIFILE_SaveFile(This); |
| |
| HeapFree(GetProcessHeap(), 0, This->lpFormat); |
| This->lpFormat = NULL; |
| This->cbFormat = 0; |
| HeapFree(GetProcessHeap(), 0, This->extra.lp); |
| This->extra.lp = NULL; |
| This->extra.cb = 0; |
| HeapFree(GetProcessHeap(), 0, This->szFileName); |
| This->szFileName = NULL; |
| if (This->hmmio) { |
| mmioClose(This->hmmio, 0); |
| This->hmmio = NULL; |
| } |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static const IUnknownVtbl unk_vtbl = |
| { |
| IUnknown_fnQueryInterface, |
| IUnknown_fnAddRef, |
| IUnknown_fnRelease |
| }; |
| |
| static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface) |
| { |
| return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface); |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ret_iface) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); |
| } |
| |
| static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| return IUnknown_AddRef(This->outer_unk); |
| } |
| |
| static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| return IUnknown_Release(This->outer_unk); |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| TRACE("(%p,%p,%d)\n",iface,afi,size); |
| |
| if (afi == NULL) |
| return AVIERR_BADPARAM; |
| if (size < 0) |
| return AVIERR_BADSIZE; |
| |
| /* update file info */ |
| This->fInfo.dwFlags = 0; |
| This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; |
| if (This->lpFormat != NULL) { |
| assert(This->sInfo.dwScale != 0); |
| |
| This->fInfo.dwStreams = 1; |
| This->fInfo.dwScale = This->sInfo.dwScale; |
| This->fInfo.dwRate = This->sInfo.dwRate; |
| This->fInfo.dwLength = This->sInfo.dwLength; |
| This->fInfo.dwSuggestedBufferSize = This->ckData.cksize; |
| This->fInfo.dwMaxBytesPerSec = |
| MulDiv(This->sInfo.dwSampleSize,This->sInfo.dwRate,This->sInfo.dwScale); |
| } |
| |
| memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); |
| |
| if ((DWORD)size < sizeof(This->fInfo)) |
| return AVIERR_BUFFERTOOSMALL; |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType, |
| LONG lParam) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam); |
| |
| /* check parameter */ |
| if (avis == NULL) |
| return AVIERR_BADPARAM; |
| |
| *avis = NULL; |
| |
| /* Does our stream exists? */ |
| if (lParam != 0 || This->fInfo.dwStreams == 0) |
| return AVIERR_NODATA; |
| if (fccType != 0 && fccType != streamtypeAUDIO) |
| return AVIERR_NODATA; |
| |
| *avis = &This->IAVIStream_iface; |
| IAVIStream_AddRef(*avis); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis, |
| AVISTREAMINFOW *asi) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| TRACE("(%p,%p,%p)\n", iface, avis, asi); |
| |
| /* check parameters */ |
| if (avis == NULL || asi == NULL) |
| return AVIERR_BADPARAM; |
| |
| *avis = NULL; |
| |
| /* We only support one audio stream */ |
| if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL) |
| return AVIERR_UNSUPPORTED; |
| if (asi->fccType != streamtypeAUDIO) |
| return AVIERR_UNSUPPORTED; |
| |
| /* Does the user have write permission? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| This->cbFormat = 0; |
| This->lpFormat = NULL; |
| |
| memcpy(&This->sInfo, asi, sizeof(This->sInfo)); |
| |
| /* make sure streaminfo if okay for us */ |
| This->sInfo.fccHandler = 0; |
| This->sInfo.dwFlags = 0; |
| This->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; |
| This->sInfo.dwStart = 0; |
| This->sInfo.dwInitialFrames = 0; |
| This->sInfo.dwFormatChangeCount = 0; |
| SetRectEmpty(&This->sInfo.rcFrame); |
| |
| This->fInfo.dwStreams = 1; |
| This->fInfo.dwScale = This->sInfo.dwScale; |
| This->fInfo.dwRate = This->sInfo.dwRate; |
| This->fInfo.dwLength = This->sInfo.dwLength; |
| |
| This->ckData.dwDataOffset = 0; |
| This->ckData.cksize = 0; |
| |
| *avis = &This->IAVIStream_iface; |
| IAVIStream_AddRef(*avis); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size); |
| |
| /* check parameters */ |
| if (lpData == NULL) |
| return AVIERR_BADPARAM; |
| if (size < 0) |
| return AVIERR_BADSIZE; |
| |
| /* Do we have write permission? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| This->fDirty = TRUE; |
| |
| return WriteExtraChunk(&This->extra, ckid, lpData, size); |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size); |
| |
| return ReadExtraChunk(&This->extra, ckid, lpData, size); |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) |
| { |
| TRACE("(%p)\n",iface); |
| |
| /* This is only needed for interleaved files. |
| * We have only one stream, which can't be interleaved. |
| */ |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam) |
| { |
| IAVIFileImpl *This = impl_from_IAVIFile(iface); |
| |
| TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam); |
| |
| /* check parameter */ |
| if (lParam < 0) |
| return AVIERR_BADPARAM; |
| |
| /* Do we have our audio stream? */ |
| if (lParam != 0 || This->fInfo.dwStreams == 0 || |
| (fccType != 0 && fccType != streamtypeAUDIO)) |
| return AVIERR_NODATA; |
| |
| /* Have user write permissions? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| HeapFree(GetProcessHeap(), 0, This->lpFormat); |
| This->lpFormat = NULL; |
| This->cbFormat = 0; |
| |
| /* update infos */ |
| This->ckData.dwDataOffset = 0; |
| This->ckData.cksize = 0; |
| |
| This->sInfo.dwScale = 0; |
| This->sInfo.dwRate = 0; |
| This->sInfo.dwLength = 0; |
| This->sInfo.dwSuggestedBufferSize = 0; |
| |
| This->fInfo.dwStreams = 0; |
| This->fInfo.dwEditCount++; |
| |
| This->fDirty = TRUE; |
| |
| return AVIERR_OK; |
| } |
| |
| static const struct IAVIFileVtbl iwavft = { |
| IAVIFile_fnQueryInterface, |
| IAVIFile_fnAddRef, |
| IAVIFile_fnRelease, |
| IAVIFile_fnInfo, |
| IAVIFile_fnGetStream, |
| IAVIFile_fnCreateStream, |
| IAVIFile_fnWriteData, |
| IAVIFile_fnReadData, |
| IAVIFile_fnEndRecord, |
| IAVIFile_fnDeleteStream |
| }; |
| |
| /***********************************************************************/ |
| |
| static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface) |
| { |
| return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface); |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, |
| void **ret_iface) |
| { |
| IAVIFileImpl *This = impl_from_IPersistFile(iface); |
| |
| return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); |
| } |
| |
| static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) |
| { |
| IAVIFileImpl *This = impl_from_IPersistFile(iface); |
| |
| return IUnknown_AddRef(This->outer_unk); |
| } |
| |
| static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) |
| { |
| IAVIFileImpl *This = impl_from_IPersistFile(iface); |
| |
| return IUnknown_Release(This->outer_unk); |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, |
| LPCLSID pClassID) |
| { |
| TRACE("(%p,%p)\n", iface, pClassID); |
| |
| if (pClassID == NULL) |
| return AVIERR_BADPARAM; |
| |
| *pClassID = CLSID_WAVFile; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) |
| { |
| IAVIFileImpl *This = impl_from_IPersistFile(iface); |
| |
| TRACE("(%p)\n", iface); |
| |
| return (This->fDirty ? S_OK : S_FALSE); |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode) |
| { |
| IAVIFileImpl *This = impl_from_IPersistFile(iface); |
| WCHAR wszStreamFmt[50]; |
| INT len; |
| |
| TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode); |
| |
| /* check parameter */ |
| if (pszFileName == NULL) |
| return AVIERR_BADPARAM; |
| |
| if (This->hmmio != NULL) |
| return AVIERR_ERROR; /* No reuse of this object for another file! */ |
| |
| /* remember mode and name */ |
| This->uMode = dwMode; |
| |
| len = lstrlenW(pszFileName) + 1; |
| This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); |
| if (This->szFileName == NULL) |
| return AVIERR_MEMORY; |
| lstrcpyW(This->szFileName, pszFileName); |
| |
| /* try to open the file */ |
| This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode); |
| if (This->hmmio == NULL) { |
| /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ |
| LPSTR szFileName; |
| len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, |
| NULL, 0, NULL, NULL); |
| szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR)); |
| if (szFileName == NULL) |
| return AVIERR_MEMORY; |
| |
| WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, |
| len, NULL, NULL); |
| |
| This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); |
| HeapFree(GetProcessHeap(), 0, szFileName); |
| if (This->hmmio == NULL) |
| return AVIERR_FILEOPEN; |
| } |
| |
| memset(& This->fInfo, 0, sizeof(This->fInfo)); |
| memset(& This->sInfo, 0, sizeof(This->sInfo)); |
| |
| LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType, |
| sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0])); |
| if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT, |
| wszStreamFmt, sizeof(wszStreamFmt)/sizeof(wszStreamFmt[0])) > 0) { |
| wsprintfW(This->sInfo.szName, wszStreamFmt, |
| AVIFILE_BasenameW(This->szFileName)); |
| } |
| |
| /* should we create a new file? */ |
| if (dwMode & OF_CREATE) { |
| /* nothing more to do */ |
| return AVIERR_OK; |
| } else |
| return AVIFILE_LoadFile(This); |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, |
| LPCOLESTR pszFileName,BOOL fRemember) |
| { |
| TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); |
| |
| /* We write directly to disk, so nothing to do. */ |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, |
| LPCOLESTR pszFileName) |
| { |
| TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); |
| |
| /* We write directly to disk, so nothing to do. */ |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName) |
| { |
| IAVIFileImpl *This = impl_from_IPersistFile(iface); |
| |
| TRACE("(%p,%p)\n", iface, ppszFileName); |
| |
| if (ppszFileName == NULL) |
| return AVIERR_BADPARAM; |
| |
| *ppszFileName = NULL; |
| |
| if (This->szFileName) { |
| int len = lstrlenW(This->szFileName) + 1; |
| |
| *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR)); |
| if (*ppszFileName == NULL) |
| return AVIERR_MEMORY; |
| |
| strcpyW(*ppszFileName, This->szFileName); |
| } |
| |
| return AVIERR_OK; |
| } |
| |
| static const struct IPersistFileVtbl iwavpft = { |
| IPersistFile_fnQueryInterface, |
| IPersistFile_fnAddRef, |
| IPersistFile_fnRelease, |
| IPersistFile_fnGetClassID, |
| IPersistFile_fnIsDirty, |
| IPersistFile_fnLoad, |
| IPersistFile_fnSave, |
| IPersistFile_fnSaveCompleted, |
| IPersistFile_fnGetCurFile |
| }; |
| |
| /***********************************************************************/ |
| |
| static inline IAVIFileImpl *impl_from_IAVIStream(IAVIStream *iface) |
| { |
| return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIStream_iface); |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ret_iface) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); |
| } |
| |
| static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| return IUnknown_AddRef(This->outer_unk); |
| } |
| |
| static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| return IUnknown_Release(This->outer_unk); |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, |
| LPARAM lParam2) |
| { |
| TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); |
| |
| /* This IAVIStream interface needs an WAVFile */ |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%p,%d)\n", iface, psi, size); |
| |
| if (psi == NULL) |
| return AVIERR_BADPARAM; |
| if (size < 0) |
| return AVIERR_BADSIZE; |
| |
| memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); |
| |
| if ((DWORD)size < sizeof(This->sInfo)) |
| return AVIERR_BUFFERTOOSMALL; |
| return AVIERR_OK; |
| } |
| |
| static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); |
| |
| /* Do we have data? */ |
| if (This->lpFormat == NULL) |
| return -1; |
| |
| /* We don't have an index */ |
| if (flags & FIND_INDEX) |
| return -1; |
| |
| if (flags & FIND_FROM_START) { |
| pos = This->sInfo.dwStart; |
| flags &= ~(FIND_FROM_START|FIND_PREV); |
| flags |= FIND_NEXT; |
| } |
| |
| if (flags & FIND_FORMAT) { |
| if ((flags & FIND_NEXT) && pos > 0) |
| pos = -1; |
| else |
| pos = 0; |
| } |
| |
| if ((flags & FIND_RET) == FIND_LENGTH || |
| (flags & FIND_RET) == FIND_SIZE) |
| return This->sInfo.dwSampleSize; |
| if ((flags & FIND_RET) == FIND_OFFSET) |
| return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize; |
| |
| return pos; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format, |
| LONG *formatsize) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); |
| |
| if (formatsize == NULL) |
| return AVIERR_BADPARAM; |
| |
| /* only interested in needed buffersize? */ |
| if (format == NULL || *formatsize <= 0) { |
| *formatsize = This->cbFormat; |
| |
| return AVIERR_OK; |
| } |
| |
| /* copy initial format (only as much as will fit) */ |
| memcpy(format, This->lpFormat, min(*formatsize, This->cbFormat)); |
| if (*formatsize < This->cbFormat) { |
| *formatsize = This->cbFormat; |
| return AVIERR_BUFFERTOOSMALL; |
| } |
| |
| *formatsize = This->cbFormat; |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format, |
| LONG formatsize) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); |
| |
| /* check parameters */ |
| if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT)) |
| return AVIERR_BADPARAM; |
| |
| /* We can only do this to an empty wave file, but ignore call |
| * if still same format */ |
| if (This->lpFormat != NULL) { |
| if (formatsize != This->cbFormat || |
| memcmp(format, This->lpFormat, formatsize) != 0) |
| return AVIERR_UNSUPPORTED; |
| |
| return AVIERR_OK; |
| } |
| |
| /* only support start at position 0 */ |
| if (pos != 0) |
| return AVIERR_UNSUPPORTED; |
| |
| /* Do we have write permission? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| /* get memory for format and copy it */ |
| This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); |
| if (This->lpFormat == NULL) |
| return AVIERR_MEMORY; |
| |
| This->cbFormat = formatsize; |
| memcpy(This->lpFormat, format, formatsize); |
| |
| /* update info's about 'data' chunk */ |
| This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD); |
| This->ckData.cksize = 0; |
| |
| /* for non-pcm format we need also a 'fact' chunk */ |
| if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) |
| This->ckData.dwDataOffset += 3 * sizeof(DWORD); |
| |
| /* update stream and file info */ |
| This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; |
| This->sInfo.dwScale = This->lpFormat->nBlockAlign; |
| This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; |
| This->sInfo.dwLength = 0; |
| This->sInfo.dwSuggestedBufferSize = 0; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer, |
| LONG buffersize, LONG *bytesread, LONG *samplesread) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, |
| buffersize, bytesread, samplesread); |
| |
| /* clear return parameters if given */ |
| if (bytesread != NULL) |
| *bytesread = 0; |
| if (samplesread != NULL) |
| *samplesread = 0; |
| |
| /* positions without data */ |
| if (start < 0 || (DWORD)start > This->sInfo.dwLength) |
| return AVIERR_OK; |
| |
| /* check samples */ |
| if (samples < 0) |
| samples = 0; |
| if (buffersize > 0) { |
| if (samples > 0) |
| samples = min((DWORD)samples, buffersize / This->sInfo.dwSampleSize); |
| else |
| samples = buffersize / This->sInfo.dwSampleSize; |
| } |
| |
| /* limit to end of stream */ |
| if ((DWORD)(start + samples) > This->sInfo.dwLength) |
| samples = This->sInfo.dwLength - start; |
| |
| /* request only the sizes? */ |
| if (buffer == NULL || buffersize <= 0) { |
| /* then I need at least one parameter for it */ |
| if (bytesread == NULL && samplesread == NULL) |
| return AVIERR_BADPARAM; |
| |
| if (bytesread != NULL) |
| *bytesread = samples * This->sInfo.dwSampleSize; |
| if (samplesread != NULL) |
| *samplesread = samples; |
| |
| return AVIERR_OK; |
| } |
| |
| /* nothing to read? */ |
| if (samples == 0) |
| return AVIERR_OK; |
| |
| /* Can I read at least one sample? */ |
| if ((DWORD)buffersize < This->sInfo.dwSampleSize) |
| return AVIERR_BUFFERTOOSMALL; |
| |
| buffersize = samples * This->sInfo.dwSampleSize; |
| |
| if (mmioSeek(This->hmmio, This->ckData.dwDataOffset |
| + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) |
| return AVIERR_FILEREAD; |
| if (mmioRead(This->hmmio, buffer, buffersize) != buffersize) |
| return AVIERR_FILEREAD; |
| |
| /* fill out return parameters if given */ |
| if (bytesread != NULL) |
| *bytesread = buffersize; |
| if (samplesread != NULL) |
| *samplesread = samples; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer, |
| LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, |
| buffer, buffersize, flags, sampwritten, byteswritten); |
| |
| /* clear return parameters if given */ |
| if (sampwritten != NULL) |
| *sampwritten = 0; |
| if (byteswritten != NULL) |
| *byteswritten = 0; |
| |
| /* check parameters */ |
| if (buffer == NULL && (buffersize > 0 || samples > 0)) |
| return AVIERR_BADPARAM; |
| |
| /* Do we have write permission? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| /* < 0 means "append" */ |
| if (start < 0) |
| start = This->sInfo.dwStart + This->sInfo.dwLength; |
| |
| /* check buffersize -- must multiple of samplesize */ |
| if (buffersize & ~(This->sInfo.dwSampleSize - 1)) |
| return AVIERR_BADSIZE; |
| |
| /* do we have anything to write? */ |
| if (buffer != NULL && buffersize > 0) { |
| This->fDirty = TRUE; |
| |
| if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + |
| start * This->sInfo.dwSampleSize, SEEK_SET) == -1) |
| return AVIERR_FILEWRITE; |
| if (mmioWrite(This->hmmio, buffer, buffersize) != buffersize) |
| return AVIERR_FILEWRITE; |
| |
| This->sInfo.dwLength = max(This->sInfo.dwLength, (DWORD)start + samples); |
| This->ckData.cksize = max(This->ckData.cksize, |
| start * This->sInfo.dwSampleSize + buffersize); |
| |
| /* fill out return parameters if given */ |
| if (sampwritten != NULL) |
| *sampwritten = samples; |
| if (byteswritten != NULL) |
| *byteswritten = buffersize; |
| } |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| TRACE("(%p,%d,%d)\n", iface, start, samples); |
| |
| /* check parameters */ |
| if (start < 0 || samples < 0) |
| return AVIERR_BADPARAM; |
| |
| /* Delete before start of stream? */ |
| if ((DWORD)(start + samples) < This->sInfo.dwStart) |
| return AVIERR_OK; |
| |
| /* Delete after end of stream? */ |
| if ((DWORD)start > This->sInfo.dwLength) |
| return AVIERR_OK; |
| |
| /* For the rest we need write permissions */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| if ((DWORD)(start + samples) >= This->sInfo.dwLength) { |
| /* deletion at end */ |
| samples = This->sInfo.dwLength - start; |
| This->sInfo.dwLength -= samples; |
| This->ckData.cksize -= samples * This->sInfo.dwSampleSize; |
| } else if ((DWORD)start <= This->sInfo.dwStart) { |
| /* deletion at start */ |
| samples = This->sInfo.dwStart - start; |
| start = This->sInfo.dwStart; |
| This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize; |
| This->ckData.cksize -= samples * This->sInfo.dwSampleSize; |
| } else { |
| /* deletion inside stream -- needs playlist and cue's */ |
| FIXME(": deletion inside of stream not supported!\n"); |
| |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| This->fDirty = TRUE; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| return IAVIFile_ReadData(&This->IAVIFile_iface, fcc, lp, lpread); |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size) |
| { |
| IAVIFileImpl *This = impl_from_IAVIStream(iface); |
| |
| return IAVIFile_WriteData(&This->IAVIFile_iface, fcc, lp, size); |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, |
| LPAVISTREAMINFOW info, LONG infolen) |
| { |
| FIXME("(%p,%p,%d): stub\n", iface, info, infolen); |
| |
| return E_FAIL; |
| } |
| |
| static const struct IAVIStreamVtbl iwavst = { |
| IAVIStream_fnQueryInterface, |
| IAVIStream_fnAddRef, |
| IAVIStream_fnRelease, |
| IAVIStream_fnCreate, |
| IAVIStream_fnInfo, |
| IAVIStream_fnFindSample, |
| IAVIStream_fnReadFormat, |
| IAVIStream_fnSetFormat, |
| IAVIStream_fnRead, |
| IAVIStream_fnWrite, |
| IAVIStream_fnDelete, |
| IAVIStream_fnReadData, |
| IAVIStream_fnWriteData, |
| IAVIStream_fnSetInfo |
| }; |
| |
| HRESULT AVIFILE_CreateWAVFile(IUnknown *outer_unk, REFIID riid, void **ret_iface) |
| { |
| IAVIFileImpl *pfile; |
| HRESULT hr; |
| |
| *ret_iface = NULL; |
| |
| pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pfile)); |
| if (!pfile) |
| return AVIERR_MEMORY; |
| |
| pfile->IUnknown_inner.lpVtbl = &unk_vtbl; |
| pfile->IAVIFile_iface.lpVtbl = &iwavft; |
| pfile->IPersistFile_iface.lpVtbl = &iwavpft; |
| pfile->IAVIStream_iface.lpVtbl = &iwavst; |
| pfile->ref = 1; |
| if (outer_unk) |
| pfile->outer_unk = outer_unk; |
| else |
| pfile->outer_unk = &pfile->IUnknown_inner; |
| |
| hr = IUnknown_QueryInterface(&pfile->IUnknown_inner, riid, ret_iface); |
| IUnknown_Release(&pfile->IUnknown_inner); |
| |
| return hr; |
| } |
| |
| /***********************************************************************/ |
| |
| static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) |
| { |
| MMCKINFO ckRIFF; |
| MMCKINFO ck; |
| |
| This->sInfo.dwLength = 0; /* just to be sure */ |
| This->fDirty = FALSE; |
| |
| /* search for RIFF chunk */ |
| ckRIFF.fccType = 0; /* find any */ |
| if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { |
| return AVIFILE_LoadSunFile(This); |
| } |
| |
| if (ckRIFF.fccType != formtypeWAVE) |
| return AVIERR_BADFORMAT; |
| |
| /* search WAVE format chunk */ |
| ck.ckid = ckidWAVEFORMAT; |
| if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, |
| &ckRIFF, MMIO_FINDCHUNK) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| /* get memory for format and read it */ |
| This->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize); |
| if (This->lpFormat == NULL) |
| return AVIERR_FILEREAD; |
| This->cbFormat = ck.cksize; |
| |
| if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) |
| return AVIERR_FILEREAD; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| /* Non-pcm formats have a fact chunk. |
| * We don't need it, so simply add it to the extra chunks. |
| */ |
| |
| /* find the big data chunk */ |
| This->ckData.ckid = ckidWAVEDATA; |
| if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData, |
| &ckRIFF, MMIO_FINDCHUNK) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| memset(&This->sInfo, 0, sizeof(This->sInfo)); |
| This->sInfo.fccType = streamtypeAUDIO; |
| This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; |
| This->sInfo.dwSampleSize = |
| This->sInfo.dwScale = This->lpFormat->nBlockAlign; |
| This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; |
| This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; |
| |
| This->fInfo.dwStreams = 1; |
| |
| if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) { |
| /* seems to be truncated */ |
| WARN(": file seems to be truncated!\n"); |
| This->ckData.cksize = mmioSeek(This->hmmio, 0, SEEK_END) - |
| This->ckData.dwDataOffset; |
| This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; |
| This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; |
| } |
| |
| /* ignore errors */ |
| FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This) |
| { |
| SUNAUDIOHEADER auhdr; |
| |
| mmioSeek(This->hmmio, 0, SEEK_SET); |
| if (mmioRead(This->hmmio, (HPSTR)&auhdr, sizeof(auhdr)) != sizeof(auhdr)) |
| return AVIERR_FILEREAD; |
| |
| if (auhdr.fccType == 0x0064732E) { |
| /* header in little endian */ |
| This->ckData.dwDataOffset = LE2H_DWORD(auhdr.offset); |
| This->ckData.cksize = LE2H_DWORD(auhdr.size); |
| |
| auhdr.encoding = LE2H_DWORD(auhdr.encoding); |
| auhdr.sampleRate = LE2H_DWORD(auhdr.sampleRate); |
| auhdr.channels = LE2H_DWORD(auhdr.channels); |
| } else if (auhdr.fccType == mmioFOURCC('.','s','n','d')) { |
| /* header in big endian */ |
| This->ckData.dwDataOffset = BE2H_DWORD(auhdr.offset); |
| This->ckData.cksize = BE2H_DWORD(auhdr.size); |
| |
| auhdr.encoding = BE2H_DWORD(auhdr.encoding); |
| auhdr.sampleRate = BE2H_DWORD(auhdr.sampleRate); |
| auhdr.channels = BE2H_DWORD(auhdr.channels); |
| } else |
| return AVIERR_FILEREAD; |
| |
| if (auhdr.channels < 1) |
| return AVIERR_BADFORMAT; |
| |
| /* get size of header */ |
| switch(auhdr.encoding) { |
| case AU_ENCODING_ADPCM_G721_32: |
| This->cbFormat = sizeof(G721_ADPCMWAVEFORMAT); break; |
| case AU_ENCODING_ADPCM_G723_24: |
| This->cbFormat = sizeof(G723_ADPCMWAVEFORMAT); break; |
| case AU_ENCODING_ADPCM_G722: |
| case AU_ENCODING_ADPCM_G723_5: |
| WARN("unsupported Sun audio format %d\n", auhdr.encoding); |
| return AVIERR_UNSUPPORTED; /* FIXME */ |
| default: |
| This->cbFormat = sizeof(WAVEFORMATEX); break; |
| }; |
| |
| This->lpFormat = HeapAlloc(GetProcessHeap(), 0, This->cbFormat); |
| if (This->lpFormat == NULL) |
| return AVIERR_MEMORY; |
| |
| This->lpFormat->nChannels = auhdr.channels; |
| This->lpFormat->nSamplesPerSec = auhdr.sampleRate; |
| switch(auhdr.encoding) { |
| case AU_ENCODING_ULAW_8: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_MULAW; |
| This->lpFormat->wBitsPerSample = 8; |
| break; |
| case AU_ENCODING_PCM_8: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; |
| This->lpFormat->wBitsPerSample = 8; |
| break; |
| case AU_ENCODING_PCM_16: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; |
| This->lpFormat->wBitsPerSample = 16; |
| break; |
| case AU_ENCODING_PCM_24: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; |
| This->lpFormat->wBitsPerSample = 24; |
| break; |
| case AU_ENCODING_PCM_32: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; |
| This->lpFormat->wBitsPerSample = 32; |
| break; |
| case AU_ENCODING_ALAW_8: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_ALAW; |
| This->lpFormat->wBitsPerSample = 8; |
| break; |
| case AU_ENCODING_ADPCM_G721_32: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_G721_ADPCM; |
| This->lpFormat->wBitsPerSample = (3*5*8); |
| This->lpFormat->nBlockAlign = 15*15*8; |
| This->lpFormat->cbSize = sizeof(WORD); |
| ((LPG721_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; |
| break; |
| case AU_ENCODING_ADPCM_G723_24: |
| This->lpFormat->wFormatTag = WAVE_FORMAT_G723_ADPCM; |
| This->lpFormat->wBitsPerSample = (3*5*8); |
| This->lpFormat->nBlockAlign = 15*15*8; |
| This->lpFormat->cbSize = 2*sizeof(WORD); |
| ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->cbExtraSize = 0; |
| ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; |
| break; |
| default: |
| WARN("unsupported Sun audio format %d\n", auhdr.encoding); |
| return AVIERR_UNSUPPORTED; |
| }; |
| |
| This->lpFormat->nBlockAlign = |
| (This->lpFormat->nChannels * This->lpFormat->wBitsPerSample) / 8; |
| if (This->lpFormat->nBlockAlign == 0 && This->lpFormat->wBitsPerSample < 8) |
| This->lpFormat->nBlockAlign++; |
| This->lpFormat->nAvgBytesPerSec = |
| This->lpFormat->nBlockAlign * This->lpFormat->nSamplesPerSec; |
| |
| This->fDirty = FALSE; |
| |
| This->sInfo.fccType = streamtypeAUDIO; |
| This->sInfo.fccHandler = 0; |
| This->sInfo.dwFlags = 0; |
| This->sInfo.wPriority = 0; |
| This->sInfo.wLanguage = 0; |
| This->sInfo.dwInitialFrames = 0; |
| This->sInfo.dwScale = This->lpFormat->nBlockAlign; |
| This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; |
| This->sInfo.dwStart = 0; |
| This->sInfo.dwLength = |
| This->ckData.cksize / This->lpFormat->nBlockAlign; |
| This->sInfo.dwSuggestedBufferSize = This->sInfo.dwLength; |
| This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; |
| |
| This->fInfo.dwStreams = 1; |
| This->fInfo.dwScale = 1; |
| This->fInfo.dwRate = This->lpFormat->nSamplesPerSec; |
| This->fInfo.dwLength = |
| MulDiv(This->ckData.cksize, This->lpFormat->nSamplesPerSec, |
| This->lpFormat->nAvgBytesPerSec); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT AVIFILE_SaveFile(const IAVIFileImpl *This) |
| { |
| MMCKINFO ckRIFF; |
| MMCKINFO ck; |
| |
| mmioSeek(This->hmmio, 0, SEEK_SET); |
| |
| /* create the RIFF chunk with formtype WAVE */ |
| ckRIFF.fccType = formtypeWAVE; |
| ckRIFF.cksize = 0; |
| if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* the next chunk is the format */ |
| ck.ckid = ckidWAVEFORMAT; |
| ck.cksize = This->cbFormat; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (This->lpFormat != NULL && This->cbFormat > 0) { |
| if (mmioWrite(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) |
| return AVIERR_FILEWRITE; |
| } |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* fact chunk is needed for non-pcm waveforms */ |
| if (This->lpFormat != NULL && This->cbFormat > sizeof(PCMWAVEFORMAT) && |
| This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) { |
| WAVEFORMATEX wfx; |
| DWORD dwFactLength; |
| HACMSTREAM has; |
| |
| /* try to open an appropriate audio codec to figure out |
| * data for fact-chunk */ |
| wfx.wFormatTag = WAVE_FORMAT_PCM; |
| if (acmFormatSuggest(NULL, This->lpFormat, &wfx, |
| sizeof(wfx), ACM_FORMATSUGGESTF_WFORMATTAG)) { |
| acmStreamOpen(&has, NULL, This->lpFormat, &wfx, NULL, |
| 0, 0, ACM_STREAMOPENF_NONREALTIME); |
| acmStreamSize(has, This->ckData.cksize, &dwFactLength, |
| ACM_STREAMSIZEF_SOURCE); |
| dwFactLength /= wfx.nBlockAlign; |
| acmStreamClose(has, 0); |
| |
| /* create the fact chunk */ |
| ck.ckid = ckidWAVEFACT; |
| ck.cksize = sizeof(dwFactLength); |
| |
| /* test for enough space before data chunk */ |
| if (mmioSeek(This->hmmio, 0, SEEK_CUR) > This->ckData.dwDataOffset |
| - ck.cksize - 4 * sizeof(DWORD)) |
| return AVIERR_FILEWRITE; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioWrite(This->hmmio, (HPSTR)&dwFactLength, ck.cksize) != ck.cksize) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| } else |
| ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n"); |
| } |
| |
| /* if there was extra stuff, we need to fill it with JUNK */ |
| if (mmioSeek(This->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) < This->ckData.dwDataOffset) { |
| ck.ckid = ckidAVIPADDING; |
| ck.cksize = 0; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| if (mmioSeek(This->hmmio, This->ckData.dwDataOffset |
| - 2 * sizeof(DWORD), SEEK_SET) == -1) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* create the data chunk */ |
| ck.ckid = ckidWAVEDATA; |
| ck.cksize = This->ckData.cksize; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioSeek(This->hmmio, This->ckData.cksize, SEEK_CUR) == -1) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* some optional extra chunks? */ |
| if (This->extra.lp != NULL && This->extra.cb > 0) { |
| /* chunk headers are already in structure */ |
| if (mmioWrite(This->hmmio, This->extra.lp, This->extra.cb) != This->extra.cb) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* close RIFF chunk */ |
| if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioFlush(This->hmmio, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| return AVIERR_OK; |
| } |