Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenavifile.c
Go to the documentation of this file.
00001 /* 00002 * Copyright 1999 Marcus Meissner 00003 * Copyright 2002-2003 Michael Günnewig 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Lesser General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2.1 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public 00016 * License along with this library; if not, write to the Free Software 00017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00018 */ 00019 00020 /* TODO: 00021 * - IAVIStreaming interface is missing for the IAVIStreamImpl 00022 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported. 00023 * - IAVIStream_fnReadFormat: formatchanges aren't read in. 00024 * - IAVIStream_fnDelete: a stub. 00025 * - IAVIStream_fnSetInfo: a stub. 00026 * - make thread safe 00027 * 00028 * KNOWN Bugs: 00029 * - native version can hangup when reading a file generated with this DLL. 00030 * When index is missing it works, but index seems to be okay. 00031 */ 00032 00033 #include <assert.h> 00034 #include <stdarg.h> 00035 00036 #include "windef.h" 00037 #include "winbase.h" 00038 #include "wingdi.h" 00039 #include "winuser.h" 00040 #include "winnls.h" 00041 #include "winerror.h" 00042 #include "mmsystem.h" 00043 #include "vfw.h" 00044 00045 #include "avifile_private.h" 00046 #include "extrachunk.h" 00047 00048 #include "wine/unicode.h" 00049 #include "wine/debug.h" 00050 00051 WINE_DEFAULT_DEBUG_CHANNEL(avifile); 00052 00053 #ifndef IDX_PER_BLOCK 00054 #define IDX_PER_BLOCK 2730 00055 #endif 00056 00057 /***********************************************************************/ 00058 00059 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); 00060 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface); 00061 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface); 00062 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); 00063 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); 00064 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); 00065 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); 00066 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); 00067 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface); 00068 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); 00069 00070 static const struct IAVIFileVtbl iavift = { 00071 IAVIFile_fnQueryInterface, 00072 IAVIFile_fnAddRef, 00073 IAVIFile_fnRelease, 00074 IAVIFile_fnInfo, 00075 IAVIFile_fnGetStream, 00076 IAVIFile_fnCreateStream, 00077 IAVIFile_fnWriteData, 00078 IAVIFile_fnReadData, 00079 IAVIFile_fnEndRecord, 00080 IAVIFile_fnDeleteStream 00081 }; 00082 00083 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj); 00084 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface); 00085 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface); 00086 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID); 00087 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface); 00088 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode); 00089 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember); 00090 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName); 00091 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName); 00092 00093 static const struct IPersistFileVtbl ipersistft = { 00094 IPersistFile_fnQueryInterface, 00095 IPersistFile_fnAddRef, 00096 IPersistFile_fnRelease, 00097 IPersistFile_fnGetClassID, 00098 IPersistFile_fnIsDirty, 00099 IPersistFile_fnLoad, 00100 IPersistFile_fnSave, 00101 IPersistFile_fnSaveCompleted, 00102 IPersistFile_fnGetCurFile 00103 }; 00104 00105 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); 00106 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface); 00107 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface); 00108 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); 00109 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); 00110 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); 00111 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); 00112 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); 00113 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); 00114 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); 00115 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); 00116 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); 00117 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); 00118 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); 00119 00120 static const struct IAVIStreamVtbl iavist = { 00121 IAVIStream_fnQueryInterface, 00122 IAVIStream_fnAddRef, 00123 IAVIStream_fnRelease, 00124 IAVIStream_fnCreate, 00125 IAVIStream_fnInfo, 00126 IAVIStream_fnFindSample, 00127 IAVIStream_fnReadFormat, 00128 IAVIStream_fnSetFormat, 00129 IAVIStream_fnRead, 00130 IAVIStream_fnWrite, 00131 IAVIStream_fnDelete, 00132 IAVIStream_fnReadData, 00133 IAVIStream_fnWriteData, 00134 IAVIStream_fnSetInfo 00135 }; 00136 00137 typedef struct _IAVIFileImpl IAVIFileImpl; 00138 00139 typedef struct _IPersistFileImpl { 00140 /* IUnknown stuff */ 00141 const IPersistFileVtbl *lpVtbl; 00142 00143 /* IPersistFile stuff */ 00144 IAVIFileImpl *paf; 00145 } IPersistFileImpl; 00146 00147 typedef struct _IAVIStreamImpl { 00148 /* IUnknown stuff */ 00149 const IAVIStreamVtbl *lpVtbl; 00150 LONG ref; 00151 00152 /* IAVIStream stuff */ 00153 IAVIFileImpl *paf; 00154 DWORD nStream; /* the n-th stream in file */ 00155 AVISTREAMINFOW sInfo; 00156 00157 LPVOID lpFormat; 00158 DWORD cbFormat; 00159 00160 LPVOID lpHandlerData; 00161 DWORD cbHandlerData; 00162 00163 EXTRACHUNKS extra; 00164 00165 LPDWORD lpBuffer; 00166 DWORD cbBuffer; /* size of lpBuffer */ 00167 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */ 00168 00169 LONG lLastFrame; /* last correct index in idxFrames */ 00170 AVIINDEXENTRY *idxFrames; 00171 DWORD nIdxFrames; /* upper index limit of idxFrames */ 00172 AVIINDEXENTRY *idxFmtChanges; 00173 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */ 00174 } IAVIStreamImpl; 00175 00176 struct _IAVIFileImpl { 00177 /* IUnknown stuff */ 00178 const IAVIFileVtbl *lpVtbl; 00179 LONG ref; 00180 00181 /* IAVIFile stuff... */ 00182 IPersistFileImpl iPersistFile; 00183 00184 AVIFILEINFOW fInfo; 00185 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS]; 00186 00187 EXTRACHUNKS fileextra; 00188 00189 DWORD dwMoviChunkPos; /* some stuff for saving ... */ 00190 DWORD dwIdxChunkPos; 00191 DWORD dwNextFramePos; 00192 DWORD dwInitialFrames; 00193 00194 MMCKINFO ckLastRecord; 00195 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */ 00196 DWORD nIdxRecords; /* current fill level */ 00197 DWORD cbIdxRecords; /* size of idxRecords */ 00198 00199 /* IPersistFile stuff ... */ 00200 HMMIO hmmio; 00201 LPWSTR szFileName; 00202 UINT uMode; 00203 BOOL fDirty; 00204 }; 00205 00206 /***********************************************************************/ 00207 00208 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, 00209 DWORD offset, DWORD flags); 00210 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This); 00211 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This); 00212 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, 00213 const AVISTREAMINFOW *asi); 00214 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This); 00215 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); 00216 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset); 00217 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, 00218 LONG count, DWORD pos, BOOL *bAbsolute); 00219 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start, 00220 LPVOID buffer, DWORD size); 00221 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, 00222 LPLONG offset); 00223 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); 00224 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This); 00225 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType, 00226 LONG lSkip); 00227 static void AVIFILE_UpdateInfo(IAVIFileImpl *This); 00228 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, 00229 FOURCC ckid, DWORD flags, LPCVOID buffer, 00230 LONG size); 00231 00232 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv) 00233 { 00234 IAVIFileImpl *pfile; 00235 HRESULT hr; 00236 00237 assert(riid != NULL && ppv != NULL); 00238 00239 *ppv = NULL; 00240 00241 pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl)); 00242 if (pfile == NULL) 00243 return AVIERR_MEMORY; 00244 00245 pfile->lpVtbl = &iavift; 00246 pfile->ref = 0; 00247 pfile->iPersistFile.lpVtbl = &ipersistft; 00248 pfile->iPersistFile.paf = pfile; 00249 00250 hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv); 00251 if (FAILED(hr)) 00252 HeapFree(GetProcessHeap(), 0, pfile); 00253 00254 return hr; 00255 } 00256 00257 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, 00258 LPVOID *obj) 00259 { 00260 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00261 00262 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); 00263 00264 if (IsEqualGUID(&IID_IUnknown, refiid) || 00265 IsEqualGUID(&IID_IAVIFile, refiid)) { 00266 *obj = iface; 00267 IAVIFile_AddRef(iface); 00268 00269 return S_OK; 00270 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { 00271 *obj = &This->iPersistFile; 00272 IAVIFile_AddRef(iface); 00273 00274 return S_OK; 00275 } 00276 00277 return OLE_E_ENUM_NOMORE; 00278 } 00279 00280 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) 00281 { 00282 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00283 ULONG ref = InterlockedIncrement(&This->ref); 00284 00285 TRACE("(%p) -> %d\n", iface, ref); 00286 00287 return ref; 00288 } 00289 00290 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) 00291 { 00292 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00293 UINT i; 00294 ULONG ref = InterlockedDecrement(&This->ref); 00295 00296 TRACE("(%p) -> %d\n", iface, ref); 00297 00298 if (!ref) { 00299 if (This->fDirty) { 00300 /* need to write headers to file */ 00301 AVIFILE_SaveFile(This); 00302 } 00303 00304 for (i = 0; i < This->fInfo.dwStreams; i++) { 00305 if (This->ppStreams[i] != NULL) { 00306 if (This->ppStreams[i]->ref != 0) { 00307 ERR(": someone has still %u reference to stream %u (%p)!\n", 00308 This->ppStreams[i]->ref, i, This->ppStreams[i]); 00309 } 00310 AVIFILE_DestructAVIStream(This->ppStreams[i]); 00311 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]); 00312 This->ppStreams[i] = NULL; 00313 } 00314 } 00315 00316 if (This->idxRecords != NULL) { 00317 HeapFree(GetProcessHeap(), 0, This->idxRecords); 00318 This->idxRecords = NULL; 00319 This->nIdxRecords = 0; 00320 } 00321 00322 if (This->fileextra.lp != NULL) { 00323 HeapFree(GetProcessHeap(), 0, This->fileextra.lp); 00324 This->fileextra.lp = NULL; 00325 This->fileextra.cb = 0; 00326 } 00327 00328 HeapFree(GetProcessHeap(), 0, This->szFileName); 00329 This->szFileName = NULL; 00330 00331 if (This->hmmio != NULL) { 00332 mmioClose(This->hmmio, 0); 00333 This->hmmio = NULL; 00334 } 00335 00336 HeapFree(GetProcessHeap(), 0, This); 00337 } 00338 return ref; 00339 } 00340 00341 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, 00342 LONG size) 00343 { 00344 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00345 00346 TRACE("(%p,%p,%d)\n",iface,afi,size); 00347 00348 if (afi == NULL) 00349 return AVIERR_BADPARAM; 00350 if (size < 0) 00351 return AVIERR_BADSIZE; 00352 00353 AVIFILE_UpdateInfo(This); 00354 00355 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); 00356 00357 if ((DWORD)size < sizeof(This->fInfo)) 00358 return AVIERR_BUFFERTOOSMALL; 00359 return AVIERR_OK; 00360 } 00361 00362 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, 00363 DWORD fccType, LONG lParam) 00364 { 00365 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00366 00367 ULONG nStream; 00368 00369 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam); 00370 00371 if (avis == NULL || lParam < 0) 00372 return AVIERR_BADPARAM; 00373 00374 nStream = AVIFILE_SearchStream(This, fccType, lParam); 00375 00376 /* Does the requested stream exist? */ 00377 if (nStream < This->fInfo.dwStreams && 00378 This->ppStreams[nStream] != NULL) { 00379 *avis = (PAVISTREAM)This->ppStreams[nStream]; 00380 IAVIStream_AddRef(*avis); 00381 00382 return AVIERR_OK; 00383 } 00384 00385 /* Sorry, but the specified stream doesn't exist */ 00386 return AVIERR_NODATA; 00387 } 00388 00389 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, 00390 LPAVISTREAMINFOW asi) 00391 { 00392 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00393 00394 DWORD n; 00395 00396 TRACE("(%p,%p,%p)\n", iface, avis, asi); 00397 00398 /* check parameters */ 00399 if (avis == NULL || asi == NULL) 00400 return AVIERR_BADPARAM; 00401 00402 *avis = NULL; 00403 00404 /* Does the user have write permission? */ 00405 if ((This->uMode & MMIO_RWMODE) == 0) 00406 return AVIERR_READONLY; 00407 00408 /* Can we add another stream? */ 00409 n = This->fInfo.dwStreams; 00410 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) { 00411 /* already reached max nr of streams 00412 * or have already written frames to disk */ 00413 return AVIERR_UNSUPPORTED; 00414 } 00415 00416 /* check AVISTREAMINFO for some really needed things */ 00417 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0) 00418 return AVIERR_BADFORMAT; 00419 00420 /* now it seems to be save to add the stream */ 00421 assert(This->ppStreams[n] == NULL); 00422 This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 00423 sizeof(IAVIStreamImpl)); 00424 if (This->ppStreams[n] == NULL) 00425 return AVIERR_MEMORY; 00426 00427 /* initialize the new allocated stream */ 00428 AVIFILE_ConstructAVIStream(This, n, asi); 00429 00430 This->fInfo.dwStreams++; 00431 This->fDirty = TRUE; 00432 00433 /* update our AVIFILEINFO structure */ 00434 AVIFILE_UpdateInfo(This); 00435 00436 /* return it */ 00437 *avis = (PAVISTREAM)This->ppStreams[n]; 00438 IAVIStream_AddRef(*avis); 00439 00440 return AVIERR_OK; 00441 } 00442 00443 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, 00444 LPVOID lpData, LONG size) 00445 { 00446 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00447 00448 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size); 00449 00450 /* check parameters */ 00451 if (lpData == NULL) 00452 return AVIERR_BADPARAM; 00453 if (size < 0) 00454 return AVIERR_BADSIZE; 00455 00456 /* Do we have write permission? */ 00457 if ((This->uMode & MMIO_RWMODE) == 0) 00458 return AVIERR_READONLY; 00459 00460 This->fDirty = TRUE; 00461 00462 return WriteExtraChunk(&This->fileextra, ckid, lpData, size); 00463 } 00464 00465 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, 00466 LPVOID lpData, LONG *size) 00467 { 00468 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00469 00470 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size); 00471 00472 return ReadExtraChunk(&This->fileextra, ckid, lpData, size); 00473 } 00474 00475 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) 00476 { 00477 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00478 00479 TRACE("(%p)\n",iface); 00480 00481 if ((This->uMode & MMIO_RWMODE) == 0) 00482 return AVIERR_READONLY; 00483 00484 This->fDirty = TRUE; 00485 00486 /* no frames written to any stream? -- compute start of 'movi'-chunk */ 00487 if (This->dwMoviChunkPos == 0) 00488 AVIFILE_ComputeMoviStart(This); 00489 00490 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED; 00491 00492 /* already written frames to any stream, ... */ 00493 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { 00494 /* close last record */ 00495 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0) 00496 return AVIERR_FILEWRITE; 00497 00498 AVIFILE_AddRecord(This); 00499 00500 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD)) 00501 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD); 00502 } 00503 00504 /* write out a new record into file, but don't close it */ 00505 This->ckLastRecord.cksize = 0; 00506 This->ckLastRecord.fccType = listtypeAVIRECORD; 00507 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) 00508 return AVIERR_FILEWRITE; 00509 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0) 00510 return AVIERR_FILEWRITE; 00511 This->dwNextFramePos += 3 * sizeof(DWORD); 00512 00513 return AVIERR_OK; 00514 } 00515 00516 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, 00517 LONG lParam) 00518 { 00519 IAVIFileImpl *This = (IAVIFileImpl *)iface; 00520 00521 ULONG nStream; 00522 00523 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam); 00524 00525 /* check parameter */ 00526 if (lParam < 0) 00527 return AVIERR_BADPARAM; 00528 00529 /* Have user write permissions? */ 00530 if ((This->uMode & MMIO_RWMODE) == 0) 00531 return AVIERR_READONLY; 00532 00533 nStream = AVIFILE_SearchStream(This, fccType, lParam); 00534 00535 /* Does the requested stream exist? */ 00536 if (nStream < This->fInfo.dwStreams && 00537 This->ppStreams[nStream] != NULL) { 00538 /* ... so delete it now */ 00539 HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]); 00540 00541 if (This->fInfo.dwStreams - nStream > 0) 00542 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1, 00543 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*)); 00544 00545 This->ppStreams[This->fInfo.dwStreams] = NULL; 00546 This->fInfo.dwStreams--; 00547 This->fDirty = TRUE; 00548 00549 /* This->fInfo will be updated further when asked for */ 00550 return AVIERR_OK; 00551 } else 00552 return AVIERR_NODATA; 00553 } 00554 00555 /***********************************************************************/ 00556 00557 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, 00558 REFIID refiid, LPVOID *obj) 00559 { 00560 IPersistFileImpl *This = (IPersistFileImpl *)iface; 00561 00562 assert(This->paf != NULL); 00563 00564 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); 00565 } 00566 00567 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) 00568 { 00569 IPersistFileImpl *This = (IPersistFileImpl *)iface; 00570 00571 assert(This->paf != NULL); 00572 00573 return IAVIFile_AddRef((PAVIFILE)This->paf); 00574 } 00575 00576 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) 00577 { 00578 IPersistFileImpl *This = (IPersistFileImpl *)iface; 00579 00580 assert(This->paf != NULL); 00581 00582 return IAVIFile_Release((PAVIFILE)This->paf); 00583 } 00584 00585 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, 00586 LPCLSID pClassID) 00587 { 00588 TRACE("(%p,%p)\n", iface, pClassID); 00589 00590 if (pClassID == NULL) 00591 return AVIERR_BADPARAM; 00592 00593 *pClassID = CLSID_AVIFile; 00594 00595 return AVIERR_OK; 00596 } 00597 00598 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) 00599 { 00600 IPersistFileImpl *This = (IPersistFileImpl *)iface; 00601 00602 TRACE("(%p)\n", iface); 00603 00604 assert(This->paf != NULL); 00605 00606 return (This->paf->fDirty ? S_OK : S_FALSE); 00607 } 00608 00609 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, 00610 LPCOLESTR pszFileName, DWORD dwMode) 00611 { 00612 IPersistFileImpl *This = (IPersistFileImpl *)iface; 00613 00614 int len; 00615 00616 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode); 00617 00618 /* check parameter */ 00619 if (pszFileName == NULL) 00620 return AVIERR_BADPARAM; 00621 00622 assert(This->paf != NULL); 00623 if (This->paf->hmmio != NULL) 00624 return AVIERR_ERROR; /* No reuse of this object for another file! */ 00625 00626 /* remember mode and name */ 00627 This->paf->uMode = dwMode; 00628 00629 len = lstrlenW(pszFileName) + 1; 00630 This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 00631 if (This->paf->szFileName == NULL) 00632 return AVIERR_MEMORY; 00633 lstrcpyW(This->paf->szFileName, pszFileName); 00634 00635 /* try to open the file */ 00636 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL, 00637 MMIO_ALLOCBUF | dwMode); 00638 if (This->paf->hmmio == NULL) { 00639 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ 00640 LPSTR szFileName; 00641 00642 len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, 00643 NULL, 0, NULL, NULL); 00644 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR)); 00645 if (szFileName == NULL) 00646 return AVIERR_MEMORY; 00647 00648 WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName, 00649 len, NULL, NULL); 00650 00651 This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); 00652 HeapFree(GetProcessHeap(), 0, szFileName); 00653 if (This->paf->hmmio == NULL) 00654 return AVIERR_FILEOPEN; 00655 } 00656 00657 /* should we create a new file? */ 00658 if (dwMode & OF_CREATE) { 00659 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo)); 00660 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE; 00661 00662 return AVIERR_OK; 00663 } else 00664 return AVIFILE_LoadFile(This->paf); 00665 } 00666 00667 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, 00668 LPCOLESTR pszFileName,BOOL fRemember) 00669 { 00670 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); 00671 00672 /* We write directly to disk, so nothing to do. */ 00673 00674 return AVIERR_OK; 00675 } 00676 00677 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, 00678 LPCOLESTR pszFileName) 00679 { 00680 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); 00681 00682 /* We write directly to disk, so nothing to do. */ 00683 00684 return AVIERR_OK; 00685 } 00686 00687 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, 00688 LPOLESTR *ppszFileName) 00689 { 00690 IPersistFileImpl *This = (IPersistFileImpl *)iface; 00691 00692 TRACE("(%p,%p)\n", iface, ppszFileName); 00693 00694 if (ppszFileName == NULL) 00695 return AVIERR_BADPARAM; 00696 00697 *ppszFileName = NULL; 00698 00699 assert(This->paf != NULL); 00700 00701 if (This->paf->szFileName != NULL) { 00702 int len = lstrlenW(This->paf->szFileName) + 1; 00703 00704 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR)); 00705 if (*ppszFileName == NULL) 00706 return AVIERR_MEMORY; 00707 00708 strcpyW(*ppszFileName, This->paf->szFileName); 00709 } 00710 00711 return AVIERR_OK; 00712 } 00713 00714 /***********************************************************************/ 00715 00716 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, 00717 REFIID refiid, LPVOID *obj) 00718 { 00719 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00720 00721 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); 00722 00723 if (IsEqualGUID(&IID_IUnknown, refiid) || 00724 IsEqualGUID(&IID_IAVIStream, refiid)) { 00725 *obj = This; 00726 IAVIStream_AddRef(iface); 00727 00728 return S_OK; 00729 } 00730 /* FIXME: IAVIStreaming interface */ 00731 00732 return OLE_E_ENUM_NOMORE; 00733 } 00734 00735 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) 00736 { 00737 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00738 ULONG ref = InterlockedIncrement(&This->ref); 00739 00740 TRACE("(%p) -> %d\n", iface, ref); 00741 00742 /* also add ref to parent, so that it doesn't kill us */ 00743 if (This->paf != NULL) 00744 IAVIFile_AddRef((PAVIFILE)This->paf); 00745 00746 return ref; 00747 } 00748 00749 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) 00750 { 00751 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00752 ULONG ref = InterlockedDecrement(&This->ref); 00753 00754 TRACE("(%p) -> %d\n", iface, ref); 00755 00756 if (This->paf != NULL) 00757 IAVIFile_Release((PAVIFILE)This->paf); 00758 00759 return ref; 00760 } 00761 00762 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, 00763 LPARAM lParam2) 00764 { 00765 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); 00766 00767 /* This IAVIStream interface needs an AVIFile */ 00768 return AVIERR_UNSUPPORTED; 00769 } 00770 00771 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, 00772 LONG size) 00773 { 00774 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00775 00776 TRACE("(%p,%p,%d)\n", iface, psi, size); 00777 00778 if (psi == NULL) 00779 return AVIERR_BADPARAM; 00780 if (size < 0) 00781 return AVIERR_BADSIZE; 00782 00783 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); 00784 00785 if ((DWORD)size < sizeof(This->sInfo)) 00786 return AVIERR_BUFFERTOOSMALL; 00787 return AVIERR_OK; 00788 } 00789 00790 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, 00791 LONG flags) 00792 { 00793 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00794 00795 LONG offset = 0; 00796 00797 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); 00798 00799 if (flags & FIND_FROM_START) { 00800 pos = This->sInfo.dwStart; 00801 flags &= ~(FIND_FROM_START|FIND_PREV); 00802 flags |= FIND_NEXT; 00803 } 00804 00805 if (This->sInfo.dwSampleSize != 0) { 00806 /* convert samples into block number with offset */ 00807 AVIFILE_SamplesToBlock(This, &pos, &offset); 00808 } 00809 00810 if (flags & FIND_TYPE) { 00811 if (flags & FIND_KEY) { 00812 while (0 <= pos && pos <= This->lLastFrame) { 00813 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME) 00814 goto RETURN_FOUND; 00815 00816 if (flags & FIND_NEXT) 00817 pos++; 00818 else 00819 pos--; 00820 }; 00821 } else if (flags & FIND_ANY) { 00822 while (0 <= pos && pos <= This->lLastFrame) { 00823 if (This->idxFrames[pos].dwChunkLength > 0) 00824 goto RETURN_FOUND; 00825 00826 if (flags & FIND_NEXT) 00827 pos++; 00828 else 00829 pos--; 00830 00831 }; 00832 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL && 00833 This->sInfo.fccType == streamtypeVIDEO) { 00834 if (flags & FIND_NEXT) { 00835 ULONG n; 00836 00837 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++) 00838 if (This->idxFmtChanges[n].ckid >= pos) { 00839 pos = This->idxFmtChanges[n].ckid; 00840 goto RETURN_FOUND; 00841 } 00842 } else { 00843 LONG n; 00844 00845 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) { 00846 if (This->idxFmtChanges[n].ckid <= pos) { 00847 pos = This->idxFmtChanges[n].ckid; 00848 goto RETURN_FOUND; 00849 } 00850 } 00851 00852 if (pos > (LONG)This->sInfo.dwStart) 00853 return 0; /* format changes always for first frame */ 00854 } 00855 } 00856 00857 return -1; 00858 } 00859 00860 RETURN_FOUND: 00861 if (pos < (LONG)This->sInfo.dwStart) 00862 return -1; 00863 00864 switch (flags & FIND_RET) { 00865 case FIND_LENGTH: 00866 /* physical size */ 00867 pos = This->idxFrames[pos].dwChunkLength; 00868 break; 00869 case FIND_OFFSET: 00870 /* physical position */ 00871 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD) 00872 + offset * This->sInfo.dwSampleSize; 00873 break; 00874 case FIND_SIZE: 00875 /* logical size */ 00876 if (This->sInfo.dwSampleSize) 00877 pos = This->sInfo.dwSampleSize; 00878 else 00879 pos = 1; 00880 break; 00881 case FIND_INDEX: 00882 FIXME(": FIND_INDEX flag is not supported!\n"); 00883 /* This is an index in the index-table on disc. */ 00884 break; 00885 }; /* else logical position */ 00886 00887 return pos; 00888 } 00889 00890 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, 00891 LPVOID format, LONG *formatsize) 00892 { 00893 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00894 00895 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); 00896 00897 if (formatsize == NULL) 00898 return AVIERR_BADPARAM; 00899 00900 /* only interested in needed buffersize? */ 00901 if (format == NULL || *formatsize <= 0) { 00902 *formatsize = This->cbFormat; 00903 00904 return AVIERR_OK; 00905 } 00906 00907 /* copy initial format (only as much as will fit) */ 00908 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat)); 00909 if (*(DWORD*)formatsize < This->cbFormat) { 00910 *formatsize = This->cbFormat; 00911 return AVIERR_BUFFERTOOSMALL; 00912 } 00913 00914 /* Could format change? When yes will it change? */ 00915 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && 00916 pos > This->sInfo.dwStart) { 00917 LONG lLastFmt; 00918 00919 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV); 00920 if (lLastFmt > 0) { 00921 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt); 00922 } 00923 } 00924 00925 *formatsize = This->cbFormat; 00926 return AVIERR_OK; 00927 } 00928 00929 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, 00930 LPVOID format, LONG formatsize) 00931 { 00932 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 00933 00934 LPBITMAPINFOHEADER lpbiNew = format; 00935 00936 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); 00937 00938 /* check parameters */ 00939 if (format == NULL || formatsize <= 0) 00940 return AVIERR_BADPARAM; 00941 00942 /* Do we have write permission? */ 00943 if ((This->paf->uMode & MMIO_RWMODE) == 0) 00944 return AVIERR_READONLY; 00945 00946 /* can only set format before frame is written! */ 00947 if (This->lLastFrame > pos) 00948 return AVIERR_UNSUPPORTED; 00949 00950 /* initial format or a formatchange? */ 00951 if (This->lpFormat == NULL) { 00952 /* initial format */ 00953 if (This->paf->dwMoviChunkPos != 0) 00954 return AVIERR_ERROR; /* user has used API in wrong sequence! */ 00955 00956 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); 00957 if (This->lpFormat == NULL) 00958 return AVIERR_MEMORY; 00959 This->cbFormat = formatsize; 00960 00961 memcpy(This->lpFormat, format, formatsize); 00962 00963 /* update some infos about stream */ 00964 if (This->sInfo.fccType == streamtypeVIDEO) { 00965 LONG lDim; 00966 00967 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left; 00968 if (lDim < lpbiNew->biWidth) 00969 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth; 00970 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top; 00971 if (lDim < lpbiNew->biHeight) 00972 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight; 00973 } else if (This->sInfo.fccType == streamtypeAUDIO) 00974 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign; 00975 00976 return AVIERR_OK; 00977 } else { 00978 MMCKINFO ck; 00979 LPBITMAPINFOHEADER lpbiOld = This->lpFormat; 00980 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize); 00981 AVIPALCHANGE *lppc = NULL; 00982 UINT n; 00983 00984 /* perhaps format change, check it ... */ 00985 if (This->cbFormat != formatsize) 00986 return AVIERR_UNSUPPORTED; 00987 00988 /* no format change, only the initial one */ 00989 if (memcmp(This->lpFormat, format, formatsize) == 0) 00990 return AVIERR_OK; 00991 00992 /* check that's only the palette, which changes */ 00993 if (lpbiOld->biSize != lpbiNew->biSize || 00994 lpbiOld->biWidth != lpbiNew->biWidth || 00995 lpbiOld->biHeight != lpbiNew->biHeight || 00996 lpbiOld->biPlanes != lpbiNew->biPlanes || 00997 lpbiOld->biBitCount != lpbiNew->biBitCount || 00998 lpbiOld->biCompression != lpbiNew->biCompression || 00999 lpbiOld->biClrUsed != lpbiNew->biClrUsed) 01000 return AVIERR_UNSUPPORTED; 01001 01002 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; 01003 01004 /* simply say all colors have changed */ 01005 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream); 01006 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY); 01007 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 01008 if (lppc == NULL) 01009 return AVIERR_MEMORY; 01010 01011 lppc->bFirstEntry = 0; 01012 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0); 01013 lppc->wFlags = 0; 01014 for (n = 0; n < lpbiOld->biClrUsed; n++) { 01015 lppc->peNew[n].peRed = rgbNew[n].rgbRed; 01016 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen; 01017 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue; 01018 lppc->peNew[n].peFlags = 0; 01019 } 01020 01021 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 || 01022 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK || 01023 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize || 01024 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) 01025 { 01026 HeapFree(GetProcessHeap(), 0, lppc); 01027 return AVIERR_FILEWRITE; 01028 } 01029 01030 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD); 01031 01032 HeapFree(GetProcessHeap(), 0, lppc); 01033 01034 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0); 01035 } 01036 } 01037 01038 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, 01039 LONG samples, LPVOID buffer, 01040 LONG buffersize, LPLONG bytesread, 01041 LPLONG samplesread) 01042 { 01043 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 01044 01045 DWORD size; 01046 HRESULT hr; 01047 01048 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, 01049 buffersize, bytesread, samplesread); 01050 01051 /* clear return parameters if given */ 01052 if (bytesread != NULL) 01053 *bytesread = 0; 01054 if (samplesread != NULL) 01055 *samplesread = 0; 01056 01057 /* check parameters */ 01058 if ((LONG)This->sInfo.dwStart > start) 01059 return AVIERR_NODATA; /* couldn't read before start of stream */ 01060 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start) 01061 return AVIERR_NODATA; /* start is past end of stream */ 01062 01063 /* should we read as much as possible? */ 01064 if (samples == -1) { 01065 /* User should know how much we have read */ 01066 if (bytesread == NULL && samplesread == NULL) 01067 return AVIERR_BADPARAM; 01068 01069 if (This->sInfo.dwSampleSize != 0) 01070 samples = buffersize / This->sInfo.dwSampleSize; 01071 else 01072 samples = 1; 01073 } 01074 01075 /* limit to end of stream */ 01076 if ((LONG)This->sInfo.dwLength < samples) 01077 samples = This->sInfo.dwLength; 01078 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples)) 01079 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart); 01080 01081 /* nothing to read? Then leave ... */ 01082 if (samples == 0) 01083 return AVIERR_OK; 01084 01085 if (This->sInfo.dwSampleSize != 0) { 01086 /* fixed samplesize -- we can read over frame/block boundaries */ 01087 LONG block = start; 01088 LONG offset = 0; 01089 01090 if (!buffer) 01091 { 01092 if (bytesread) 01093 *bytesread = samples*This->sInfo.dwSampleSize; 01094 if (samplesread) 01095 *samplesread = samples; 01096 return AVIERR_OK; 01097 } 01098 01099 /* convert start sample to block,offset pair */ 01100 AVIFILE_SamplesToBlock(This, &block, &offset); 01101 01102 /* convert samples to bytes */ 01103 samples *= This->sInfo.dwSampleSize; 01104 01105 while (samples > 0 && buffersize > 0) { 01106 LONG blocksize; 01107 if (block != This->dwCurrentFrame) { 01108 hr = AVIFILE_ReadBlock(This, block, NULL, 0); 01109 if (FAILED(hr)) 01110 return hr; 01111 } 01112 01113 size = min((DWORD)samples, (DWORD)buffersize); 01114 blocksize = This->lpBuffer[1]; 01115 TRACE("blocksize = %u\n",blocksize); 01116 size = min(size, blocksize - offset); 01117 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size); 01118 01119 block++; 01120 offset = 0; 01121 buffer = ((LPBYTE)buffer)+size; 01122 samples -= size; 01123 buffersize -= size; 01124 01125 /* fill out return parameters if given */ 01126 if (bytesread != NULL) 01127 *bytesread += size; 01128 if (samplesread != NULL) 01129 *samplesread += size / This->sInfo.dwSampleSize; 01130 } 01131 01132 if (samples == 0) 01133 return AVIERR_OK; 01134 else 01135 return AVIERR_BUFFERTOOSMALL; 01136 } else { 01137 /* variable samplesize -- we can only read one full frame/block */ 01138 if (samples > 1) 01139 samples = 1; 01140 01141 assert(start <= This->lLastFrame); 01142 size = This->idxFrames[start].dwChunkLength; 01143 if (buffer != NULL && buffersize >= size) { 01144 hr = AVIFILE_ReadBlock(This, start, buffer, size); 01145 if (FAILED(hr)) 01146 return hr; 01147 } else if (buffer != NULL) 01148 return AVIERR_BUFFERTOOSMALL; 01149 01150 /* fill out return parameters if given */ 01151 if (bytesread != NULL) 01152 *bytesread = size; 01153 if (samplesread != NULL) 01154 *samplesread = samples; 01155 01156 return AVIERR_OK; 01157 } 01158 } 01159 01160 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, 01161 LONG samples, LPVOID buffer, 01162 LONG buffersize, DWORD flags, 01163 LPLONG sampwritten, 01164 LPLONG byteswritten) 01165 { 01166 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 01167 01168 FOURCC ckid; 01169 HRESULT hr; 01170 01171 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, 01172 buffer, buffersize, flags, sampwritten, byteswritten); 01173 01174 /* clear return parameters if given */ 01175 if (sampwritten != NULL) 01176 *sampwritten = 0; 01177 if (byteswritten != NULL) 01178 *byteswritten = 0; 01179 01180 /* check parameters */ 01181 if (buffer == NULL && (buffersize > 0 || samples > 0)) 01182 return AVIERR_BADPARAM; 01183 01184 /* Have we write permission? */ 01185 if ((This->paf->uMode & MMIO_RWMODE) == 0) 01186 return AVIERR_READONLY; 01187 01188 switch (This->sInfo.fccType) { 01189 case streamtypeAUDIO: 01190 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream); 01191 break; 01192 default: 01193 if ((flags & AVIIF_KEYFRAME) && buffersize != 0) 01194 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream); 01195 else 01196 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream); 01197 break; 01198 }; 01199 01200 /* append to end of stream? */ 01201 if (start == -1) { 01202 if (This->lLastFrame == -1) 01203 start = This->sInfo.dwStart; 01204 else 01205 start = This->sInfo.dwLength; 01206 } else if (This->lLastFrame == -1) 01207 This->sInfo.dwStart = start; 01208 01209 if (This->sInfo.dwSampleSize != 0) { 01210 /* fixed sample size -- audio like */ 01211 if (samples * This->sInfo.dwSampleSize != buffersize) 01212 return AVIERR_BADPARAM; 01213 01214 /* Couldn't skip audio-like data -- User must supply appropriate silence */ 01215 if (This->sInfo.dwLength != start) 01216 return AVIERR_UNSUPPORTED; 01217 01218 /* Convert position to frame/block */ 01219 start = This->lLastFrame + 1; 01220 01221 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) { 01222 FIXME(": not interleaved, could collect audio data!\n"); 01223 } 01224 } else { 01225 /* variable sample size -- video like */ 01226 if (samples > 1) 01227 return AVIERR_UNSUPPORTED; 01228 01229 /* must we fill up with empty frames? */ 01230 if (This->lLastFrame != -1) { 01231 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); 01232 01233 while (start > This->lLastFrame + 1) { 01234 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0); 01235 if (FAILED(hr)) 01236 return hr; 01237 } 01238 } 01239 } 01240 01241 /* write the block now */ 01242 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize); 01243 if (SUCCEEDED(hr)) { 01244 /* fill out return parameters if given */ 01245 if (sampwritten != NULL) 01246 *sampwritten = samples; 01247 if (byteswritten != NULL) 01248 *byteswritten = buffersize; 01249 } 01250 01251 return hr; 01252 } 01253 01254 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, 01255 LONG samples) 01256 { 01257 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 01258 01259 FIXME("(%p,%d,%d): stub\n", iface, start, samples); 01260 01261 /* check parameters */ 01262 if (start < 0 || samples < 0) 01263 return AVIERR_BADPARAM; 01264 01265 /* Delete before start of stream? */ 01266 if (start + samples < This->sInfo.dwStart) 01267 return AVIERR_OK; 01268 01269 /* Delete after end of stream? */ 01270 if (start > This->sInfo.dwLength) 01271 return AVIERR_OK; 01272 01273 /* For the rest we need write permissions */ 01274 if ((This->paf->uMode & MMIO_RWMODE) == 0) 01275 return AVIERR_READONLY; 01276 01277 /* 1. overwrite the data with JUNK 01278 * 01279 * if ISINTERLEAVED { 01280 * 2. concat all neighboured JUNK-blocks in this record to one 01281 * 3. if this record only contains JUNK and is at end set dwNextFramePos 01282 * to start of this record, repeat this. 01283 * } else { 01284 * 2. concat all neighboured JUNK-blocks. 01285 * 3. if the JUNK block is at the end, then set dwNextFramePos to 01286 * start of this block. 01287 * } 01288 */ 01289 01290 return AVIERR_UNSUPPORTED; 01291 } 01292 01293 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, 01294 LPVOID lp, LPLONG lpread) 01295 { 01296 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 01297 01298 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread); 01299 01300 if (fcc == ckidSTREAMHANDLERDATA) { 01301 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) { 01302 if (lp == NULL || *lpread <= 0) { 01303 *lpread = This->cbHandlerData; 01304 return AVIERR_OK; 01305 } 01306 01307 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread)); 01308 if (*lpread < This->cbHandlerData) 01309 return AVIERR_BUFFERTOOSMALL; 01310 return AVIERR_OK; 01311 } else 01312 return AVIERR_NODATA; 01313 } else 01314 return ReadExtraChunk(&This->extra, fcc, lp, lpread); 01315 } 01316 01317 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, 01318 LPVOID lp, LONG size) 01319 { 01320 IAVIStreamImpl *This = (IAVIStreamImpl *)iface; 01321 01322 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size); 01323 01324 /* check parameters */ 01325 if (lp == NULL) 01326 return AVIERR_BADPARAM; 01327 if (size <= 0) 01328 return AVIERR_BADSIZE; 01329 01330 /* need write permission */ 01331 if ((This->paf->uMode & MMIO_RWMODE) == 0) 01332 return AVIERR_READONLY; 01333 01334 /* already written something to this file? */ 01335 if (This->paf->dwMoviChunkPos != 0) { 01336 /* the data will be inserted before the 'movi' chunk, so check for 01337 * enough space */ 01338 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf); 01339 01340 /* ckid,size => 2 * sizeof(DWORD) */ 01341 dwPos += 2 * sizeof(DWORD) + size; 01342 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD)) 01343 return AVIERR_UNSUPPORTED; /* not enough space left */ 01344 } 01345 01346 This->paf->fDirty = TRUE; 01347 01348 if (fcc == ckidSTREAMHANDLERDATA) { 01349 if (This->lpHandlerData != NULL) { 01350 FIXME(": handler data already set -- overwirte?\n"); 01351 return AVIERR_UNSUPPORTED; 01352 } 01353 01354 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size); 01355 if (This->lpHandlerData == NULL) 01356 return AVIERR_MEMORY; 01357 This->cbHandlerData = size; 01358 memcpy(This->lpHandlerData, lp, size); 01359 01360 return AVIERR_OK; 01361 } else 01362 return WriteExtraChunk(&This->extra, fcc, lp, size); 01363 } 01364 01365 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, 01366 LPAVISTREAMINFOW info, LONG infolen) 01367 { 01368 FIXME("(%p,%p,%d): stub\n", iface, info, infolen); 01369 01370 return E_FAIL; 01371 } 01372 01373 /***********************************************************************/ 01374 01375 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags) 01376 { 01377 UINT n; 01378 01379 /* pre-conditions */ 01380 assert(This != NULL); 01381 01382 switch (TWOCCFromFOURCC(ckid)) { 01383 case cktypeDIBbits: 01384 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) 01385 flags |= AVIIF_KEYFRAME; 01386 break; 01387 case cktypeDIBcompressed: 01388 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) 01389 flags &= ~AVIIF_KEYFRAME; 01390 break; 01391 case cktypePALchange: 01392 if (This->sInfo.fccType != streamtypeVIDEO) { 01393 ERR(": found palette change in non-video stream!\n"); 01394 return AVIERR_BADFORMAT; 01395 } 01396 01397 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) { 01398 DWORD new_count = This->nIdxFmtChanges + 16; 01399 void *new_buffer; 01400 01401 if (This->idxFmtChanges == NULL) { 01402 This->idxFmtChanges = 01403 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY)); 01404 if (!This->idxFmtChanges) return AVIERR_MEMORY; 01405 } else { 01406 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges, 01407 new_count * sizeof(AVIINDEXENTRY)); 01408 if (!new_buffer) return AVIERR_MEMORY; 01409 This->idxFmtChanges = new_buffer; 01410 } 01411 This->nIdxFmtChanges = new_count; 01412 } 01413 01414 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; 01415 n = ++This->sInfo.dwFormatChangeCount; 01416 This->idxFmtChanges[n].ckid = This->lLastFrame; 01417 This->idxFmtChanges[n].dwFlags = 0; 01418 This->idxFmtChanges[n].dwChunkOffset = offset; 01419 This->idxFmtChanges[n].dwChunkLength = size; 01420 01421 return AVIERR_OK; 01422 case cktypeWAVEbytes: 01423 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) 01424 flags |= AVIIF_KEYFRAME; 01425 break; 01426 default: 01427 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid)); 01428 break; 01429 }; 01430 01431 /* first frame is always a keyframe */ 01432 if (This->lLastFrame == -1) 01433 flags |= AVIIF_KEYFRAME; 01434 01435 if (This->sInfo.dwSuggestedBufferSize < size) 01436 This->sInfo.dwSuggestedBufferSize = size; 01437 01438 /* get memory for index */ 01439 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) { 01440 This->nIdxFrames += 512; 01441 if (This->idxFrames == NULL) 01442 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY)); 01443 else 01444 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames, 01445 This->nIdxFrames * sizeof(AVIINDEXENTRY)); 01446 if (This->idxFrames == NULL) 01447 return AVIERR_MEMORY; 01448 } 01449 01450 This->lLastFrame++; 01451 This->idxFrames[This->lLastFrame].ckid = ckid; 01452 This->idxFrames[This->lLastFrame].dwFlags = flags; 01453 This->idxFrames[This->lLastFrame].dwChunkOffset = offset; 01454 This->idxFrames[This->lLastFrame].dwChunkLength = size; 01455 01456 /* update AVISTREAMINFO structure if necessary */ 01457 if (This->sInfo.dwLength <= This->lLastFrame) 01458 This->sInfo.dwLength = This->lLastFrame + 1; 01459 01460 return AVIERR_OK; 01461 } 01462 01463 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This) 01464 { 01465 /* pre-conditions */ 01466 assert(This != NULL && This->ppStreams[0] != NULL); 01467 01468 if (This->idxRecords == NULL || This->cbIdxRecords == 0) { 01469 This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY); 01470 This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords); 01471 if (This->idxRecords == NULL) 01472 return AVIERR_MEMORY; 01473 } 01474 01475 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY)); 01476 01477 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD; 01478 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST; 01479 This->idxRecords[This->nIdxRecords].dwChunkOffset = 01480 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD); 01481 This->idxRecords[This->nIdxRecords].dwChunkLength = 01482 This->ckLastRecord.cksize; 01483 This->nIdxRecords++; 01484 01485 return AVIERR_OK; 01486 } 01487 01488 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This) 01489 { 01490 DWORD dwPos; 01491 DWORD nStream; 01492 01493 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */ 01494 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader); 01495 01496 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 01497 IAVIStreamImpl *pStream = This->ppStreams[nStream]; 01498 01499 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */ 01500 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader); 01501 dwPos += ((pStream->cbFormat + 1) & ~1U); 01502 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) 01503 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U); 01504 if (lstrlenW(pStream->sInfo.szName) > 0) 01505 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U); 01506 } 01507 01508 if (This->dwMoviChunkPos == 0) { 01509 This->dwNextFramePos = dwPos; 01510 01511 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */ 01512 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD)) 01513 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1); 01514 01515 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD); 01516 } 01517 01518 return dwPos; 01519 } 01520 01521 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi) 01522 { 01523 IAVIStreamImpl *pstream; 01524 01525 /* pre-conditions */ 01526 assert(paf != NULL); 01527 assert(nr < MAX_AVISTREAMS); 01528 assert(paf->ppStreams[nr] != NULL); 01529 01530 pstream = paf->ppStreams[nr]; 01531 01532 pstream->lpVtbl = &iavist; 01533 pstream->ref = 0; 01534 pstream->paf = paf; 01535 pstream->nStream = nr; 01536 pstream->dwCurrentFrame = (DWORD)-1; 01537 pstream->lLastFrame = -1; 01538 01539 if (asi != NULL) { 01540 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo)); 01541 01542 if (asi->dwLength > 0) { 01543 /* pre-allocate mem for frame-index structure */ 01544 pstream->idxFrames = 01545 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY)); 01546 if (pstream->idxFrames != NULL) 01547 pstream->nIdxFrames = asi->dwLength; 01548 } 01549 if (asi->dwFormatChangeCount > 0) { 01550 /* pre-allocate mem for formatchange-index structure */ 01551 pstream->idxFmtChanges = 01552 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY)); 01553 if (pstream->idxFmtChanges != NULL) 01554 pstream->nIdxFmtChanges = asi->dwFormatChangeCount; 01555 } 01556 01557 /* These values will be computed */ 01558 pstream->sInfo.dwLength = 0; 01559 pstream->sInfo.dwSuggestedBufferSize = 0; 01560 pstream->sInfo.dwFormatChangeCount = 0; 01561 pstream->sInfo.dwEditCount = 1; 01562 if (pstream->sInfo.dwSampleSize > 0) 01563 SetRectEmpty(&pstream->sInfo.rcFrame); 01564 } 01565 01566 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 01567 } 01568 01569 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This) 01570 { 01571 /* pre-conditions */ 01572 assert(This != NULL); 01573 01574 This->dwCurrentFrame = (DWORD)-1; 01575 This->lLastFrame = -1; 01576 This->paf = NULL; 01577 if (This->idxFrames != NULL) { 01578 HeapFree(GetProcessHeap(), 0, This->idxFrames); 01579 This->idxFrames = NULL; 01580 This->nIdxFrames = 0; 01581 } 01582 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges); 01583 This->idxFmtChanges = NULL; 01584 if (This->lpBuffer != NULL) { 01585 HeapFree(GetProcessHeap(), 0, This->lpBuffer); 01586 This->lpBuffer = NULL; 01587 This->cbBuffer = 0; 01588 } 01589 if (This->lpHandlerData != NULL) { 01590 HeapFree(GetProcessHeap(), 0, This->lpHandlerData); 01591 This->lpHandlerData = NULL; 01592 This->cbHandlerData = 0; 01593 } 01594 if (This->extra.lp != NULL) { 01595 HeapFree(GetProcessHeap(), 0, This->extra.lp); 01596 This->extra.lp = NULL; 01597 This->extra.cb = 0; 01598 } 01599 if (This->lpFormat != NULL) { 01600 HeapFree(GetProcessHeap(), 0, This->lpFormat); 01601 This->lpFormat = NULL; 01602 This->cbFormat = 0; 01603 } 01604 } 01605 01606 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) 01607 { 01608 MainAVIHeader MainAVIHdr; 01609 MMCKINFO ckRIFF; 01610 MMCKINFO ckLIST1; 01611 MMCKINFO ckLIST2; 01612 MMCKINFO ck; 01613 IAVIStreamImpl *pStream; 01614 DWORD nStream; 01615 HRESULT hr; 01616 01617 if (This->hmmio == NULL) 01618 return AVIERR_FILEOPEN; 01619 01620 /* initialize stream ptr's */ 01621 memset(This->ppStreams, 0, sizeof(This->ppStreams)); 01622 01623 /* try to get "RIFF" chunk -- must not be at beginning of file! */ 01624 ckRIFF.fccType = formtypeAVI; 01625 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { 01626 ERR(": not an AVI!\n"); 01627 return AVIERR_FILEREAD; 01628 } 01629 01630 /* get "LIST" "hdrl" */ 01631 ckLIST1.fccType = listtypeAVIHEADER; 01632 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST); 01633 if (FAILED(hr)) 01634 return hr; 01635 01636 /* get "avih" chunk */ 01637 ck.ckid = ckidAVIMAINHDR; 01638 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK); 01639 if (FAILED(hr)) 01640 return hr; 01641 01642 if (ck.cksize != sizeof(MainAVIHdr)) { 01643 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize); 01644 return AVIERR_BADFORMAT; 01645 } 01646 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) 01647 return AVIERR_FILEREAD; 01648 01649 /* check for MAX_AVISTREAMS limit */ 01650 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) { 01651 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS); 01652 return AVIERR_UNSUPPORTED; 01653 } 01654 01655 /* adjust permissions if copyrighted material in file */ 01656 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) { 01657 This->uMode &= ~MMIO_RWMODE; 01658 This->uMode |= MMIO_READ; 01659 } 01660 01661 /* convert MainAVIHeader into AVIFILINFOW */ 01662 memset(&This->fInfo, 0, sizeof(This->fInfo)); 01663 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame; 01664 This->fInfo.dwScale = 1000000; 01665 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec; 01666 This->fInfo.dwFlags = MainAVIHdr.dwFlags; 01667 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 01668 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames; 01669 This->fInfo.dwStreams = MainAVIHdr.dwStreams; 01670 This->fInfo.dwSuggestedBufferSize = 0; 01671 This->fInfo.dwWidth = MainAVIHdr.dwWidth; 01672 This->fInfo.dwHeight = MainAVIHdr.dwHeight; 01673 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType, 01674 sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0])); 01675 01676 /* go back to into header list */ 01677 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 01678 return AVIERR_FILEREAD; 01679 01680 /* foreach stream exists a "LIST","strl" chunk */ 01681 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 01682 /* get next nested chunk in this "LIST","strl" */ 01683 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK) 01684 return AVIERR_FILEREAD; 01685 01686 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */ 01687 if (ckLIST2.ckid == FOURCC_LIST && 01688 ckLIST2.fccType == listtypeSTREAMHEADER) { 01689 pStream = This->ppStreams[nStream] = 01690 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl)); 01691 if (pStream == NULL) 01692 return AVIERR_MEMORY; 01693 AVIFILE_ConstructAVIStream(This, nStream, NULL); 01694 01695 ck.ckid = 0; 01696 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) { 01697 switch (ck.ckid) { 01698 case ckidSTREAMHANDLERDATA: 01699 if (pStream->lpHandlerData != NULL) 01700 return AVIERR_BADFORMAT; 01701 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 01702 if (pStream->lpHandlerData == NULL) 01703 return AVIERR_MEMORY; 01704 pStream->cbHandlerData = ck.cksize; 01705 01706 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) 01707 return AVIERR_FILEREAD; 01708 break; 01709 case ckidSTREAMFORMAT: 01710 if (pStream->lpFormat != NULL) 01711 return AVIERR_BADFORMAT; 01712 if (ck.cksize == 0) 01713 break; 01714 01715 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 01716 if (pStream->lpFormat == NULL) 01717 return AVIERR_MEMORY; 01718 pStream->cbFormat = ck.cksize; 01719 01720 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) 01721 return AVIERR_FILEREAD; 01722 01723 if (pStream->sInfo.fccType == streamtypeVIDEO) { 01724 LPBITMAPINFOHEADER lpbi = pStream->lpFormat; 01725 01726 /* some corrections to the video format */ 01727 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) 01728 lpbi->biClrUsed = 1u << lpbi->biBitCount; 01729 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0) 01730 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight; 01731 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) { 01732 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') || 01733 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' ')) 01734 lpbi->biCompression = BI_RLE8; 01735 } 01736 if (lpbi->biCompression == BI_RGB && 01737 (pStream->sInfo.fccHandler == 0 || 01738 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))) 01739 pStream->sInfo.fccHandler = comptypeDIB; 01740 01741 /* init rcFrame if it's empty */ 01742 if (IsRectEmpty(&pStream->sInfo.rcFrame)) 01743 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight); 01744 } 01745 break; 01746 case ckidSTREAMHEADER: 01747 { 01748 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0}; 01749 static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0}; 01750 01751 AVIStreamHeader streamHdr; 01752 WCHAR szType[25]; 01753 UINT count; 01754 LONG n = ck.cksize; 01755 01756 if (ck.cksize > sizeof(streamHdr)) 01757 n = sizeof(streamHdr); 01758 01759 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n) 01760 return AVIERR_FILEREAD; 01761 01762 pStream->sInfo.fccType = streamHdr.fccType; 01763 pStream->sInfo.fccHandler = streamHdr.fccHandler; 01764 pStream->sInfo.dwFlags = streamHdr.dwFlags; 01765 pStream->sInfo.wPriority = streamHdr.wPriority; 01766 pStream->sInfo.wLanguage = streamHdr.wLanguage; 01767 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames; 01768 pStream->sInfo.dwScale = streamHdr.dwScale; 01769 pStream->sInfo.dwRate = streamHdr.dwRate; 01770 pStream->sInfo.dwStart = streamHdr.dwStart; 01771 pStream->sInfo.dwLength = streamHdr.dwLength; 01772 pStream->sInfo.dwSuggestedBufferSize = 0; 01773 pStream->sInfo.dwQuality = streamHdr.dwQuality; 01774 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize; 01775 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left; 01776 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top; 01777 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right; 01778 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom; 01779 pStream->sInfo.dwEditCount = 0; 01780 pStream->sInfo.dwFormatChangeCount = 0; 01781 01782 /* generate description for stream like "filename.avi Type #n" */ 01783 if (streamHdr.fccType == streamtypeVIDEO) 01784 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0])); 01785 else if (streamHdr.fccType == streamtypeAUDIO) 01786 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0])); 01787 else 01788 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType); 01789 01790 /* get count of this streamtype up to this stream */ 01791 count = 0; 01792 for (n = nStream; 0 <= n; n--) { 01793 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType) 01794 count++; 01795 } 01796 01797 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName)); 01798 01799 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */ 01800 wsprintfW(pStream->sInfo.szName, streamNameFmt, 01801 AVIFILE_BasenameW(This->szFileName), szType, count); 01802 } 01803 break; 01804 case ckidSTREAMNAME: 01805 { /* streamname will be saved as ASCII string */ 01806 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 01807 if (str == NULL) 01808 return AVIERR_MEMORY; 01809 01810 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize) 01811 { 01812 HeapFree(GetProcessHeap(), 0, str); 01813 return AVIERR_FILEREAD; 01814 } 01815 01816 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName, 01817 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0])); 01818 01819 HeapFree(GetProcessHeap(), 0, str); 01820 } 01821 break; 01822 case ckidAVIPADDING: 01823 case mmioFOURCC('p','a','d','d'): 01824 break; 01825 default: 01826 WARN(": found extra chunk 0x%08X\n", ck.ckid); 01827 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck); 01828 if (FAILED(hr)) 01829 return hr; 01830 }; 01831 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO) 01832 { 01833 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */ 01834 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */ 01835 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); 01836 pStream->sInfo.dwScale = 1; 01837 pStream->sInfo.dwRate = wfx->nSamplesPerSec; 01838 } 01839 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 01840 return AVIERR_FILEREAD; 01841 } 01842 } else { 01843 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */ 01844 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2); 01845 if (FAILED(hr)) 01846 return hr; 01847 } 01848 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) 01849 return AVIERR_FILEREAD; 01850 } 01851 01852 /* read any extra headers in "LIST","hdrl" */ 01853 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0); 01854 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 01855 return AVIERR_FILEREAD; 01856 01857 /* search "LIST","movi" chunk in "RIFF","AVI " */ 01858 ckLIST1.fccType = listtypeAVIMOVIE; 01859 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, 01860 MMIO_FINDLIST); 01861 if (FAILED(hr)) 01862 return hr; 01863 01864 This->dwMoviChunkPos = ckLIST1.dwDataOffset; 01865 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset; 01866 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 01867 return AVIERR_FILEREAD; 01868 01869 /* try to find an index */ 01870 ck.ckid = ckidAVINEWINDEX; 01871 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, 01872 &ck, &ckRIFF, MMIO_FINDCHUNK); 01873 if (SUCCEEDED(hr) && ck.cksize > 0) { 01874 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset))) 01875 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX; 01876 } 01877 01878 /* when we haven't found an index or it's bad, then build one 01879 * by parsing 'movi' chunk */ 01880 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) { 01881 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) 01882 This->ppStreams[nStream]->lLastFrame = -1; 01883 01884 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) { 01885 ERR(": Oops, can't seek back to 'movi' chunk!\n"); 01886 return AVIERR_FILEREAD; 01887 } 01888 01889 /* seek through the 'movi' list until end */ 01890 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) { 01891 if (ck.ckid != FOURCC_LIST) { 01892 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) { 01893 nStream = StreamFromFOURCC(ck.ckid); 01894 01895 if (nStream > This->fInfo.dwStreams) 01896 return AVIERR_BADFORMAT; 01897 01898 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, 01899 ck.dwDataOffset - 2 * sizeof(DWORD), 0); 01900 } else { 01901 nStream = StreamFromFOURCC(ck.ckid); 01902 WARN(": file seems to be truncated!\n"); 01903 if (nStream <= This->fInfo.dwStreams && 01904 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) { 01905 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END); 01906 if (ck.cksize != -1) { 01907 ck.cksize -= ck.dwDataOffset; 01908 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1); 01909 01910 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, 01911 ck.dwDataOffset - 2 * sizeof(DWORD), 0); 01912 } 01913 } 01914 } 01915 } 01916 } 01917 } 01918 01919 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) 01920 { 01921 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize; 01922 if (This->fInfo.dwSuggestedBufferSize < sugbuf) 01923 This->fInfo.dwSuggestedBufferSize = sugbuf; 01924 } 01925 01926 /* find other chunks */ 01927 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0); 01928 01929 return AVIERR_OK; 01930 } 01931 01932 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset) 01933 { 01934 AVIINDEXENTRY *lp; 01935 DWORD pos, n; 01936 HRESULT hr = AVIERR_OK; 01937 BOOL bAbsolute = TRUE; 01938 01939 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY)); 01940 if (lp == NULL) 01941 return AVIERR_MEMORY; 01942 01943 /* adjust limits for index tables, so that inserting will be faster */ 01944 for (n = 0; n < This->fInfo.dwStreams; n++) { 01945 IAVIStreamImpl *pStream = This->ppStreams[n]; 01946 01947 pStream->lLastFrame = -1; 01948 01949 if (pStream->idxFrames != NULL) { 01950 HeapFree(GetProcessHeap(), 0, pStream->idxFrames); 01951 pStream->idxFrames = NULL; 01952 pStream->nIdxFrames = 0; 01953 } 01954 01955 if (pStream->sInfo.dwSampleSize != 0) { 01956 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { 01957 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames; 01958 } else if (pStream->sInfo.dwSuggestedBufferSize) { 01959 pStream->nIdxFrames = 01960 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize; 01961 } 01962 } else 01963 pStream->nIdxFrames = pStream->sInfo.dwLength; 01964 01965 pStream->idxFrames = 01966 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY)); 01967 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) { 01968 pStream->nIdxFrames = 0; 01969 HeapFree(GetProcessHeap(), 0, lp); 01970 return AVIERR_MEMORY; 01971 } 01972 } 01973 01974 pos = (DWORD)-1; 01975 while (size != 0) { 01976 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size); 01977 01978 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) { 01979 hr = AVIERR_FILEREAD; 01980 break; 01981 } 01982 size -= read; 01983 01984 if (pos == (DWORD)-1) 01985 pos = offset - lp->dwChunkOffset + sizeof(DWORD); 01986 01987 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY), 01988 pos, &bAbsolute); 01989 } 01990 01991 HeapFree(GetProcessHeap(), 0, lp); 01992 01993 /* checking ... */ 01994 for (n = 0; n < This->fInfo.dwStreams; n++) { 01995 IAVIStreamImpl *pStream = This->ppStreams[n]; 01996 01997 if (pStream->sInfo.dwSampleSize == 0 && 01998 pStream->sInfo.dwLength != pStream->lLastFrame+1) 01999 ERR("stream %u length mismatch: dwLength=%u found=%d\n", 02000 n, pStream->sInfo.dwLength, pStream->lLastFrame); 02001 } 02002 02003 return hr; 02004 } 02005 02006 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, 02007 LONG count, DWORD pos, BOOL *bAbsolute) 02008 { 02009 if (lp == NULL) 02010 return AVIERR_BADPARAM; 02011 02012 for (; count > 0; count--, lp++) { 02013 WORD nStream = StreamFromFOURCC(lp->ckid); 02014 02015 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F) 02016 continue; /* skip these */ 02017 02018 if (nStream > This->fInfo.dwStreams) 02019 return AVIERR_BADFORMAT; 02020 02021 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos) 02022 *bAbsolute = FALSE; 02023 02024 if (*bAbsolute) 02025 lp->dwChunkOffset += sizeof(DWORD); 02026 else 02027 lp->dwChunkOffset += pos; 02028 02029 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags))) 02030 return AVIERR_MEMORY; 02031 } 02032 02033 return AVIERR_OK; 02034 } 02035 02036 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos, 02037 LPVOID buffer, DWORD size) 02038 { 02039 /* pre-conditions */ 02040 assert(This != NULL); 02041 assert(This->paf != NULL); 02042 assert(This->paf->hmmio != NULL); 02043 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); 02044 assert(pos <= This->lLastFrame); 02045 02046 /* should we read as much as block gives us? */ 02047 if (size == 0 || size > This->idxFrames[pos].dwChunkLength) 02048 size = This->idxFrames[pos].dwChunkLength; 02049 02050 /* read into out own buffer or given one? */ 02051 if (buffer == NULL) { 02052 /* we also read the chunk */ 02053 size += 2 * sizeof(DWORD); 02054 02055 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */ 02056 if (This->lpBuffer == NULL || This->cbBuffer < size) { 02057 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize); 02058 02059 if (This->lpBuffer == NULL) { 02060 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize); 02061 if (!This->lpBuffer) return AVIERR_MEMORY; 02062 } else { 02063 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize); 02064 if (!new_buffer) return AVIERR_MEMORY; 02065 This->lpBuffer = new_buffer; 02066 } 02067 This->cbBuffer = maxSize; 02068 } 02069 02070 /* now read the complete chunk into our buffer */ 02071 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1) 02072 return AVIERR_FILEREAD; 02073 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size) 02074 return AVIERR_FILEREAD; 02075 02076 /* check if it was the correct block which we have read */ 02077 if (This->lpBuffer[0] != This->idxFrames[pos].ckid || 02078 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) { 02079 ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset); 02080 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n", 02081 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid, 02082 This->idxFrames[pos].dwChunkLength); 02083 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n", 02084 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]); 02085 return AVIERR_FILEREAD; 02086 } 02087 } else { 02088 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1) 02089 return AVIERR_FILEREAD; 02090 if (mmioRead(This->paf->hmmio, buffer, size) != size) 02091 return AVIERR_FILEREAD; 02092 } 02093 02094 return AVIERR_OK; 02095 } 02096 02097 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset) 02098 { 02099 LONG block; 02100 02101 /* pre-conditions */ 02102 assert(This != NULL); 02103 assert(pos != NULL); 02104 assert(offset != NULL); 02105 assert(This->sInfo.dwSampleSize != 0); 02106 assert(*pos >= This->sInfo.dwStart); 02107 02108 /* convert start sample to start bytes */ 02109 (*offset) = (*pos) - This->sInfo.dwStart; 02110 (*offset) *= This->sInfo.dwSampleSize; 02111 02112 /* convert bytes to block number */ 02113 for (block = 0; block <= This->lLastFrame; block++) { 02114 if (This->idxFrames[block].dwChunkLength <= *offset) 02115 (*offset) -= This->idxFrames[block].dwChunkLength; 02116 else 02117 break; 02118 } 02119 02120 *pos = block; 02121 } 02122 02123 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) 02124 { 02125 MainAVIHeader MainAVIHdr; 02126 IAVIStreamImpl* pStream; 02127 MMCKINFO ckRIFF; 02128 MMCKINFO ckLIST1; 02129 MMCKINFO ckLIST2; 02130 MMCKINFO ck; 02131 DWORD nStream; 02132 DWORD dwPos; 02133 HRESULT hr; 02134 02135 /* initialize some things */ 02136 if (This->dwMoviChunkPos == 0) 02137 AVIFILE_ComputeMoviStart(This); 02138 02139 /* written one record to much? */ 02140 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { 02141 This->dwNextFramePos -= 3 * sizeof(DWORD); 02142 if (This->nIdxRecords > 0) 02143 This->nIdxRecords--; 02144 } 02145 02146 AVIFILE_UpdateInfo(This); 02147 02148 assert(This->fInfo.dwScale != 0); 02149 02150 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr)); 02151 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000, 02152 This->fInfo.dwScale); 02153 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec; 02154 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE; 02155 MainAVIHdr.dwFlags = This->fInfo.dwFlags; 02156 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength; 02157 MainAVIHdr.dwInitialFrames = 0; 02158 MainAVIHdr.dwStreams = This->fInfo.dwStreams; 02159 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize; 02160 MainAVIHdr.dwWidth = This->fInfo.dwWidth; 02161 MainAVIHdr.dwHeight = This->fInfo.dwHeight; 02162 MainAVIHdr.dwInitialFrames = This->dwInitialFrames; 02163 02164 /* now begin writing ... */ 02165 mmioSeek(This->hmmio, 0, SEEK_SET); 02166 02167 /* RIFF chunk */ 02168 ckRIFF.cksize = 0; 02169 ckRIFF.fccType = formtypeAVI; 02170 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) 02171 return AVIERR_FILEWRITE; 02172 02173 /* AVI headerlist */ 02174 ckLIST1.cksize = 0; 02175 ckLIST1.fccType = listtypeAVIHEADER; 02176 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) 02177 return AVIERR_FILEWRITE; 02178 02179 /* MainAVIHeader */ 02180 ck.ckid = ckidAVIMAINHDR; 02181 ck.cksize = sizeof(MainAVIHdr); 02182 ck.fccType = 0; 02183 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02184 return AVIERR_FILEWRITE; 02185 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) 02186 return AVIERR_FILEWRITE; 02187 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02188 return AVIERR_FILEWRITE; 02189 02190 /* write the headers of each stream into a separate streamheader list */ 02191 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 02192 AVIStreamHeader strHdr; 02193 02194 pStream = This->ppStreams[nStream]; 02195 02196 /* begin the new streamheader list */ 02197 ckLIST2.cksize = 0; 02198 ckLIST2.fccType = listtypeSTREAMHEADER; 02199 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK) 02200 return AVIERR_FILEWRITE; 02201 02202 /* create an AVIStreamHeader from the AVSTREAMINFO */ 02203 strHdr.fccType = pStream->sInfo.fccType; 02204 strHdr.fccHandler = pStream->sInfo.fccHandler; 02205 strHdr.dwFlags = pStream->sInfo.dwFlags; 02206 strHdr.wPriority = pStream->sInfo.wPriority; 02207 strHdr.wLanguage = pStream->sInfo.wLanguage; 02208 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames; 02209 strHdr.dwScale = pStream->sInfo.dwScale; 02210 strHdr.dwRate = pStream->sInfo.dwRate; 02211 strHdr.dwStart = pStream->sInfo.dwStart; 02212 strHdr.dwLength = pStream->sInfo.dwLength; 02213 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize; 02214 strHdr.dwQuality = pStream->sInfo.dwQuality; 02215 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize; 02216 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left; 02217 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top; 02218 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right; 02219 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom; 02220 02221 /* now write the AVIStreamHeader */ 02222 ck.ckid = ckidSTREAMHEADER; 02223 ck.cksize = sizeof(strHdr); 02224 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02225 return AVIERR_FILEWRITE; 02226 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize) 02227 return AVIERR_FILEWRITE; 02228 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02229 return AVIERR_FILEWRITE; 02230 02231 /* ... the hopefully ever present streamformat ... */ 02232 ck.ckid = ckidSTREAMFORMAT; 02233 ck.cksize = pStream->cbFormat; 02234 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02235 return AVIERR_FILEWRITE; 02236 if (pStream->lpFormat != NULL && ck.cksize > 0) { 02237 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) 02238 return AVIERR_FILEWRITE; 02239 } 02240 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02241 return AVIERR_FILEWRITE; 02242 02243 /* ... some optional existing handler data ... */ 02244 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) { 02245 ck.ckid = ckidSTREAMHANDLERDATA; 02246 ck.cksize = pStream->cbHandlerData; 02247 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02248 return AVIERR_FILEWRITE; 02249 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) 02250 return AVIERR_FILEWRITE; 02251 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02252 return AVIERR_FILEWRITE; 02253 } 02254 02255 /* ... some optional additional extra chunk for this stream ... */ 02256 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) { 02257 /* the chunk header(s) are already in the structure */ 02258 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb) 02259 return AVIERR_FILEWRITE; 02260 } 02261 02262 /* ... an optional name for this stream ... */ 02263 if (lstrlenW(pStream->sInfo.szName) > 0) { 02264 LPSTR str; 02265 02266 ck.ckid = ckidSTREAMNAME; 02267 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1; 02268 if (ck.cksize & 1) /* align */ 02269 ck.cksize++; 02270 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02271 return AVIERR_FILEWRITE; 02272 02273 /* the streamname must be saved in ASCII not Unicode */ 02274 str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 02275 if (str == NULL) 02276 return AVIERR_MEMORY; 02277 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str, 02278 ck.cksize, NULL, NULL); 02279 02280 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) { 02281 HeapFree(GetProcessHeap(), 0, str); 02282 return AVIERR_FILEWRITE; 02283 } 02284 02285 HeapFree(GetProcessHeap(), 0, str); 02286 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02287 return AVIERR_FILEWRITE; 02288 } 02289 02290 /* close streamheader list for this stream */ 02291 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) 02292 return AVIERR_FILEWRITE; 02293 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */ 02294 02295 /* close the aviheader list */ 02296 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 02297 return AVIERR_FILEWRITE; 02298 02299 /* check for padding to pre-guessed 'movi'-chunk position */ 02300 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize; 02301 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) { 02302 ck.ckid = ckidAVIPADDING; 02303 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD); 02304 assert((LONG)ck.cksize >= 0); 02305 02306 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02307 return AVIERR_FILEWRITE; 02308 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1) 02309 return AVIERR_FILEWRITE; 02310 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02311 return AVIERR_FILEWRITE; 02312 } 02313 02314 /* now write the 'movi' chunk */ 02315 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET); 02316 ckLIST1.cksize = 0; 02317 ckLIST1.fccType = listtypeAVIMOVIE; 02318 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) 02319 return AVIERR_FILEWRITE; 02320 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) 02321 return AVIERR_FILEWRITE; 02322 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 02323 return AVIERR_FILEWRITE; 02324 02325 /* write 'idx1' chunk */ 02326 hr = AVIFILE_SaveIndex(This); 02327 if (FAILED(hr)) 02328 return hr; 02329 02330 /* write optional extra file chunks */ 02331 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) { 02332 /* as for the streams, are the chunk header(s) in the structure */ 02333 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb) 02334 return AVIERR_FILEWRITE; 02335 } 02336 02337 /* close RIFF chunk */ 02338 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) 02339 return AVIERR_FILEWRITE; 02340 02341 /* add some JUNK at end for bad parsers */ 02342 memset(&ckRIFF, 0, sizeof(ckRIFF)); 02343 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF)); 02344 mmioFlush(This->hmmio, 0); 02345 02346 return AVIERR_OK; 02347 } 02348 02349 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This) 02350 { 02351 IAVIStreamImpl *pStream; 02352 AVIINDEXENTRY idx; 02353 MMCKINFO ck; 02354 DWORD nStream; 02355 LONG n; 02356 02357 ck.ckid = ckidAVINEWINDEX; 02358 ck.cksize = 0; 02359 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 02360 return AVIERR_FILEWRITE; 02361 02362 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { 02363 /* is interleaved -- write block of corresponding frames */ 02364 LONG lInitialFrames = 0; 02365 LONG stepsize; 02366 LONG i; 02367 02368 if (This->ppStreams[0]->sInfo.dwSampleSize == 0) 02369 stepsize = 1; 02370 else 02371 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000); 02372 02373 assert(stepsize > 0); 02374 02375 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 02376 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames) 02377 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames; 02378 } 02379 02380 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames; 02381 i += stepsize) { 02382 DWORD nFrame = lInitialFrames + i; 02383 02384 assert(nFrame < This->nIdxRecords); 02385 02386 idx.ckid = listtypeAVIRECORD; 02387 idx.dwFlags = AVIIF_LIST; 02388 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength; 02389 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset 02390 - This->dwMoviChunkPos; 02391 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 02392 return AVIERR_FILEWRITE; 02393 02394 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 02395 pStream = This->ppStreams[nStream]; 02396 02397 /* heave we reached start of this stream? */ 02398 if (-(LONG)pStream->sInfo.dwInitialFrames > i) 02399 continue; 02400 02401 if (pStream->sInfo.dwInitialFrames < lInitialFrames) 02402 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames); 02403 02404 /* reached end of this stream? */ 02405 if (pStream->lLastFrame <= nFrame) 02406 continue; 02407 02408 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && 02409 pStream->sInfo.dwFormatChangeCount != 0 && 02410 pStream->idxFmtChanges != NULL) { 02411 DWORD pos; 02412 02413 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { 02414 if (pStream->idxFmtChanges[pos].ckid == nFrame) { 02415 idx.dwFlags = AVIIF_NOTIME; 02416 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); 02417 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; 02418 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset 02419 - This->dwMoviChunkPos; 02420 02421 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 02422 return AVIERR_FILEWRITE; 02423 break; 02424 } 02425 } 02426 } /* if have formatchanges */ 02427 02428 idx.ckid = pStream->idxFrames[nFrame].ckid; 02429 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags; 02430 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength; 02431 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset 02432 - This->dwMoviChunkPos; 02433 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 02434 return AVIERR_FILEWRITE; 02435 } 02436 } 02437 } else { 02438 /* not interleaved -- write index for each stream at once */ 02439 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 02440 pStream = This->ppStreams[nStream]; 02441 02442 for (n = 0; n <= pStream->lLastFrame; n++) { 02443 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && 02444 (pStream->sInfo.dwFormatChangeCount != 0)) { 02445 DWORD pos; 02446 02447 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { 02448 if (pStream->idxFmtChanges[pos].ckid == n) { 02449 idx.dwFlags = AVIIF_NOTIME; 02450 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); 02451 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; 02452 idx.dwChunkOffset = 02453 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos; 02454 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 02455 return AVIERR_FILEWRITE; 02456 break; 02457 } 02458 } 02459 } /* if have formatchanges */ 02460 02461 idx.ckid = pStream->idxFrames[n].ckid; 02462 idx.dwFlags = pStream->idxFrames[n].dwFlags; 02463 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength; 02464 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset 02465 - This->dwMoviChunkPos; 02466 02467 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 02468 return AVIERR_FILEWRITE; 02469 } 02470 } 02471 } /* if not interleaved */ 02472 02473 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 02474 return AVIERR_FILEWRITE; 02475 02476 return AVIERR_OK; 02477 } 02478 02479 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip) 02480 { 02481 UINT i; 02482 UINT nStream; 02483 02484 /* pre-condition */ 02485 assert(lSkip >= 0); 02486 02487 if (fcc != 0) { 02488 /* search the number of the specified stream */ 02489 nStream = (ULONG)-1; 02490 for (i = 0; i < This->fInfo.dwStreams; i++) { 02491 assert(This->ppStreams[i] != NULL); 02492 02493 if (This->ppStreams[i]->sInfo.fccType == fcc) { 02494 if (lSkip == 0) { 02495 nStream = i; 02496 break; 02497 } else 02498 lSkip--; 02499 } 02500 } 02501 } else 02502 nStream = lSkip; 02503 02504 return nStream; 02505 } 02506 02507 static void AVIFILE_UpdateInfo(IAVIFileImpl *This) 02508 { 02509 UINT i; 02510 02511 /* pre-conditions */ 02512 assert(This != NULL); 02513 02514 This->fInfo.dwMaxBytesPerSec = 0; 02515 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 02516 This->fInfo.dwSuggestedBufferSize = 0; 02517 This->fInfo.dwWidth = 0; 02518 This->fInfo.dwHeight = 0; 02519 This->fInfo.dwScale = 0; 02520 This->fInfo.dwRate = 0; 02521 This->fInfo.dwLength = 0; 02522 This->dwInitialFrames = 0; 02523 02524 for (i = 0; i < This->fInfo.dwStreams; i++) { 02525 AVISTREAMINFOW *psi; 02526 DWORD n; 02527 02528 /* pre-conditions */ 02529 assert(This->ppStreams[i] != NULL); 02530 02531 psi = &This->ppStreams[i]->sInfo; 02532 assert(psi->dwScale != 0); 02533 assert(psi->dwRate != 0); 02534 02535 if (i == 0) { 02536 /* use first stream timings as base */ 02537 This->fInfo.dwScale = psi->dwScale; 02538 This->fInfo.dwRate = psi->dwRate; 02539 This->fInfo.dwLength = psi->dwLength; 02540 } else { 02541 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0], 02542 (PAVISTREAM)This->ppStreams[i],psi->dwLength); 02543 if (This->fInfo.dwLength < n) 02544 This->fInfo.dwLength = n; 02545 } 02546 02547 if (This->dwInitialFrames < psi->dwInitialFrames) 02548 This->dwInitialFrames = psi->dwInitialFrames; 02549 02550 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize) 02551 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize; 02552 02553 if (psi->dwSampleSize != 0) { 02554 /* fixed sample size -- exact computation */ 02555 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate, 02556 psi->dwScale); 02557 } else { 02558 /* variable sample size -- only upper limit */ 02559 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize, 02560 psi->dwRate, psi->dwScale); 02561 02562 /* update dimensions */ 02563 n = psi->rcFrame.right - psi->rcFrame.left; 02564 if (This->fInfo.dwWidth < n) 02565 This->fInfo.dwWidth = n; 02566 n = psi->rcFrame.bottom - psi->rcFrame.top; 02567 if (This->fInfo.dwHeight < n) 02568 This->fInfo.dwHeight = n; 02569 } 02570 } 02571 } 02572 02573 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, 02574 FOURCC ckid, DWORD flags, LPCVOID buffer, 02575 LONG size) 02576 { 02577 MMCKINFO ck; 02578 02579 ck.ckid = ckid; 02580 ck.cksize = size; 02581 ck.fccType = 0; 02582 02583 /* if no frame/block is already written, we must compute start of movi chunk */ 02584 if (This->paf->dwMoviChunkPos == 0) 02585 AVIFILE_ComputeMoviStart(This->paf); 02586 02587 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1) 02588 return AVIERR_FILEWRITE; 02589 02590 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK) 02591 return AVIERR_FILEWRITE; 02592 if (buffer != NULL && size > 0) { 02593 if (mmioWrite(This->paf->hmmio, buffer, size) != size) 02594 return AVIERR_FILEWRITE; 02595 } 02596 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) 02597 return AVIERR_FILEWRITE; 02598 02599 This->paf->fDirty = TRUE; 02600 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); 02601 02602 return AVIFILE_AddFrame(This, ckid, size, 02603 ck.dwDataOffset - 2 * sizeof(DWORD), flags); 02604 } Generated on Fri May 25 2012 04:20:49 for ReactOS by
1.7.6.1
|