| /* |
| * Copyright 1999 Marcus Meissner |
| * Copyright 2002-2003 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 |
| */ |
| |
| /* TODO: |
| * - IAVIStreaming interface is missing for the IAVIStreamImpl |
| * - IAVIStream_fnFindSample: FIND_INDEX isn't supported. |
| * - IAVIStream_fnReadFormat: formatchanges aren't read in. |
| * - IAVIStream_fnDelete: a stub. |
| * - IAVIStream_fnSetInfo: a stub. |
| * - make thread safe |
| * |
| * KNOWN Bugs: |
| * - native version can hangup when reading a file generated with this DLL. |
| * When index is missing it works, but index seems to be okay. |
| */ |
| |
| #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 "avifile_private.h" |
| #include "extrachunk.h" |
| |
| #include "wine/unicode.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(avifile); |
| |
| #ifndef IDX_PER_BLOCK |
| #define IDX_PER_BLOCK 2730 |
| #endif |
| |
| /***********************************************************************/ |
| |
| static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); |
| static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface); |
| static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface); |
| static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); |
| static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); |
| static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); |
| static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); |
| static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); |
| static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface); |
| static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); |
| |
| static const struct IAVIFileVtbl iavift = { |
| IAVIFile_fnQueryInterface, |
| IAVIFile_fnAddRef, |
| IAVIFile_fnRelease, |
| IAVIFile_fnInfo, |
| IAVIFile_fnGetStream, |
| IAVIFile_fnCreateStream, |
| IAVIFile_fnWriteData, |
| IAVIFile_fnReadData, |
| IAVIFile_fnEndRecord, |
| IAVIFile_fnDeleteStream |
| }; |
| |
| static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj); |
| static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface); |
| static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface); |
| static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID); |
| static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface); |
| static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode); |
| static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember); |
| static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName); |
| static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName); |
| |
| static const struct IPersistFileVtbl ipersistft = { |
| IPersistFile_fnQueryInterface, |
| IPersistFile_fnAddRef, |
| IPersistFile_fnRelease, |
| IPersistFile_fnGetClassID, |
| IPersistFile_fnIsDirty, |
| IPersistFile_fnLoad, |
| IPersistFile_fnSave, |
| IPersistFile_fnSaveCompleted, |
| IPersistFile_fnGetCurFile |
| }; |
| |
| static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); |
| static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface); |
| static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface); |
| static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); |
| static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); |
| static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); |
| static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); |
| static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); |
| static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); |
| static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); |
| static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); |
| static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); |
| static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); |
| static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); |
| |
| static const struct IAVIStreamVtbl iavist = { |
| 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 |
| }; |
| |
| typedef struct _IAVIFileImpl IAVIFileImpl; |
| |
| typedef struct _IPersistFileImpl { |
| /* IUnknown stuff */ |
| const IPersistFileVtbl *lpVtbl; |
| |
| /* IPersistFile stuff */ |
| IAVIFileImpl *paf; |
| } IPersistFileImpl; |
| |
| typedef struct _IAVIStreamImpl { |
| /* IUnknown stuff */ |
| const IAVIStreamVtbl *lpVtbl; |
| LONG ref; |
| |
| /* IAVIStream stuff */ |
| IAVIFileImpl *paf; |
| DWORD nStream; /* the n-th stream in file */ |
| AVISTREAMINFOW sInfo; |
| |
| LPVOID lpFormat; |
| DWORD cbFormat; |
| |
| LPVOID lpHandlerData; |
| DWORD cbHandlerData; |
| |
| EXTRACHUNKS extra; |
| |
| LPDWORD lpBuffer; |
| DWORD cbBuffer; /* size of lpBuffer */ |
| DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */ |
| |
| LONG lLastFrame; /* last correct index in idxFrames */ |
| AVIINDEXENTRY *idxFrames; |
| DWORD nIdxFrames; /* upper index limit of idxFrames */ |
| AVIINDEXENTRY *idxFmtChanges; |
| DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */ |
| } IAVIStreamImpl; |
| |
| struct _IAVIFileImpl { |
| /* IUnknown stuff */ |
| const IAVIFileVtbl *lpVtbl; |
| LONG ref; |
| |
| /* IAVIFile stuff... */ |
| IPersistFileImpl iPersistFile; |
| |
| AVIFILEINFOW fInfo; |
| IAVIStreamImpl *ppStreams[MAX_AVISTREAMS]; |
| |
| EXTRACHUNKS fileextra; |
| |
| DWORD dwMoviChunkPos; /* some stuff for saving ... */ |
| DWORD dwIdxChunkPos; |
| DWORD dwNextFramePos; |
| DWORD dwInitialFrames; |
| |
| MMCKINFO ckLastRecord; |
| AVIINDEXENTRY *idxRecords; /* won't be updated while loading */ |
| DWORD nIdxRecords; /* current fill level */ |
| DWORD cbIdxRecords; /* size of idxRecords */ |
| |
| /* IPersistFile stuff ... */ |
| HMMIO hmmio; |
| LPWSTR szFileName; |
| UINT uMode; |
| BOOL fDirty; |
| }; |
| |
| /***********************************************************************/ |
| |
| static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, |
| DWORD offset, DWORD flags); |
| static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This); |
| static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This); |
| static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, |
| const AVISTREAMINFOW *asi); |
| static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This); |
| static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); |
| static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset); |
| static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, |
| LONG count, DWORD pos, BOOL *bAbsolute); |
| static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start, |
| LPVOID buffer, DWORD size); |
| static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, |
| LPLONG offset); |
| static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); |
| static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This); |
| static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType, |
| LONG lSkip); |
| static void AVIFILE_UpdateInfo(IAVIFileImpl *This); |
| static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, |
| FOURCC ckid, DWORD flags, LPCVOID buffer, |
| LONG size); |
| |
| HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv) |
| { |
| IAVIFileImpl *pfile; |
| HRESULT hr; |
| |
| assert(riid != NULL && ppv != NULL); |
| |
| *ppv = NULL; |
| |
| pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl)); |
| if (pfile == NULL) |
| return AVIERR_MEMORY; |
| |
| pfile->lpVtbl = &iavift; |
| pfile->ref = 0; |
| pfile->iPersistFile.lpVtbl = &ipersistft; |
| pfile->iPersistFile.paf = pfile; |
| |
| hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv); |
| if (FAILED(hr)) |
| HeapFree(GetProcessHeap(), 0, pfile); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, |
| LPVOID *obj) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); |
| |
| if (IsEqualGUID(&IID_IUnknown, refiid) || |
| IsEqualGUID(&IID_IAVIFile, refiid)) { |
| *obj = iface; |
| IAVIFile_AddRef(iface); |
| |
| return S_OK; |
| } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { |
| *obj = &This->iPersistFile; |
| IAVIFile_AddRef(iface); |
| |
| return S_OK; |
| } |
| |
| return OLE_E_ENUM_NOMORE; |
| } |
| |
| static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) -> %d\n", iface, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| UINT i; |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) -> %d\n", iface, ref); |
| |
| if (!ref) { |
| if (This->fDirty) { |
| /* need to write headers to file */ |
| AVIFILE_SaveFile(This); |
| } |
| |
| for (i = 0; i < This->fInfo.dwStreams; i++) { |
| if (This->ppStreams[i] != NULL) { |
| if (This->ppStreams[i]->ref != 0) { |
| ERR(": someone has still %u reference to stream %u (%p)!\n", |
| This->ppStreams[i]->ref, i, This->ppStreams[i]); |
| } |
| AVIFILE_DestructAVIStream(This->ppStreams[i]); |
| HeapFree(GetProcessHeap(), 0, This->ppStreams[i]); |
| This->ppStreams[i] = NULL; |
| } |
| } |
| |
| if (This->idxRecords != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->idxRecords); |
| This->idxRecords = NULL; |
| This->nIdxRecords = 0; |
| } |
| |
| if (This->fileextra.lp != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->fileextra.lp); |
| This->fileextra.lp = NULL; |
| This->fileextra.cb = 0; |
| } |
| |
| HeapFree(GetProcessHeap(), 0, This->szFileName); |
| This->szFileName = NULL; |
| |
| if (This->hmmio != NULL) { |
| mmioClose(This->hmmio, 0); |
| This->hmmio = NULL; |
| } |
| |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| return ref; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, |
| LONG size) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| TRACE("(%p,%p,%d)\n",iface,afi,size); |
| |
| if (afi == NULL) |
| return AVIERR_BADPARAM; |
| if (size < 0) |
| return AVIERR_BADSIZE; |
| |
| AVIFILE_UpdateInfo(This); |
| |
| 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, PAVISTREAM *avis, |
| DWORD fccType, LONG lParam) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| ULONG nStream; |
| |
| TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam); |
| |
| if (avis == NULL || lParam < 0) |
| return AVIERR_BADPARAM; |
| |
| nStream = AVIFILE_SearchStream(This, fccType, lParam); |
| |
| /* Does the requested stream exist? */ |
| if (nStream < This->fInfo.dwStreams && |
| This->ppStreams[nStream] != NULL) { |
| *avis = (PAVISTREAM)This->ppStreams[nStream]; |
| IAVIStream_AddRef(*avis); |
| |
| return AVIERR_OK; |
| } |
| |
| /* Sorry, but the specified stream doesn't exist */ |
| return AVIERR_NODATA; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, |
| LPAVISTREAMINFOW asi) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| DWORD n; |
| |
| TRACE("(%p,%p,%p)\n", iface, avis, asi); |
| |
| /* check parameters */ |
| if (avis == NULL || asi == NULL) |
| return AVIERR_BADPARAM; |
| |
| *avis = NULL; |
| |
| /* Does the user have write permission? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| /* Can we add another stream? */ |
| n = This->fInfo.dwStreams; |
| if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) { |
| /* already reached max nr of streams |
| * or have already written frames to disk */ |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| /* check AVISTREAMINFO for some really needed things */ |
| if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0) |
| return AVIERR_BADFORMAT; |
| |
| /* now it seems to be save to add the stream */ |
| assert(This->ppStreams[n] == NULL); |
| This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
| sizeof(IAVIStreamImpl)); |
| if (This->ppStreams[n] == NULL) |
| return AVIERR_MEMORY; |
| |
| /* initialize the new allocated stream */ |
| AVIFILE_ConstructAVIStream(This, n, asi); |
| |
| This->fInfo.dwStreams++; |
| This->fDirty = TRUE; |
| |
| /* update our AVIFILEINFO structure */ |
| AVIFILE_UpdateInfo(This); |
| |
| /* return it */ |
| *avis = (PAVISTREAM)This->ppStreams[n]; |
| IAVIStream_AddRef(*avis); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, |
| LPVOID lpData, LONG size) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)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->fileextra, ckid, lpData, size); |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, |
| LPVOID lpData, LONG *size) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size); |
| |
| return ReadExtraChunk(&This->fileextra, ckid, lpData, size); |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| TRACE("(%p)\n",iface); |
| |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| This->fDirty = TRUE; |
| |
| /* no frames written to any stream? -- compute start of 'movi'-chunk */ |
| if (This->dwMoviChunkPos == 0) |
| AVIFILE_ComputeMoviStart(This); |
| |
| This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED; |
| |
| /* already written frames to any stream, ... */ |
| if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { |
| /* close last record */ |
| if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0) |
| return AVIERR_FILEWRITE; |
| |
| AVIFILE_AddRecord(This); |
| |
| if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD)) |
| This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD); |
| } |
| |
| /* write out a new record into file, but don't close it */ |
| This->ckLastRecord.cksize = 0; |
| This->ckLastRecord.fccType = listtypeAVIRECORD; |
| if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) |
| return AVIERR_FILEWRITE; |
| if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0) |
| return AVIERR_FILEWRITE; |
| This->dwNextFramePos += 3 * sizeof(DWORD); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, |
| LONG lParam) |
| { |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; |
| |
| ULONG nStream; |
| |
| TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam); |
| |
| /* check parameter */ |
| if (lParam < 0) |
| return AVIERR_BADPARAM; |
| |
| /* Have user write permissions? */ |
| if ((This->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| nStream = AVIFILE_SearchStream(This, fccType, lParam); |
| |
| /* Does the requested stream exist? */ |
| if (nStream < This->fInfo.dwStreams && |
| This->ppStreams[nStream] != NULL) { |
| /* ... so delete it now */ |
| HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]); |
| |
| if (This->fInfo.dwStreams - nStream > 0) |
| memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1, |
| (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*)); |
| |
| This->ppStreams[This->fInfo.dwStreams] = NULL; |
| This->fInfo.dwStreams--; |
| This->fDirty = TRUE; |
| |
| /* This->fInfo will be updated further when asked for */ |
| return AVIERR_OK; |
| } else |
| return AVIERR_NODATA; |
| } |
| |
| /***********************************************************************/ |
| |
| static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, |
| REFIID refiid, LPVOID *obj) |
| { |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; |
| |
| assert(This->paf != NULL); |
| |
| return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); |
| } |
| |
| static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) |
| { |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; |
| |
| assert(This->paf != NULL); |
| |
| return IAVIFile_AddRef((PAVIFILE)This->paf); |
| } |
| |
| static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) |
| { |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; |
| |
| assert(This->paf != NULL); |
| |
| return IAVIFile_Release((PAVIFILE)This->paf); |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, |
| LPCLSID pClassID) |
| { |
| TRACE("(%p,%p)\n", iface, pClassID); |
| |
| if (pClassID == NULL) |
| return AVIERR_BADPARAM; |
| |
| *pClassID = CLSID_AVIFile; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) |
| { |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; |
| |
| TRACE("(%p)\n", iface); |
| |
| assert(This->paf != NULL); |
| |
| return (This->paf->fDirty ? S_OK : S_FALSE); |
| } |
| |
| static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, |
| LPCOLESTR pszFileName, DWORD dwMode) |
| { |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; |
| |
| int len; |
| |
| TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode); |
| |
| /* check parameter */ |
| if (pszFileName == NULL) |
| return AVIERR_BADPARAM; |
| |
| assert(This->paf != NULL); |
| if (This->paf->hmmio != NULL) |
| return AVIERR_ERROR; /* No reuse of this object for another file! */ |
| |
| /* remember mode and name */ |
| This->paf->uMode = dwMode; |
| |
| len = lstrlenW(pszFileName) + 1; |
| This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); |
| if (This->paf->szFileName == NULL) |
| return AVIERR_MEMORY; |
| lstrcpyW(This->paf->szFileName, pszFileName); |
| |
| /* try to open the file */ |
| This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL, |
| MMIO_ALLOCBUF | dwMode); |
| if (This->paf->hmmio == NULL) { |
| /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ |
| LPSTR szFileName; |
| |
| len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, |
| NULL, 0, NULL, NULL); |
| szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR)); |
| if (szFileName == NULL) |
| return AVIERR_MEMORY; |
| |
| WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName, |
| len, NULL, NULL); |
| |
| This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); |
| HeapFree(GetProcessHeap(), 0, szFileName); |
| if (This->paf->hmmio == NULL) |
| return AVIERR_FILEOPEN; |
| } |
| |
| /* should we create a new file? */ |
| if (dwMode & OF_CREATE) { |
| memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo)); |
| This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE; |
| |
| return AVIERR_OK; |
| } else |
| return AVIFILE_LoadFile(This->paf); |
| } |
| |
| 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) |
| { |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; |
| |
| TRACE("(%p,%p)\n", iface, ppszFileName); |
| |
| if (ppszFileName == NULL) |
| return AVIERR_BADPARAM; |
| |
| *ppszFileName = NULL; |
| |
| assert(This->paf != NULL); |
| |
| if (This->paf->szFileName != NULL) { |
| int len = lstrlenW(This->paf->szFileName) + 1; |
| |
| *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR)); |
| if (*ppszFileName == NULL) |
| return AVIERR_MEMORY; |
| |
| strcpyW(*ppszFileName, This->paf->szFileName); |
| } |
| |
| return AVIERR_OK; |
| } |
| |
| /***********************************************************************/ |
| |
| static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, |
| REFIID refiid, LPVOID *obj) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); |
| |
| if (IsEqualGUID(&IID_IUnknown, refiid) || |
| IsEqualGUID(&IID_IAVIStream, refiid)) { |
| *obj = This; |
| IAVIStream_AddRef(iface); |
| |
| return S_OK; |
| } |
| /* FIXME: IAVIStreaming interface */ |
| |
| return OLE_E_ENUM_NOMORE; |
| } |
| |
| static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) -> %d\n", iface, ref); |
| |
| /* also add ref to parent, so that it doesn't kill us */ |
| if (This->paf != NULL) |
| IAVIFile_AddRef((PAVIFILE)This->paf); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) -> %d\n", iface, ref); |
| |
| if (This->paf != NULL) |
| IAVIFile_Release((PAVIFILE)This->paf); |
| |
| return ref; |
| } |
| |
| 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 AVIFile */ |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, |
| LONG size) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)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) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| LONG offset = 0; |
| |
| TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); |
| |
| if (flags & FIND_FROM_START) { |
| pos = This->sInfo.dwStart; |
| flags &= ~(FIND_FROM_START|FIND_PREV); |
| flags |= FIND_NEXT; |
| } |
| |
| if (This->sInfo.dwSampleSize != 0) { |
| /* convert samples into block number with offset */ |
| AVIFILE_SamplesToBlock(This, &pos, &offset); |
| } |
| |
| if (flags & FIND_TYPE) { |
| if (flags & FIND_KEY) { |
| while (0 <= pos && pos <= This->lLastFrame) { |
| if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME) |
| goto RETURN_FOUND; |
| |
| if (flags & FIND_NEXT) |
| pos++; |
| else |
| pos--; |
| }; |
| } else if (flags & FIND_ANY) { |
| while (0 <= pos && pos <= This->lLastFrame) { |
| if (This->idxFrames[pos].dwChunkLength > 0) |
| goto RETURN_FOUND; |
| |
| if (flags & FIND_NEXT) |
| pos++; |
| else |
| pos--; |
| |
| }; |
| } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL && |
| This->sInfo.fccType == streamtypeVIDEO) { |
| if (flags & FIND_NEXT) { |
| ULONG n; |
| |
| for (n = 0; n < This->sInfo.dwFormatChangeCount; n++) |
| if (This->idxFmtChanges[n].ckid >= pos) { |
| pos = This->idxFmtChanges[n].ckid; |
| goto RETURN_FOUND; |
| } |
| } else { |
| LONG n; |
| |
| for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) { |
| if (This->idxFmtChanges[n].ckid <= pos) { |
| pos = This->idxFmtChanges[n].ckid; |
| goto RETURN_FOUND; |
| } |
| } |
| |
| if (pos > (LONG)This->sInfo.dwStart) |
| return 0; /* format changes always for first frame */ |
| } |
| } |
| |
| return -1; |
| } |
| |
| RETURN_FOUND: |
| if (pos < (LONG)This->sInfo.dwStart) |
| return -1; |
| |
| switch (flags & FIND_RET) { |
| case FIND_LENGTH: |
| /* physical size */ |
| pos = This->idxFrames[pos].dwChunkLength; |
| break; |
| case FIND_OFFSET: |
| /* physical position */ |
| pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD) |
| + offset * This->sInfo.dwSampleSize; |
| break; |
| case FIND_SIZE: |
| /* logical size */ |
| if (This->sInfo.dwSampleSize) |
| pos = This->sInfo.dwSampleSize; |
| else |
| pos = 1; |
| break; |
| case FIND_INDEX: |
| FIXME(": FIND_INDEX flag is not supported!\n"); |
| /* This is an index in the index-table on disc. */ |
| break; |
| }; /* else logical position */ |
| |
| return pos; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, |
| LPVOID format, LONG *formatsize) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)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(*(DWORD*)formatsize, This->cbFormat)); |
| if (*(DWORD*)formatsize < This->cbFormat) { |
| *formatsize = This->cbFormat; |
| return AVIERR_BUFFERTOOSMALL; |
| } |
| |
| /* Could format change? When yes will it change? */ |
| if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && |
| pos > This->sInfo.dwStart) { |
| LONG lLastFmt; |
| |
| lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV); |
| if (lLastFmt > 0) { |
| FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt); |
| } |
| } |
| |
| *formatsize = This->cbFormat; |
| return AVIERR_OK; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, |
| LPVOID format, LONG formatsize) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| LPBITMAPINFOHEADER lpbiNew = format; |
| |
| TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); |
| |
| /* check parameters */ |
| if (format == NULL || formatsize <= 0) |
| return AVIERR_BADPARAM; |
| |
| /* Do we have write permission? */ |
| if ((This->paf->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| /* can only set format before frame is written! */ |
| if (This->lLastFrame > pos) |
| return AVIERR_UNSUPPORTED; |
| |
| /* initial format or a formatchange? */ |
| if (This->lpFormat == NULL) { |
| /* initial format */ |
| if (This->paf->dwMoviChunkPos != 0) |
| return AVIERR_ERROR; /* user has used API in wrong sequence! */ |
| |
| This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); |
| if (This->lpFormat == NULL) |
| return AVIERR_MEMORY; |
| This->cbFormat = formatsize; |
| |
| memcpy(This->lpFormat, format, formatsize); |
| |
| /* update some infos about stream */ |
| if (This->sInfo.fccType == streamtypeVIDEO) { |
| LONG lDim; |
| |
| lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left; |
| if (lDim < lpbiNew->biWidth) |
| This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth; |
| lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top; |
| if (lDim < lpbiNew->biHeight) |
| This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight; |
| } else if (This->sInfo.fccType == streamtypeAUDIO) |
| This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign; |
| |
| return AVIERR_OK; |
| } else { |
| MMCKINFO ck; |
| LPBITMAPINFOHEADER lpbiOld = This->lpFormat; |
| RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize); |
| AVIPALCHANGE *lppc = NULL; |
| UINT n; |
| |
| /* perhaps format change, check it ... */ |
| if (This->cbFormat != formatsize) |
| return AVIERR_UNSUPPORTED; |
| |
| /* no format change, only the initial one */ |
| if (memcmp(This->lpFormat, format, formatsize) == 0) |
| return AVIERR_OK; |
| |
| /* check that's only the palette, which changes */ |
| if (lpbiOld->biSize != lpbiNew->biSize || |
| lpbiOld->biWidth != lpbiNew->biWidth || |
| lpbiOld->biHeight != lpbiNew->biHeight || |
| lpbiOld->biPlanes != lpbiNew->biPlanes || |
| lpbiOld->biBitCount != lpbiNew->biBitCount || |
| lpbiOld->biCompression != lpbiNew->biCompression || |
| lpbiOld->biClrUsed != lpbiNew->biClrUsed) |
| return AVIERR_UNSUPPORTED; |
| |
| This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; |
| |
| /* simply say all colors have changed */ |
| ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream); |
| ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY); |
| lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize); |
| if (lppc == NULL) |
| return AVIERR_MEMORY; |
| |
| lppc->bFirstEntry = 0; |
| lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0); |
| lppc->wFlags = 0; |
| for (n = 0; n < lpbiOld->biClrUsed; n++) { |
| lppc->peNew[n].peRed = rgbNew[n].rgbRed; |
| lppc->peNew[n].peGreen = rgbNew[n].rgbGreen; |
| lppc->peNew[n].peBlue = rgbNew[n].rgbBlue; |
| lppc->peNew[n].peFlags = 0; |
| } |
| |
| if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 || |
| mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK || |
| mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize || |
| mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) |
| { |
| HeapFree(GetProcessHeap(), 0, lppc); |
| return AVIERR_FILEWRITE; |
| } |
| |
| This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD); |
| |
| HeapFree(GetProcessHeap(), 0, lppc); |
| |
| return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0); |
| } |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, |
| LONG samples, LPVOID buffer, |
| LONG buffersize, LPLONG bytesread, |
| LPLONG samplesread) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| DWORD size; |
| HRESULT hr; |
| |
| 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; |
| |
| /* check parameters */ |
| if ((LONG)This->sInfo.dwStart > start) |
| return AVIERR_NODATA; /* couldn't read before start of stream */ |
| if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start) |
| return AVIERR_NODATA; /* start is past end of stream */ |
| |
| /* should we read as much as possible? */ |
| if (samples == -1) { |
| /* User should know how much we have read */ |
| if (bytesread == NULL && samplesread == NULL) |
| return AVIERR_BADPARAM; |
| |
| if (This->sInfo.dwSampleSize != 0) |
| samples = buffersize / This->sInfo.dwSampleSize; |
| else |
| samples = 1; |
| } |
| |
| /* limit to end of stream */ |
| if ((LONG)This->sInfo.dwLength < samples) |
| samples = This->sInfo.dwLength; |
| if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples)) |
| samples = This->sInfo.dwLength - (start - This->sInfo.dwStart); |
| |
| /* nothing to read? Then leave ... */ |
| if (samples == 0) |
| return AVIERR_OK; |
| |
| if (This->sInfo.dwSampleSize != 0) { |
| /* fixed samplesize -- we can read over frame/block boundaries */ |
| LONG block = start; |
| LONG offset = 0; |
| |
| if (!buffer) |
| { |
| if (bytesread) |
| *bytesread = samples*This->sInfo.dwSampleSize; |
| if (samplesread) |
| *samplesread = samples; |
| return AVIERR_OK; |
| } |
| |
| /* convert start sample to block,offset pair */ |
| AVIFILE_SamplesToBlock(This, &block, &offset); |
| |
| /* convert samples to bytes */ |
| samples *= This->sInfo.dwSampleSize; |
| |
| while (samples > 0 && buffersize > 0) { |
| LONG blocksize; |
| if (block != This->dwCurrentFrame) { |
| hr = AVIFILE_ReadBlock(This, block, NULL, 0); |
| if (FAILED(hr)) |
| return hr; |
| } |
| |
| size = min((DWORD)samples, (DWORD)buffersize); |
| blocksize = This->lpBuffer[1]; |
| TRACE("blocksize = %u\n",blocksize); |
| size = min(size, blocksize - offset); |
| memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size); |
| |
| block++; |
| offset = 0; |
| buffer = ((LPBYTE)buffer)+size; |
| samples -= size; |
| buffersize -= size; |
| |
| /* fill out return parameters if given */ |
| if (bytesread != NULL) |
| *bytesread += size; |
| if (samplesread != NULL) |
| *samplesread += size / This->sInfo.dwSampleSize; |
| } |
| |
| if (samples == 0) |
| return AVIERR_OK; |
| else |
| return AVIERR_BUFFERTOOSMALL; |
| } else { |
| /* variable samplesize -- we can only read one full frame/block */ |
| if (samples > 1) |
| samples = 1; |
| |
| assert(start <= This->lLastFrame); |
| size = This->idxFrames[start].dwChunkLength; |
| if (buffer != NULL && buffersize >= size) { |
| hr = AVIFILE_ReadBlock(This, start, buffer, size); |
| if (FAILED(hr)) |
| return hr; |
| } else if (buffer != NULL) |
| return AVIERR_BUFFERTOOSMALL; |
| |
| /* fill out return parameters if given */ |
| if (bytesread != NULL) |
| *bytesread = size; |
| if (samplesread != NULL) |
| *samplesread = samples; |
| |
| return AVIERR_OK; |
| } |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, |
| LONG samples, LPVOID buffer, |
| LONG buffersize, DWORD flags, |
| LPLONG sampwritten, |
| LPLONG byteswritten) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| FOURCC ckid; |
| HRESULT hr; |
| |
| 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; |
| |
| /* Have we write permission? */ |
| if ((This->paf->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| switch (This->sInfo.fccType) { |
| case streamtypeAUDIO: |
| ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream); |
| break; |
| default: |
| if ((flags & AVIIF_KEYFRAME) && buffersize != 0) |
| ckid = MAKEAVICKID(cktypeDIBbits, This->nStream); |
| else |
| ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream); |
| break; |
| }; |
| |
| /* append to end of stream? */ |
| if (start == -1) { |
| if (This->lLastFrame == -1) |
| start = This->sInfo.dwStart; |
| else |
| start = This->sInfo.dwLength; |
| } else if (This->lLastFrame == -1) |
| This->sInfo.dwStart = start; |
| |
| if (This->sInfo.dwSampleSize != 0) { |
| /* fixed sample size -- audio like */ |
| if (samples * This->sInfo.dwSampleSize != buffersize) |
| return AVIERR_BADPARAM; |
| |
| /* Couldn't skip audio-like data -- User must supply appropriate silence */ |
| if (This->sInfo.dwLength != start) |
| return AVIERR_UNSUPPORTED; |
| |
| /* Convert position to frame/block */ |
| start = This->lLastFrame + 1; |
| |
| if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) { |
| FIXME(": not interleaved, could collect audio data!\n"); |
| } |
| } else { |
| /* variable sample size -- video like */ |
| if (samples > 1) |
| return AVIERR_UNSUPPORTED; |
| |
| /* must we fill up with empty frames? */ |
| if (This->lLastFrame != -1) { |
| FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); |
| |
| while (start > This->lLastFrame + 1) { |
| hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0); |
| if (FAILED(hr)) |
| return hr; |
| } |
| } |
| } |
| |
| /* write the block now */ |
| hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize); |
| if (SUCCEEDED(hr)) { |
| /* fill out return parameters if given */ |
| if (sampwritten != NULL) |
| *sampwritten = samples; |
| if (byteswritten != NULL) |
| *byteswritten = buffersize; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, |
| LONG samples) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| FIXME("(%p,%d,%d): stub\n", iface, start, samples); |
| |
| /* check parameters */ |
| if (start < 0 || samples < 0) |
| return AVIERR_BADPARAM; |
| |
| /* Delete before start of stream? */ |
| if (start + samples < This->sInfo.dwStart) |
| return AVIERR_OK; |
| |
| /* Delete after end of stream? */ |
| if (start > This->sInfo.dwLength) |
| return AVIERR_OK; |
| |
| /* For the rest we need write permissions */ |
| if ((This->paf->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| /* 1. overwrite the data with JUNK |
| * |
| * if ISINTERLEAVED { |
| * 2. concat all neighboured JUNK-blocks in this record to one |
| * 3. if this record only contains JUNK and is at end set dwNextFramePos |
| * to start of this record, repeat this. |
| * } else { |
| * 2. concat all neighboured JUNK-blocks. |
| * 3. if the JUNK block is at the end, then set dwNextFramePos to |
| * start of this block. |
| * } |
| */ |
| |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, |
| LPVOID lp, LPLONG lpread) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread); |
| |
| if (fcc == ckidSTREAMHANDLERDATA) { |
| if (This->lpHandlerData != NULL && This->cbHandlerData > 0) { |
| if (lp == NULL || *lpread <= 0) { |
| *lpread = This->cbHandlerData; |
| return AVIERR_OK; |
| } |
| |
| memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread)); |
| if (*lpread < This->cbHandlerData) |
| return AVIERR_BUFFERTOOSMALL; |
| return AVIERR_OK; |
| } else |
| return AVIERR_NODATA; |
| } else |
| return ReadExtraChunk(&This->extra, fcc, lp, lpread); |
| } |
| |
| static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, |
| LPVOID lp, LONG size) |
| { |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; |
| |
| TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size); |
| |
| /* check parameters */ |
| if (lp == NULL) |
| return AVIERR_BADPARAM; |
| if (size <= 0) |
| return AVIERR_BADSIZE; |
| |
| /* need write permission */ |
| if ((This->paf->uMode & MMIO_RWMODE) == 0) |
| return AVIERR_READONLY; |
| |
| /* already written something to this file? */ |
| if (This->paf->dwMoviChunkPos != 0) { |
| /* the data will be inserted before the 'movi' chunk, so check for |
| * enough space */ |
| DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf); |
| |
| /* ckid,size => 2 * sizeof(DWORD) */ |
| dwPos += 2 * sizeof(DWORD) + size; |
| if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD)) |
| return AVIERR_UNSUPPORTED; /* not enough space left */ |
| } |
| |
| This->paf->fDirty = TRUE; |
| |
| if (fcc == ckidSTREAMHANDLERDATA) { |
| if (This->lpHandlerData != NULL) { |
| FIXME(": handler data already set -- overwirte?\n"); |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size); |
| if (This->lpHandlerData == NULL) |
| return AVIERR_MEMORY; |
| This->cbHandlerData = size; |
| memcpy(This->lpHandlerData, lp, size); |
| |
| return AVIERR_OK; |
| } else |
| return WriteExtraChunk(&This->extra, 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 HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags) |
| { |
| UINT n; |
| |
| /* pre-conditions */ |
| assert(This != NULL); |
| |
| switch (TWOCCFromFOURCC(ckid)) { |
| case cktypeDIBbits: |
| if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) |
| flags |= AVIIF_KEYFRAME; |
| break; |
| case cktypeDIBcompressed: |
| if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) |
| flags &= ~AVIIF_KEYFRAME; |
| break; |
| case cktypePALchange: |
| if (This->sInfo.fccType != streamtypeVIDEO) { |
| ERR(": found palette change in non-video stream!\n"); |
| return AVIERR_BADFORMAT; |
| } |
| |
| if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) { |
| DWORD new_count = This->nIdxFmtChanges + 16; |
| void *new_buffer; |
| |
| if (This->idxFmtChanges == NULL) { |
| This->idxFmtChanges = |
| HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY)); |
| if (!This->idxFmtChanges) return AVIERR_MEMORY; |
| } else { |
| new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges, |
| new_count * sizeof(AVIINDEXENTRY)); |
| if (!new_buffer) return AVIERR_MEMORY; |
| This->idxFmtChanges = new_buffer; |
| } |
| This->nIdxFmtChanges = new_count; |
| } |
| |
| This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; |
| n = ++This->sInfo.dwFormatChangeCount; |
| This->idxFmtChanges[n].ckid = This->lLastFrame; |
| This->idxFmtChanges[n].dwFlags = 0; |
| This->idxFmtChanges[n].dwChunkOffset = offset; |
| This->idxFmtChanges[n].dwChunkLength = size; |
| |
| return AVIERR_OK; |
| case cktypeWAVEbytes: |
| if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) |
| flags |= AVIIF_KEYFRAME; |
| break; |
| default: |
| WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid)); |
| break; |
| }; |
| |
| /* first frame is always a keyframe */ |
| if (This->lLastFrame == -1) |
| flags |= AVIIF_KEYFRAME; |
| |
| if (This->sInfo.dwSuggestedBufferSize < size) |
| This->sInfo.dwSuggestedBufferSize = size; |
| |
| /* get memory for index */ |
| if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) { |
| This->nIdxFrames += 512; |
| if (This->idxFrames == NULL) |
| This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY)); |
| else |
| This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames, |
| This->nIdxFrames * sizeof(AVIINDEXENTRY)); |
| if (This->idxFrames == NULL) |
| return AVIERR_MEMORY; |
| } |
| |
| This->lLastFrame++; |
| This->idxFrames[This->lLastFrame].ckid = ckid; |
| This->idxFrames[This->lLastFrame].dwFlags = flags; |
| This->idxFrames[This->lLastFrame].dwChunkOffset = offset; |
| This->idxFrames[This->lLastFrame].dwChunkLength = size; |
| |
| /* update AVISTREAMINFO structure if necessary */ |
| if (This->sInfo.dwLength <= This->lLastFrame) |
| This->sInfo.dwLength = This->lLastFrame + 1; |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This) |
| { |
| /* pre-conditions */ |
| assert(This != NULL && This->ppStreams[0] != NULL); |
| |
| if (This->idxRecords == NULL || This->cbIdxRecords == 0) { |
| This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY); |
| This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords); |
| if (This->idxRecords == NULL) |
| return AVIERR_MEMORY; |
| } |
| |
| assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY)); |
| |
| This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD; |
| This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST; |
| This->idxRecords[This->nIdxRecords].dwChunkOffset = |
| This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD); |
| This->idxRecords[This->nIdxRecords].dwChunkLength = |
| This->ckLastRecord.cksize; |
| This->nIdxRecords++; |
| |
| return AVIERR_OK; |
| } |
| |
| static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This) |
| { |
| DWORD dwPos; |
| DWORD nStream; |
| |
| /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */ |
| dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader); |
| |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { |
| IAVIStreamImpl *pStream = This->ppStreams[nStream]; |
| |
| /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */ |
| dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader); |
| dwPos += ((pStream->cbFormat + 1) & ~1U); |
| if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) |
| dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U); |
| if (lstrlenW(pStream->sInfo.szName) > 0) |
| dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U); |
| } |
| |
| if (This->dwMoviChunkPos == 0) { |
| This->dwNextFramePos = dwPos; |
| |
| /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */ |
| if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD)) |
| This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1); |
| |
| This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD); |
| } |
| |
| return dwPos; |
| } |
| |
| static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi) |
| { |
| IAVIStreamImpl *pstream; |
| |
| /* pre-conditions */ |
| assert(paf != NULL); |
| assert(nr < MAX_AVISTREAMS); |
| assert(paf->ppStreams[nr] != NULL); |
| |
| pstream = paf->ppStreams[nr]; |
| |
| pstream->lpVtbl = &iavist; |
| pstream->ref = 0; |
| pstream->paf = paf; |
| pstream->nStream = nr; |
| pstream->dwCurrentFrame = (DWORD)-1; |
| pstream->lLastFrame = -1; |
| |
| if (asi != NULL) { |
| memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo)); |
| |
| if (asi->dwLength > 0) { |
| /* pre-allocate mem for frame-index structure */ |
| pstream->idxFrames = |
| HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY)); |
| if (pstream->idxFrames != NULL) |
| pstream->nIdxFrames = asi->dwLength; |
| } |
| if (asi->dwFormatChangeCount > 0) { |
| /* pre-allocate mem for formatchange-index structure */ |
| pstream->idxFmtChanges = |
| HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY)); |
| if (pstream->idxFmtChanges != NULL) |
| pstream->nIdxFmtChanges = asi->dwFormatChangeCount; |
| } |
| |
| /* These values will be computed */ |
| pstream->sInfo.dwLength = 0; |
| pstream->sInfo.dwSuggestedBufferSize = 0; |
| pstream->sInfo.dwFormatChangeCount = 0; |
| pstream->sInfo.dwEditCount = 1; |
| if (pstream->sInfo.dwSampleSize > 0) |
| SetRectEmpty(&pstream->sInfo.rcFrame); |
| } |
| |
| pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; |
| } |
| |
| static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This) |
| { |
| /* pre-conditions */ |
| assert(This != NULL); |
| |
| This->dwCurrentFrame = (DWORD)-1; |
| This->lLastFrame = -1; |
| This->paf = NULL; |
| if (This->idxFrames != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->idxFrames); |
| This->idxFrames = NULL; |
| This->nIdxFrames = 0; |
| } |
| HeapFree(GetProcessHeap(), 0, This->idxFmtChanges); |
| This->idxFmtChanges = NULL; |
| if (This->lpBuffer != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->lpBuffer); |
| This->lpBuffer = NULL; |
| This->cbBuffer = 0; |
| } |
| if (This->lpHandlerData != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->lpHandlerData); |
| This->lpHandlerData = NULL; |
| This->cbHandlerData = 0; |
| } |
| if (This->extra.lp != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->extra.lp); |
| This->extra.lp = NULL; |
| This->extra.cb = 0; |
| } |
| if (This->lpFormat != NULL) { |
| HeapFree(GetProcessHeap(), 0, This->lpFormat); |
| This->lpFormat = NULL; |
| This->cbFormat = 0; |
| } |
| } |
| |
| static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) |
| { |
| MainAVIHeader MainAVIHdr; |
| MMCKINFO ckRIFF; |
| MMCKINFO ckLIST1; |
| MMCKINFO ckLIST2; |
| MMCKINFO ck; |
| IAVIStreamImpl *pStream; |
| DWORD nStream; |
| HRESULT hr; |
| |
| if (This->hmmio == NULL) |
| return AVIERR_FILEOPEN; |
| |
| /* initialize stream ptr's */ |
| memset(This->ppStreams, 0, sizeof(This->ppStreams)); |
| |
| /* try to get "RIFF" chunk -- must not be at beginning of file! */ |
| ckRIFF.fccType = formtypeAVI; |
| if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { |
| ERR(": not an AVI!\n"); |
| return AVIERR_FILEREAD; |
| } |
| |
| /* get "LIST" "hdrl" */ |
| ckLIST1.fccType = listtypeAVIHEADER; |
| hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST); |
| if (FAILED(hr)) |
| return hr; |
| |
| /* get "avih" chunk */ |
| ck.ckid = ckidAVIMAINHDR; |
| hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (ck.cksize != sizeof(MainAVIHdr)) { |
| ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize); |
| return AVIERR_BADFORMAT; |
| } |
| if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) |
| return AVIERR_FILEREAD; |
| |
| /* check for MAX_AVISTREAMS limit */ |
| if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) { |
| WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS); |
| return AVIERR_UNSUPPORTED; |
| } |
| |
| /* adjust permissions if copyrighted material in file */ |
| if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) { |
| This->uMode &= ~MMIO_RWMODE; |
| This->uMode |= MMIO_READ; |
| } |
| |
| /* convert MainAVIHeader into AVIFILINFOW */ |
| memset(&This->fInfo, 0, sizeof(This->fInfo)); |
| This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame; |
| This->fInfo.dwScale = 1000000; |
| This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec; |
| This->fInfo.dwFlags = MainAVIHdr.dwFlags; |
| This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; |
| This->fInfo.dwLength = MainAVIHdr.dwTotalFrames; |
| This->fInfo.dwStreams = MainAVIHdr.dwStreams; |
| This->fInfo.dwSuggestedBufferSize = 0; |
| This->fInfo.dwWidth = MainAVIHdr.dwWidth; |
| This->fInfo.dwHeight = MainAVIHdr.dwHeight; |
| LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType, |
| sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0])); |
| |
| /* go back to into header list */ |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| /* foreach stream exists a "LIST","strl" chunk */ |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { |
| /* get next nested chunk in this "LIST","strl" */ |
| if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */ |
| if (ckLIST2.ckid == FOURCC_LIST && |
| ckLIST2.fccType == listtypeSTREAMHEADER) { |
| pStream = This->ppStreams[nStream] = |
| HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl)); |
| if (pStream == NULL) |
| return AVIERR_MEMORY; |
| AVIFILE_ConstructAVIStream(This, nStream, NULL); |
| |
| ck.ckid = 0; |
| while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) { |
| switch (ck.ckid) { |
| case ckidSTREAMHANDLERDATA: |
| if (pStream->lpHandlerData != NULL) |
| return AVIERR_BADFORMAT; |
| pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize); |
| if (pStream->lpHandlerData == NULL) |
| return AVIERR_MEMORY; |
| pStream->cbHandlerData = ck.cksize; |
| |
| if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) |
| return AVIERR_FILEREAD; |
| break; |
| case ckidSTREAMFORMAT: |
| if (pStream->lpFormat != NULL) |
| return AVIERR_BADFORMAT; |
| if (ck.cksize == 0) |
| break; |
| |
| pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize); |
| if (pStream->lpFormat == NULL) |
| return AVIERR_MEMORY; |
| pStream->cbFormat = ck.cksize; |
| |
| if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) |
| return AVIERR_FILEREAD; |
| |
| if (pStream->sInfo.fccType == streamtypeVIDEO) { |
| LPBITMAPINFOHEADER lpbi = pStream->lpFormat; |
| |
| /* some corrections to the video format */ |
| if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) |
| lpbi->biClrUsed = 1u << lpbi->biBitCount; |
| if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0) |
| lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight; |
| if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) { |
| if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') || |
| pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' ')) |
| lpbi->biCompression = BI_RLE8; |
| } |
| if (lpbi->biCompression == BI_RGB && |
| (pStream->sInfo.fccHandler == 0 || |
| pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))) |
| pStream->sInfo.fccHandler = comptypeDIB; |
| |
| /* init rcFrame if it's empty */ |
| if (IsRectEmpty(&pStream->sInfo.rcFrame)) |
| SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight); |
| } |
| break; |
| case ckidSTREAMHEADER: |
| { |
| static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0}; |
| static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0}; |
| |
| AVIStreamHeader streamHdr; |
| WCHAR szType[25]; |
| UINT count; |
| LONG n = ck.cksize; |
| |
| if (ck.cksize > sizeof(streamHdr)) |
| n = sizeof(streamHdr); |
| |
| if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n) |
| return AVIERR_FILEREAD; |
| |
| pStream->sInfo.fccType = streamHdr.fccType; |
| pStream->sInfo.fccHandler = streamHdr.fccHandler; |
| pStream->sInfo.dwFlags = streamHdr.dwFlags; |
| pStream->sInfo.wPriority = streamHdr.wPriority; |
| pStream->sInfo.wLanguage = streamHdr.wLanguage; |
| pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames; |
| pStream->sInfo.dwScale = streamHdr.dwScale; |
| pStream->sInfo.dwRate = streamHdr.dwRate; |
| pStream->sInfo.dwStart = streamHdr.dwStart; |
| pStream->sInfo.dwLength = streamHdr.dwLength; |
| pStream->sInfo.dwSuggestedBufferSize = 0; |
| pStream->sInfo.dwQuality = streamHdr.dwQuality; |
| pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize; |
| pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left; |
| pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top; |
| pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right; |
| pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom; |
| pStream->sInfo.dwEditCount = 0; |
| pStream->sInfo.dwFormatChangeCount = 0; |
| |
| /* generate description for stream like "filename.avi Type #n" */ |
| if (streamHdr.fccType == streamtypeVIDEO) |
| LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0])); |
| else if (streamHdr.fccType == streamtypeAUDIO) |
| LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0])); |
| else |
| wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType); |
| |
| /* get count of this streamtype up to this stream */ |
| count = 0; |
| for (n = nStream; 0 <= n; n--) { |
| if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType) |
| count++; |
| } |
| |
| memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName)); |
| |
| /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */ |
| wsprintfW(pStream->sInfo.szName, streamNameFmt, |
| AVIFILE_BasenameW(This->szFileName), szType, count); |
| } |
| break; |
| case ckidSTREAMNAME: |
| { /* streamname will be saved as ASCII string */ |
| LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); |
| if (str == NULL) |
| return AVIERR_MEMORY; |
| |
| if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize) |
| { |
| HeapFree(GetProcessHeap(), 0, str); |
| return AVIERR_FILEREAD; |
| } |
| |
| MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName, |
| sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0])); |
| |
| HeapFree(GetProcessHeap(), 0, str); |
| } |
| break; |
| case ckidAVIPADDING: |
| case mmioFOURCC('p','a','d','d'): |
| break; |
| default: |
| WARN(": found extra chunk 0x%08X\n", ck.ckid); |
| hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck); |
| if (FAILED(hr)) |
| return hr; |
| }; |
| if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO) |
| { |
| WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */ |
| pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */ |
| TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); |
| pStream->sInfo.dwScale = 1; |
| pStream->sInfo.dwRate = wfx->nSamplesPerSec; |
| } |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| } |
| } else { |
| /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */ |
| hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2); |
| if (FAILED(hr)) |
| return hr; |
| } |
| if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| } |
| |
| /* read any extra headers in "LIST","hdrl" */ |
| FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0); |
| if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| /* search "LIST","movi" chunk in "RIFF","AVI " */ |
| ckLIST1.fccType = listtypeAVIMOVIE; |
| hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, |
| MMIO_FINDLIST); |
| if (FAILED(hr)) |
| return hr; |
| |
| This->dwMoviChunkPos = ckLIST1.dwDataOffset; |
| This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset; |
| if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) |
| return AVIERR_FILEREAD; |
| |
| /* try to find an index */ |
| ck.ckid = ckidAVINEWINDEX; |
| hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, |
| &ck, &ckRIFF, MMIO_FINDCHUNK); |
| if (SUCCEEDED(hr) && ck.cksize > 0) { |
| if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset))) |
| This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX; |
| } |
| |
| /* when we haven't found an index or it's bad, then build one |
| * by parsing 'movi' chunk */ |
| if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) { |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) |
| This->ppStreams[nStream]->lLastFrame = -1; |
| |
| if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) { |
| ERR(": Oops, can't seek back to 'movi' chunk!\n"); |
| return AVIERR_FILEREAD; |
| } |
| |
| /* seek through the 'movi' list until end */ |
| while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) { |
| if (ck.ckid != FOURCC_LIST) { |
| if (mmioAscend(This->hmmio, &ck, 0) == S_OK) { |
| nStream = StreamFromFOURCC(ck.ckid); |
| |
| if (nStream > This->fInfo.dwStreams) |
| return AVIERR_BADFORMAT; |
| |
| AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, |
| ck.dwDataOffset - 2 * sizeof(DWORD), 0); |
| } else { |
| nStream = StreamFromFOURCC(ck.ckid); |
| WARN(": file seems to be truncated!\n"); |
| if (nStream <= This->fInfo.dwStreams && |
| This->ppStreams[nStream]->sInfo.dwSampleSize > 0) { |
| ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END); |
| if (ck.cksize != -1) { |
| ck.cksize -= ck.dwDataOffset; |
| ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1); |
| |
| AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, |
| ck.dwDataOffset - 2 * sizeof(DWORD), 0); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) |
| { |
| DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize; |
| if (This->fInfo.dwSuggestedBufferSize < sugbuf) |
| This->fInfo.dwSuggestedBufferSize = sugbuf; |
| } |
| |
| /* find other chunks */ |
| FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset) |
| { |
| AVIINDEXENTRY *lp; |
| DWORD pos, n; |
| HRESULT hr = AVIERR_OK; |
| BOOL bAbsolute = TRUE; |
| |
| lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY)); |
| if (lp == NULL) |
| return AVIERR_MEMORY; |
| |
| /* adjust limits for index tables, so that inserting will be faster */ |
| for (n = 0; n < This->fInfo.dwStreams; n++) { |
| IAVIStreamImpl *pStream = This->ppStreams[n]; |
| |
| pStream->lLastFrame = -1; |
| |
| if (pStream->idxFrames != NULL) { |
| HeapFree(GetProcessHeap(), 0, pStream->idxFrames); |
| pStream->idxFrames = NULL; |
| pStream->nIdxFrames = 0; |
| } |
| |
| if (pStream->sInfo.dwSampleSize != 0) { |
| if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { |
| pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames; |
| } else if (pStream->sInfo.dwSuggestedBufferSize) { |
| pStream->nIdxFrames = |
| pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize; |
| } |
| } else |
| pStream->nIdxFrames = pStream->sInfo.dwLength; |
| |
| pStream->idxFrames = |
| HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY)); |
| if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) { |
| pStream->nIdxFrames = 0; |
| HeapFree(GetProcessHeap(), 0, lp); |
| return AVIERR_MEMORY; |
| } |
| } |
| |
| pos = (DWORD)-1; |
| while (size != 0) { |
| LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size); |
| |
| if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) { |
| hr = AVIERR_FILEREAD; |
| break; |
| } |
| size -= read; |
| |
| if (pos == (DWORD)-1) |
| pos = offset - lp->dwChunkOffset + sizeof(DWORD); |
| |
| AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY), |
| pos, &bAbsolute); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, lp); |
| |
| /* checking ... */ |
| for (n = 0; n < This->fInfo.dwStreams; n++) { |
| IAVIStreamImpl *pStream = This->ppStreams[n]; |
| |
| if (pStream->sInfo.dwSampleSize == 0 && |
| pStream->sInfo.dwLength != pStream->lLastFrame+1) |
| ERR("stream %u length mismatch: dwLength=%u found=%d\n", |
| n, pStream->sInfo.dwLength, pStream->lLastFrame); |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, |
| LONG count, DWORD pos, BOOL *bAbsolute) |
| { |
| if (lp == NULL) |
| return AVIERR_BADPARAM; |
| |
| for (; count > 0; count--, lp++) { |
| WORD nStream = StreamFromFOURCC(lp->ckid); |
| |
| if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F) |
| continue; /* skip these */ |
| |
| if (nStream > This->fInfo.dwStreams) |
| return AVIERR_BADFORMAT; |
| |
| if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos) |
| *bAbsolute = FALSE; |
| |
| if (*bAbsolute) |
| lp->dwChunkOffset += sizeof(DWORD); |
| else |
| lp->dwChunkOffset += pos; |
| |
| if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags))) |
| return AVIERR_MEMORY; |
| } |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos, |
| LPVOID buffer, DWORD size) |
| { |
| /* pre-conditions */ |
| assert(This != NULL); |
| assert(This->paf != NULL); |
| assert(This->paf->hmmio != NULL); |
| assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); |
| assert(pos <= This->lLastFrame); |
| |
| /* should we read as much as block gives us? */ |
| if (size == 0 || size > This->idxFrames[pos].dwChunkLength) |
| size = This->idxFrames[pos].dwChunkLength; |
| |
| /* read into out own buffer or given one? */ |
| if (buffer == NULL) { |
| /* we also read the chunk */ |
| size += 2 * sizeof(DWORD); |
| |
| /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */ |
| if (This->lpBuffer == NULL || This->cbBuffer < size) { |
| DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize); |
| |
| if (This->lpBuffer == NULL) { |
| This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize); |
| if (!This->lpBuffer) return AVIERR_MEMORY; |
| } else { |
| void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize); |
| if (!new_buffer) return AVIERR_MEMORY; |
| This->lpBuffer = new_buffer; |
| } |
| This->cbBuffer = maxSize; |
| } |
| |
| /* now read the complete chunk into our buffer */ |
| if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1) |
| return AVIERR_FILEREAD; |
| if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size) |
| return AVIERR_FILEREAD; |
| |
| /* check if it was the correct block which we have read */ |
| if (This->lpBuffer[0] != This->idxFrames[pos].ckid || |
| This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) { |
| ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset); |
| ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n", |
| (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid, |
| This->idxFrames[pos].dwChunkLength); |
| ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n", |
| (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]); |
| return AVIERR_FILEREAD; |
| } |
| } else { |
| if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1) |
| return AVIERR_FILEREAD; |
| if (mmioRead(This->paf->hmmio, buffer, size) != size) |
| return AVIERR_FILEREAD; |
| } |
| |
| return AVIERR_OK; |
| } |
| |
| static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset) |
| { |
| LONG block; |
| |
| /* pre-conditions */ |
| assert(This != NULL); |
| assert(pos != NULL); |
| assert(offset != NULL); |
| assert(This->sInfo.dwSampleSize != 0); |
| assert(*pos >= This->sInfo.dwStart); |
| |
| /* convert start sample to start bytes */ |
| (*offset) = (*pos) - This->sInfo.dwStart; |
| (*offset) *= This->sInfo.dwSampleSize; |
| |
| /* convert bytes to block number */ |
| for (block = 0; block <= This->lLastFrame; block++) { |
| if (This->idxFrames[block].dwChunkLength <= *offset) |
| (*offset) -= This->idxFrames[block].dwChunkLength; |
| else |
| break; |
| } |
| |
| *pos = block; |
| } |
| |
| static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) |
| { |
| MainAVIHeader MainAVIHdr; |
| IAVIStreamImpl* pStream; |
| MMCKINFO ckRIFF; |
| MMCKINFO ckLIST1; |
| MMCKINFO ckLIST2; |
| MMCKINFO ck; |
| DWORD nStream; |
| DWORD dwPos; |
| HRESULT hr; |
| |
| /* initialize some things */ |
| if (This->dwMoviChunkPos == 0) |
| AVIFILE_ComputeMoviStart(This); |
| |
| /* written one record to much? */ |
| if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { |
| This->dwNextFramePos -= 3 * sizeof(DWORD); |
| if (This->nIdxRecords > 0) |
| This->nIdxRecords--; |
| } |
| |
| AVIFILE_UpdateInfo(This); |
| |
| assert(This->fInfo.dwScale != 0); |
| |
| memset(&MainAVIHdr, 0, sizeof(MainAVIHdr)); |
| MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000, |
| This->fInfo.dwScale); |
| MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec; |
| MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE; |
| MainAVIHdr.dwFlags = This->fInfo.dwFlags; |
| MainAVIHdr.dwTotalFrames = This->fInfo.dwLength; |
| MainAVIHdr.dwInitialFrames = 0; |
| MainAVIHdr.dwStreams = This->fInfo.dwStreams; |
| MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize; |
| MainAVIHdr.dwWidth = This->fInfo.dwWidth; |
| MainAVIHdr.dwHeight = This->fInfo.dwHeight; |
| MainAVIHdr.dwInitialFrames = This->dwInitialFrames; |
| |
| /* now begin writing ... */ |
| mmioSeek(This->hmmio, 0, SEEK_SET); |
| |
| /* RIFF chunk */ |
| ckRIFF.cksize = 0; |
| ckRIFF.fccType = formtypeAVI; |
| if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* AVI headerlist */ |
| ckLIST1.cksize = 0; |
| ckLIST1.fccType = listtypeAVIHEADER; |
| if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* MainAVIHeader */ |
| ck.ckid = ckidAVIMAINHDR; |
| ck.cksize = sizeof(MainAVIHdr); |
| ck.fccType = 0; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* write the headers of each stream into a separate streamheader list */ |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { |
| AVIStreamHeader strHdr; |
| |
| pStream = This->ppStreams[nStream]; |
| |
| /* begin the new streamheader list */ |
| ckLIST2.cksize = 0; |
| ckLIST2.fccType = listtypeSTREAMHEADER; |
| if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* create an AVIStreamHeader from the AVSTREAMINFO */ |
| strHdr.fccType = pStream->sInfo.fccType; |
| strHdr.fccHandler = pStream->sInfo.fccHandler; |
| strHdr.dwFlags = pStream->sInfo.dwFlags; |
| strHdr.wPriority = pStream->sInfo.wPriority; |
| strHdr.wLanguage = pStream->sInfo.wLanguage; |
| strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames; |
| strHdr.dwScale = pStream->sInfo.dwScale; |
| strHdr.dwRate = pStream->sInfo.dwRate; |
| strHdr.dwStart = pStream->sInfo.dwStart; |
| strHdr.dwLength = pStream->sInfo.dwLength; |
| strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize; |
| strHdr.dwQuality = pStream->sInfo.dwQuality; |
| strHdr.dwSampleSize = pStream->sInfo.dwSampleSize; |
| strHdr.rcFrame.left = pStream->sInfo.rcFrame.left; |
| strHdr.rcFrame.top = pStream->sInfo.rcFrame.top; |
| strHdr.rcFrame.right = pStream->sInfo.rcFrame.right; |
| strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom; |
| |
| /* now write the AVIStreamHeader */ |
| ck.ckid = ckidSTREAMHEADER; |
| ck.cksize = sizeof(strHdr); |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* ... the hopefully ever present streamformat ... */ |
| ck.ckid = ckidSTREAMFORMAT; |
| ck.cksize = pStream->cbFormat; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (pStream->lpFormat != NULL && ck.cksize > 0) { |
| if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) |
| return AVIERR_FILEWRITE; |
| } |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* ... some optional existing handler data ... */ |
| if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) { |
| ck.ckid = ckidSTREAMHANDLERDATA; |
| ck.cksize = pStream->cbHandlerData; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* ... some optional additional extra chunk for this stream ... */ |
| if (pStream->extra.lp != NULL && pStream->extra.cb > 0) { |
| /* the chunk header(s) are already in the structure */ |
| if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* ... an optional name for this stream ... */ |
| if (lstrlenW(pStream->sInfo.szName) > 0) { |
| LPSTR str; |
| |
| ck.ckid = ckidSTREAMNAME; |
| ck.cksize = lstrlenW(pStream->sInfo.szName) + 1; |
| if (ck.cksize & 1) /* align */ |
| ck.cksize++; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* the streamname must be saved in ASCII not Unicode */ |
| str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); |
| if (str == NULL) |
| return AVIERR_MEMORY; |
| WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str, |
| ck.cksize, NULL, NULL); |
| |
| if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) { |
| HeapFree(GetProcessHeap(), 0, str); |
| return AVIERR_FILEWRITE; |
| } |
| |
| HeapFree(GetProcessHeap(), 0, str); |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* close streamheader list for this stream */ |
| if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| } /* for (0 <= nStream < MainAVIHdr.dwStreams) */ |
| |
| /* close the aviheader list */ |
| if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* check for padding to pre-guessed 'movi'-chunk position */ |
| dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize; |
| if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) { |
| ck.ckid = ckidAVIPADDING; |
| ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD); |
| assert((LONG)ck.cksize >= 0); |
| |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* now write the 'movi' chunk */ |
| mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET); |
| ckLIST1.cksize = 0; |
| ckLIST1.fccType = listtypeAVIMOVIE; |
| if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) |
| return AVIERR_FILEWRITE; |
| if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* write 'idx1' chunk */ |
| hr = AVIFILE_SaveIndex(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| /* write optional extra file chunks */ |
| if (This->fileextra.lp != NULL && This->fileextra.cb > 0) { |
| /* as for the streams, are the chunk header(s) in the structure */ |
| if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb) |
| return AVIERR_FILEWRITE; |
| } |
| |
| /* close RIFF chunk */ |
| if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| /* add some JUNK at end for bad parsers */ |
| memset(&ckRIFF, 0, sizeof(ckRIFF)); |
| mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF)); |
| mmioFlush(This->hmmio, 0); |
| |
| return AVIERR_OK; |
| } |
| |
| static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This) |
| { |
| IAVIStreamImpl *pStream; |
| AVIINDEXENTRY idx; |
| MMCKINFO ck; |
| DWORD nStream; |
| LONG n; |
| |
| ck.ckid = ckidAVINEWINDEX; |
| ck.cksize = 0; |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { |
| /* is interleaved -- write block of corresponding frames */ |
| LONG lInitialFrames = 0; |
| LONG stepsize; |
| LONG i; |
| |
| if (This->ppStreams[0]->sInfo.dwSampleSize == 0) |
| stepsize = 1; |
| else |
| stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000); |
| |
| assert(stepsize > 0); |
| |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { |
| if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames) |
| lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames; |
| } |
| |
| for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames; |
| i += stepsize) { |
| DWORD nFrame = lInitialFrames + i; |
| |
| assert(nFrame < This->nIdxRecords); |
| |
| idx.ckid = listtypeAVIRECORD; |
| idx.dwFlags = AVIIF_LIST; |
| idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength; |
| idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset |
| - This->dwMoviChunkPos; |
| if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) |
| return AVIERR_FILEWRITE; |
| |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { |
| pStream = This->ppStreams[nStream]; |
| |
| /* heave we reached start of this stream? */ |
| if (-(LONG)pStream->sInfo.dwInitialFrames > i) |
| continue; |
| |
| if (pStream->sInfo.dwInitialFrames < lInitialFrames) |
| nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames); |
| |
| /* reached end of this stream? */ |
| if (pStream->lLastFrame <= nFrame) |
| continue; |
| |
| if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && |
| pStream->sInfo.dwFormatChangeCount != 0 && |
| pStream->idxFmtChanges != NULL) { |
| DWORD pos; |
| |
| for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { |
| if (pStream->idxFmtChanges[pos].ckid == nFrame) { |
| idx.dwFlags = AVIIF_NOTIME; |
| idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); |
| idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; |
| idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset |
| - This->dwMoviChunkPos; |
| |
| if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) |
| return AVIERR_FILEWRITE; |
| break; |
| } |
| } |
| } /* if have formatchanges */ |
| |
| idx.ckid = pStream->idxFrames[nFrame].ckid; |
| idx.dwFlags = pStream->idxFrames[nFrame].dwFlags; |
| idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength; |
| idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset |
| - This->dwMoviChunkPos; |
| if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) |
| return AVIERR_FILEWRITE; |
| } |
| } |
| } else { |
| /* not interleaved -- write index for each stream at once */ |
| for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { |
| pStream = This->ppStreams[nStream]; |
| |
| for (n = 0; n <= pStream->lLastFrame; n++) { |
| if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && |
| (pStream->sInfo.dwFormatChangeCount != 0)) { |
| DWORD pos; |
| |
| for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { |
| if (pStream->idxFmtChanges[pos].ckid == n) { |
| idx.dwFlags = AVIIF_NOTIME; |
| idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); |
| idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; |
| idx.dwChunkOffset = |
| pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos; |
| if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) |
| return AVIERR_FILEWRITE; |
| break; |
| } |
| } |
| } /* if have formatchanges */ |
| |
| idx.ckid = pStream->idxFrames[n].ckid; |
| idx.dwFlags = pStream->idxFrames[n].dwFlags; |
| idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength; |
| idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset |
| - This->dwMoviChunkPos; |
| |
| if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) |
| return AVIERR_FILEWRITE; |
| } |
| } |
| } /* if not interleaved */ |
| |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| return AVIERR_OK; |
| } |
| |
| static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip) |
| { |
| UINT i; |
| UINT nStream; |
| |
| /* pre-condition */ |
| assert(lSkip >= 0); |
| |
| if (fcc != 0) { |
| /* search the number of the specified stream */ |
| nStream = (ULONG)-1; |
| for (i = 0; i < This->fInfo.dwStreams; i++) { |
| assert(This->ppStreams[i] != NULL); |
| |
| if (This->ppStreams[i]->sInfo.fccType == fcc) { |
| if (lSkip == 0) { |
| nStream = i; |
| break; |
| } else |
| lSkip--; |
| } |
| } |
| } else |
| nStream = lSkip; |
| |
| return nStream; |
| } |
| |
| static void AVIFILE_UpdateInfo(IAVIFileImpl *This) |
| { |
| UINT i; |
| |
| /* pre-conditions */ |
| assert(This != NULL); |
| |
| This->fInfo.dwMaxBytesPerSec = 0; |
| This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; |
| This->fInfo.dwSuggestedBufferSize = 0; |
| This->fInfo.dwWidth = 0; |
| This->fInfo.dwHeight = 0; |
| This->fInfo.dwScale = 0; |
| This->fInfo.dwRate = 0; |
| This->fInfo.dwLength = 0; |
| This->dwInitialFrames = 0; |
| |
| for (i = 0; i < This->fInfo.dwStreams; i++) { |
| AVISTREAMINFOW *psi; |
| DWORD n; |
| |
| /* pre-conditions */ |
| assert(This->ppStreams[i] != NULL); |
| |
| psi = &This->ppStreams[i]->sInfo; |
| assert(psi->dwScale != 0); |
| assert(psi->dwRate != 0); |
| |
| if (i == 0) { |
| /* use first stream timings as base */ |
| This->fInfo.dwScale = psi->dwScale; |
| This->fInfo.dwRate = psi->dwRate; |
| This->fInfo.dwLength = psi->dwLength; |
| } else { |
| n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0], |
| (PAVISTREAM)This->ppStreams[i],psi->dwLength); |
| if (This->fInfo.dwLength < n) |
| This->fInfo.dwLength = n; |
| } |
| |
| if (This->dwInitialFrames < psi->dwInitialFrames) |
| This->dwInitialFrames = psi->dwInitialFrames; |
| |
| if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize) |
| This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize; |
| |
| if (psi->dwSampleSize != 0) { |
| /* fixed sample size -- exact computation */ |
| This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate, |
| psi->dwScale); |
| } else { |
| /* variable sample size -- only upper limit */ |
| This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize, |
| psi->dwRate, psi->dwScale); |
| |
| /* update dimensions */ |
| n = psi->rcFrame.right - psi->rcFrame.left; |
| if (This->fInfo.dwWidth < n) |
| This->fInfo.dwWidth = n; |
| n = psi->rcFrame.bottom - psi->rcFrame.top; |
| if (This->fInfo.dwHeight < n) |
| This->fInfo.dwHeight = n; |
| } |
| } |
| } |
| |
| static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, |
| FOURCC ckid, DWORD flags, LPCVOID buffer, |
| LONG size) |
| { |
| MMCKINFO ck; |
| |
| ck.ckid = ckid; |
| ck.cksize = size; |
| ck.fccType = 0; |
| |
| /* if no frame/block is already written, we must compute start of movi chunk */ |
| if (This->paf->dwMoviChunkPos == 0) |
| AVIFILE_ComputeMoviStart(This->paf); |
| |
| if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1) |
| return AVIERR_FILEWRITE; |
| |
| if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| if (buffer != NULL && size > 0) { |
| if (mmioWrite(This->paf->hmmio, buffer, size) != size) |
| return AVIERR_FILEWRITE; |
| } |
| if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) |
| return AVIERR_FILEWRITE; |
| |
| This->paf->fDirty = TRUE; |
| This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); |
| |
| return AVIFILE_AddFrame(This, ckid, size, |
| ck.dwDataOffset - 2 * sizeof(DWORD), flags); |
| } |