1 /* 2 * Copyright 1999 Marcus Meissner 3 * Copyright 2002-2003 Michael Günnewig 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 /* TODO: 21 * - IAVIStreaming interface is missing for the IAVIStreamImpl 22 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported. 23 * - IAVIStream_fnReadFormat: formatchanges aren't read in. 24 * - IAVIStream_fnDelete: a stub. 25 * - IAVIStream_fnSetInfo: a stub. 26 * - make thread safe 27 * 28 * KNOWN Bugs: 29 * - native version can hangup when reading a file generated with this DLL. 30 * When index is missing it works, but index seems to be okay. 31 */ 32 33 #define COBJMACROS 34 #include <assert.h> 35 #include <stdarg.h> 36 37 #include "windef.h" 38 #include "winbase.h" 39 #include "wingdi.h" 40 #include "winuser.h" 41 #include "winnls.h" 42 #include "winerror.h" 43 #include "mmsystem.h" 44 #include "vfw.h" 45 46 #include "avifile_private.h" 47 #include "extrachunk.h" 48 49 #include "wine/debug.h" 50 51 WINE_DEFAULT_DEBUG_CHANNEL(avifile); 52 53 #ifndef IDX_PER_BLOCK 54 #define IDX_PER_BLOCK 2730 55 #endif 56 57 typedef struct _IAVIFileImpl IAVIFileImpl; 58 59 typedef struct _IAVIStreamImpl { 60 IAVIStream IAVIStream_iface; 61 LONG ref; 62 63 IAVIFileImpl *paf; 64 DWORD nStream; /* the n-th stream in file */ 65 AVISTREAMINFOW sInfo; 66 67 LPVOID lpFormat; 68 DWORD cbFormat; 69 70 LPVOID lpHandlerData; 71 DWORD cbHandlerData; 72 73 EXTRACHUNKS extra; 74 75 LPDWORD lpBuffer; 76 DWORD cbBuffer; /* size of lpBuffer */ 77 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */ 78 79 LONG lLastFrame; /* last correct index in idxFrames */ 80 AVIINDEXENTRY *idxFrames; 81 DWORD nIdxFrames; /* upper index limit of idxFrames */ 82 AVIINDEXENTRY *idxFmtChanges; 83 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */ 84 } IAVIStreamImpl; 85 86 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface) 87 { 88 return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface); 89 } 90 91 struct _IAVIFileImpl { 92 IUnknown IUnknown_inner; 93 IAVIFile IAVIFile_iface; 94 IPersistFile IPersistFile_iface; 95 IUnknown *outer_unk; 96 LONG ref; 97 98 AVIFILEINFOW fInfo; 99 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS]; 100 101 EXTRACHUNKS fileextra; 102 103 DWORD dwMoviChunkPos; /* some stuff for saving ... */ 104 DWORD dwIdxChunkPos; 105 DWORD dwNextFramePos; 106 DWORD dwInitialFrames; 107 108 MMCKINFO ckLastRecord; 109 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */ 110 DWORD nIdxRecords; /* current fill level */ 111 DWORD cbIdxRecords; /* size of idxRecords */ 112 113 /* IPersistFile stuff ... */ 114 HMMIO hmmio; 115 LPWSTR szFileName; 116 UINT uMode; 117 BOOL fDirty; 118 }; 119 120 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface) 121 { 122 return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner); 123 } 124 125 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface) 126 { 127 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface); 128 } 129 130 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface) 131 { 132 return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface); 133 } 134 135 /***********************************************************************/ 136 137 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, 138 DWORD offset, DWORD flags); 139 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This); 140 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This); 141 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, 142 const AVISTREAMINFOW *asi); 143 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This); 144 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); 145 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset); 146 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, 147 LONG count, DWORD pos, BOOL *bAbsolute); 148 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start, 149 LPVOID buffer, DWORD size); 150 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, 151 LPLONG offset); 152 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); 153 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This); 154 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType, 155 LONG lSkip); 156 static void AVIFILE_UpdateInfo(IAVIFileImpl *This); 157 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, 158 FOURCC ckid, DWORD flags, LPCVOID buffer, 159 LONG size); 160 161 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv) 162 { 163 IAVIFileImpl *This = impl_from_IUnknown(iface); 164 165 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv); 166 167 if (!ppv) { 168 WARN("invalid parameter\n"); 169 return E_INVALIDARG; 170 } 171 *ppv = NULL; 172 173 if (IsEqualIID(riid, &IID_IUnknown)) 174 *ppv = &This->IUnknown_inner; 175 else if (IsEqualIID(riid, &IID_IAVIFile)) 176 *ppv = &This->IAVIFile_iface; 177 else if (IsEqualGUID(riid, &IID_IPersistFile)) 178 *ppv = &This->IPersistFile_iface; 179 else { 180 WARN("unknown IID %s\n", debugstr_guid(riid)); 181 return E_NOINTERFACE; 182 } 183 184 /* Violation of the COM aggregation ref counting rule */ 185 IUnknown_AddRef(&This->IUnknown_inner); 186 return S_OK; 187 } 188 189 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface) 190 { 191 IAVIFileImpl *This = impl_from_IUnknown(iface); 192 ULONG ref = InterlockedIncrement(&This->ref); 193 194 TRACE("(%p) ref=%d\n", This, ref); 195 196 return ref; 197 } 198 199 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface) 200 { 201 IAVIFileImpl *This = impl_from_IUnknown(iface); 202 ULONG ref = InterlockedDecrement(&This->ref); 203 UINT i; 204 205 TRACE("(%p) ref=%d\n", This, ref); 206 207 if (!ref) { 208 if (This->fDirty) 209 AVIFILE_SaveFile(This); 210 211 for (i = 0; i < This->fInfo.dwStreams; i++) { 212 if (This->ppStreams[i] != NULL) { 213 if (This->ppStreams[i]->ref != 0) 214 ERR(": someone has still %u reference to stream %u (%p)!\n", 215 This->ppStreams[i]->ref, i, This->ppStreams[i]); 216 AVIFILE_DestructAVIStream(This->ppStreams[i]); 217 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]); 218 This->ppStreams[i] = NULL; 219 } 220 } 221 222 if (This->idxRecords != NULL) { 223 HeapFree(GetProcessHeap(), 0, This->idxRecords); 224 This->idxRecords = NULL; 225 This->nIdxRecords = 0; 226 } 227 228 if (This->fileextra.lp != NULL) { 229 HeapFree(GetProcessHeap(), 0, This->fileextra.lp); 230 This->fileextra.lp = NULL; 231 This->fileextra.cb = 0; 232 } 233 234 HeapFree(GetProcessHeap(), 0, This->szFileName); 235 This->szFileName = NULL; 236 237 if (This->hmmio != NULL) { 238 mmioClose(This->hmmio, 0); 239 This->hmmio = NULL; 240 } 241 242 HeapFree(GetProcessHeap(), 0, This); 243 } 244 return ref; 245 } 246 247 static const IUnknownVtbl unk_vtbl = 248 { 249 IUnknown_fnQueryInterface, 250 IUnknown_fnAddRef, 251 IUnknown_fnRelease 252 }; 253 254 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv) 255 { 256 IAVIFileImpl *This = impl_from_IAVIFile(iface); 257 258 return IUnknown_QueryInterface(This->outer_unk, riid, ppv); 259 } 260 261 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) 262 { 263 IAVIFileImpl *This = impl_from_IAVIFile(iface); 264 265 return IUnknown_AddRef(This->outer_unk); 266 } 267 268 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) 269 { 270 IAVIFileImpl *This = impl_from_IAVIFile(iface); 271 272 return IUnknown_Release(This->outer_unk); 273 } 274 275 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size) 276 { 277 IAVIFileImpl *This = impl_from_IAVIFile(iface); 278 279 TRACE("(%p,%p,%d)\n",iface,afi,size); 280 281 if (afi == NULL) 282 return AVIERR_BADPARAM; 283 if (size < 0) 284 return AVIERR_BADSIZE; 285 286 AVIFILE_UpdateInfo(This); 287 288 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); 289 290 if ((DWORD)size < sizeof(This->fInfo)) 291 return AVIERR_BUFFERTOOSMALL; 292 return AVIERR_OK; 293 } 294 295 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType, 296 LONG lParam) 297 { 298 IAVIFileImpl *This = impl_from_IAVIFile(iface); 299 ULONG nStream; 300 301 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam); 302 303 if (avis == NULL || lParam < 0) 304 return AVIERR_BADPARAM; 305 306 nStream = AVIFILE_SearchStream(This, fccType, lParam); 307 308 /* Does the requested stream exist? */ 309 if (nStream < This->fInfo.dwStreams && 310 This->ppStreams[nStream] != NULL) { 311 *avis = &This->ppStreams[nStream]->IAVIStream_iface; 312 IAVIStream_AddRef(*avis); 313 314 return AVIERR_OK; 315 } 316 317 /* Sorry, but the specified stream doesn't exist */ 318 *avis = NULL; 319 return AVIERR_NODATA; 320 } 321 322 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis, 323 AVISTREAMINFOW *asi) 324 { 325 IAVIFileImpl *This = impl_from_IAVIFile(iface); 326 DWORD n; 327 328 TRACE("(%p,%p,%p)\n", iface, avis, asi); 329 330 /* check parameters */ 331 if (avis == NULL || asi == NULL) 332 return AVIERR_BADPARAM; 333 334 *avis = NULL; 335 336 /* Does the user have write permission? */ 337 if ((This->uMode & MMIO_RWMODE) == 0) 338 return AVIERR_READONLY; 339 340 /* Can we add another stream? */ 341 n = This->fInfo.dwStreams; 342 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) { 343 /* already reached max nr of streams 344 * or have already written frames to disk */ 345 return AVIERR_UNSUPPORTED; 346 } 347 348 /* check AVISTREAMINFO for some really needed things */ 349 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0) 350 return AVIERR_BADFORMAT; 351 352 /* now it seems to be save to add the stream */ 353 assert(This->ppStreams[n] == NULL); 354 This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 355 sizeof(IAVIStreamImpl)); 356 if (This->ppStreams[n] == NULL) 357 return AVIERR_MEMORY; 358 359 /* initialize the new allocated stream */ 360 AVIFILE_ConstructAVIStream(This, n, asi); 361 362 This->fInfo.dwStreams++; 363 This->fDirty = TRUE; 364 365 /* update our AVIFILEINFO structure */ 366 AVIFILE_UpdateInfo(This); 367 368 /* return it */ 369 *avis = &This->ppStreams[n]->IAVIStream_iface; 370 IAVIStream_AddRef(*avis); 371 372 return AVIERR_OK; 373 } 374 375 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size) 376 { 377 IAVIFileImpl *This = impl_from_IAVIFile(iface); 378 379 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size); 380 381 /* check parameters */ 382 if (lpData == NULL) 383 return AVIERR_BADPARAM; 384 if (size < 0) 385 return AVIERR_BADSIZE; 386 387 /* Do we have write permission? */ 388 if ((This->uMode & MMIO_RWMODE) == 0) 389 return AVIERR_READONLY; 390 391 This->fDirty = TRUE; 392 393 return WriteExtraChunk(&This->fileextra, ckid, lpData, size); 394 } 395 396 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size) 397 { 398 IAVIFileImpl *This = impl_from_IAVIFile(iface); 399 400 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size); 401 402 return ReadExtraChunk(&This->fileextra, ckid, lpData, size); 403 } 404 405 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) 406 { 407 IAVIFileImpl *This = impl_from_IAVIFile(iface); 408 409 TRACE("(%p)\n",iface); 410 411 if ((This->uMode & MMIO_RWMODE) == 0) 412 return AVIERR_READONLY; 413 414 This->fDirty = TRUE; 415 416 /* no frames written to any stream? -- compute start of 'movi'-chunk */ 417 if (This->dwMoviChunkPos == 0) 418 AVIFILE_ComputeMoviStart(This); 419 420 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED; 421 422 /* already written frames to any stream, ... */ 423 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { 424 /* close last record */ 425 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0) 426 return AVIERR_FILEWRITE; 427 428 AVIFILE_AddRecord(This); 429 430 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD)) 431 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD); 432 } 433 434 /* write out a new record into file, but don't close it */ 435 This->ckLastRecord.cksize = 0; 436 This->ckLastRecord.fccType = listtypeAVIRECORD; 437 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) 438 return AVIERR_FILEWRITE; 439 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0) 440 return AVIERR_FILEWRITE; 441 This->dwNextFramePos += 3 * sizeof(DWORD); 442 443 return AVIERR_OK; 444 } 445 446 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam) 447 { 448 IAVIFileImpl *This = impl_from_IAVIFile(iface); 449 ULONG nStream; 450 451 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam); 452 453 /* check parameter */ 454 if (lParam < 0) 455 return AVIERR_BADPARAM; 456 457 /* Have user write permissions? */ 458 if ((This->uMode & MMIO_RWMODE) == 0) 459 return AVIERR_READONLY; 460 461 nStream = AVIFILE_SearchStream(This, fccType, lParam); 462 463 /* Does the requested stream exist? */ 464 if (nStream < This->fInfo.dwStreams && 465 This->ppStreams[nStream] != NULL) { 466 /* ... so delete it now */ 467 HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]); 468 This->fInfo.dwStreams--; 469 if (nStream < This->fInfo.dwStreams) 470 memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1], 471 (This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0])); 472 473 This->ppStreams[This->fInfo.dwStreams] = NULL; 474 This->fDirty = TRUE; 475 476 /* This->fInfo will be updated further when asked for */ 477 return AVIERR_OK; 478 } else 479 return AVIERR_NODATA; 480 } 481 482 static const struct IAVIFileVtbl avif_vt = { 483 IAVIFile_fnQueryInterface, 484 IAVIFile_fnAddRef, 485 IAVIFile_fnRelease, 486 IAVIFile_fnInfo, 487 IAVIFile_fnGetStream, 488 IAVIFile_fnCreateStream, 489 IAVIFile_fnWriteData, 490 IAVIFile_fnReadData, 491 IAVIFile_fnEndRecord, 492 IAVIFile_fnDeleteStream 493 }; 494 495 496 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv) 497 { 498 IAVIFileImpl *This = impl_from_IPersistFile(iface); 499 500 return IUnknown_QueryInterface(This->outer_unk, riid, ppv); 501 } 502 503 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) 504 { 505 IAVIFileImpl *This = impl_from_IPersistFile(iface); 506 507 return IUnknown_AddRef(This->outer_unk); 508 } 509 510 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) 511 { 512 IAVIFileImpl *This = impl_from_IPersistFile(iface); 513 514 return IUnknown_Release(This->outer_unk); 515 } 516 517 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID) 518 { 519 TRACE("(%p,%p)\n", iface, pClassID); 520 521 if (pClassID == NULL) 522 return AVIERR_BADPARAM; 523 524 *pClassID = CLSID_AVIFile; 525 526 return AVIERR_OK; 527 } 528 529 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) 530 { 531 IAVIFileImpl *This = impl_from_IPersistFile(iface); 532 533 TRACE("(%p)\n", iface); 534 535 return (This->fDirty ? S_OK : S_FALSE); 536 } 537 538 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode) 539 { 540 IAVIFileImpl *This = impl_from_IPersistFile(iface); 541 int len; 542 543 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode); 544 545 /* check parameter */ 546 if (pszFileName == NULL) 547 return AVIERR_BADPARAM; 548 549 if (This->hmmio != NULL) 550 return AVIERR_ERROR; /* No reuse of this object for another file! */ 551 552 /* remember mode and name */ 553 This->uMode = dwMode; 554 555 len = lstrlenW(pszFileName) + 1; 556 This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 557 if (This->szFileName == NULL) 558 return AVIERR_MEMORY; 559 lstrcpyW(This->szFileName, pszFileName); 560 561 /* try to open the file */ 562 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode); 563 if (This->hmmio == NULL) { 564 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ 565 LPSTR szFileName; 566 567 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL); 568 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR)); 569 if (szFileName == NULL) 570 return AVIERR_MEMORY; 571 572 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL); 573 574 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); 575 HeapFree(GetProcessHeap(), 0, szFileName); 576 if (This->hmmio == NULL) 577 return AVIERR_FILEOPEN; 578 } 579 580 /* should we create a new file? */ 581 if (dwMode & OF_CREATE) { 582 memset(& This->fInfo, 0, sizeof(This->fInfo)); 583 This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE; 584 585 return AVIERR_OK; 586 } else 587 return AVIFILE_LoadFile(This); 588 } 589 590 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName, 591 BOOL fRemember) 592 { 593 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); 594 595 /* We write directly to disk, so nothing to do. */ 596 597 return AVIERR_OK; 598 } 599 600 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName) 601 { 602 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); 603 604 /* We write directly to disk, so nothing to do. */ 605 606 return AVIERR_OK; 607 } 608 609 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName) 610 { 611 IAVIFileImpl *This = impl_from_IPersistFile(iface); 612 613 TRACE("(%p,%p)\n", iface, ppszFileName); 614 615 if (ppszFileName == NULL) 616 return AVIERR_BADPARAM; 617 618 *ppszFileName = NULL; 619 620 if (This->szFileName != NULL) { 621 int len = lstrlenW(This->szFileName) + 1; 622 623 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR)); 624 if (*ppszFileName == NULL) 625 return AVIERR_MEMORY; 626 627 lstrcpyW(*ppszFileName, This->szFileName); 628 } 629 630 return AVIERR_OK; 631 } 632 633 static const struct IPersistFileVtbl pf_vt = { 634 IPersistFile_fnQueryInterface, 635 IPersistFile_fnAddRef, 636 IPersistFile_fnRelease, 637 IPersistFile_fnGetClassID, 638 IPersistFile_fnIsDirty, 639 IPersistFile_fnLoad, 640 IPersistFile_fnSave, 641 IPersistFile_fnSaveCompleted, 642 IPersistFile_fnGetCurFile 643 }; 644 645 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv) 646 { 647 IAVIFileImpl *obj; 648 HRESULT hr; 649 650 *ppv = NULL; 651 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl)); 652 if (!obj) 653 return AVIERR_MEMORY; 654 655 obj->IUnknown_inner.lpVtbl = &unk_vtbl; 656 obj->IAVIFile_iface.lpVtbl = &avif_vt; 657 obj->IPersistFile_iface.lpVtbl = &pf_vt; 658 obj->ref = 1; 659 if (pUnkOuter) 660 obj->outer_unk = pUnkOuter; 661 else 662 obj->outer_unk = &obj->IUnknown_inner; 663 664 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv); 665 IUnknown_Release(&obj->IUnknown_inner); 666 667 return hr; 668 } 669 670 671 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv) 672 { 673 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 674 675 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv); 676 677 if (!ppv) { 678 WARN("invalid parameter\n"); 679 return E_INVALIDARG; 680 } 681 *ppv = NULL; 682 683 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) { 684 *ppv = iface; 685 IAVIStream_AddRef(iface); 686 687 return S_OK; 688 } 689 /* FIXME: IAVIStreaming interface */ 690 691 return E_NOINTERFACE; 692 } 693 694 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) 695 { 696 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 697 ULONG ref = InterlockedIncrement(&This->ref); 698 699 TRACE("(%p) ref=%d\n", This, ref); 700 701 /* also add ref to parent, so that it doesn't kill us */ 702 if (This->paf != NULL) 703 IAVIFile_AddRef(&This->paf->IAVIFile_iface); 704 705 return ref; 706 } 707 708 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface) 709 { 710 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 711 ULONG ref = InterlockedDecrement(&This->ref); 712 713 TRACE("(%p) ref=%d\n", This, ref); 714 715 if (This->paf != NULL) 716 IAVIFile_Release(&This->paf->IAVIFile_iface); 717 718 return ref; 719 } 720 721 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2) 722 { 723 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); 724 725 /* This IAVIStream interface needs an AVIFile */ 726 return AVIERR_UNSUPPORTED; 727 } 728 729 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size) 730 { 731 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 732 733 TRACE("(%p,%p,%d)\n", iface, psi, size); 734 735 if (psi == NULL) 736 return AVIERR_BADPARAM; 737 if (size < 0) 738 return AVIERR_BADSIZE; 739 740 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); 741 742 if ((DWORD)size < sizeof(This->sInfo)) 743 return AVIERR_BUFFERTOOSMALL; 744 return AVIERR_OK; 745 } 746 747 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags) 748 { 749 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 750 LONG offset = 0; 751 752 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); 753 754 if (flags & FIND_FROM_START) { 755 pos = This->sInfo.dwStart; 756 flags &= ~(FIND_FROM_START|FIND_PREV); 757 flags |= FIND_NEXT; 758 } 759 760 if (This->sInfo.dwSampleSize != 0) { 761 /* convert samples into block number with offset */ 762 AVIFILE_SamplesToBlock(This, &pos, &offset); 763 } 764 765 if (flags & FIND_TYPE) { 766 if (flags & FIND_KEY) { 767 while (0 <= pos && pos <= This->lLastFrame) { 768 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME) 769 goto RETURN_FOUND; 770 771 if (flags & FIND_NEXT) 772 pos++; 773 else 774 pos--; 775 }; 776 } else if (flags & FIND_ANY) { 777 while (0 <= pos && pos <= This->lLastFrame) { 778 if (This->idxFrames[pos].dwChunkLength > 0) 779 goto RETURN_FOUND; 780 781 if (flags & FIND_NEXT) 782 pos++; 783 else 784 pos--; 785 786 }; 787 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL && 788 This->sInfo.fccType == streamtypeVIDEO) { 789 if (flags & FIND_NEXT) { 790 ULONG n; 791 792 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++) 793 if (This->idxFmtChanges[n].ckid >= pos) { 794 pos = This->idxFmtChanges[n].ckid; 795 goto RETURN_FOUND; 796 } 797 } else { 798 LONG n; 799 800 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) { 801 if (This->idxFmtChanges[n].ckid <= pos) { 802 pos = This->idxFmtChanges[n].ckid; 803 goto RETURN_FOUND; 804 } 805 } 806 807 if (pos > (LONG)This->sInfo.dwStart) 808 return 0; /* format changes always for first frame */ 809 } 810 } 811 812 return -1; 813 } 814 815 RETURN_FOUND: 816 if (pos < (LONG)This->sInfo.dwStart) 817 return -1; 818 819 switch (flags & FIND_RET) { 820 case FIND_LENGTH: 821 /* physical size */ 822 pos = This->idxFrames[pos].dwChunkLength; 823 break; 824 case FIND_OFFSET: 825 /* physical position */ 826 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD) 827 + offset * This->sInfo.dwSampleSize; 828 break; 829 case FIND_SIZE: 830 /* logical size */ 831 if (This->sInfo.dwSampleSize) 832 pos = This->sInfo.dwSampleSize; 833 else 834 pos = 1; 835 break; 836 case FIND_INDEX: 837 FIXME(": FIND_INDEX flag is not supported!\n"); 838 /* This is an index in the index-table on disc. */ 839 break; 840 }; /* else logical position */ 841 842 return pos; 843 } 844 845 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format, 846 LONG *formatsize) 847 { 848 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 849 850 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); 851 852 if (formatsize == NULL) 853 return AVIERR_BADPARAM; 854 855 /* only interested in needed buffersize? */ 856 if (format == NULL || *formatsize <= 0) { 857 *formatsize = This->cbFormat; 858 859 return AVIERR_OK; 860 } 861 862 /* copy initial format (only as much as will fit) */ 863 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat)); 864 if (*(DWORD*)formatsize < This->cbFormat) { 865 *formatsize = This->cbFormat; 866 return AVIERR_BUFFERTOOSMALL; 867 } 868 869 /* Could format change? When yes will it change? */ 870 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && 871 pos > This->sInfo.dwStart) { 872 LONG lLastFmt; 873 874 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV); 875 if (lLastFmt > 0) { 876 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt); 877 } 878 } 879 880 *formatsize = This->cbFormat; 881 return AVIERR_OK; 882 } 883 884 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format, 885 LONG formatsize) 886 { 887 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 888 BITMAPINFOHEADER *lpbiNew = format; 889 890 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); 891 892 /* check parameters */ 893 if (format == NULL || formatsize <= 0) 894 return AVIERR_BADPARAM; 895 896 /* Do we have write permission? */ 897 if ((This->paf->uMode & MMIO_RWMODE) == 0) 898 return AVIERR_READONLY; 899 900 /* can only set format before frame is written! */ 901 if (This->lLastFrame > pos) 902 return AVIERR_UNSUPPORTED; 903 904 /* initial format or a formatchange? */ 905 if (This->lpFormat == NULL) { 906 /* initial format */ 907 if (This->paf->dwMoviChunkPos != 0) 908 return AVIERR_ERROR; /* user has used API in wrong sequence! */ 909 910 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); 911 if (This->lpFormat == NULL) 912 return AVIERR_MEMORY; 913 This->cbFormat = formatsize; 914 915 memcpy(This->lpFormat, format, formatsize); 916 917 /* update some infos about stream */ 918 if (This->sInfo.fccType == streamtypeVIDEO) { 919 LONG lDim; 920 921 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left; 922 if (lDim < lpbiNew->biWidth) 923 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth; 924 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top; 925 if (lDim < lpbiNew->biHeight) 926 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight; 927 } else if (This->sInfo.fccType == streamtypeAUDIO) 928 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign; 929 930 return AVIERR_OK; 931 } else { 932 MMCKINFO ck; 933 LPBITMAPINFOHEADER lpbiOld = This->lpFormat; 934 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize); 935 AVIPALCHANGE *lppc = NULL; 936 UINT n; 937 938 /* perhaps format change, check it ... */ 939 if (This->cbFormat != formatsize) 940 return AVIERR_UNSUPPORTED; 941 942 /* no format change, only the initial one */ 943 if (memcmp(This->lpFormat, format, formatsize) == 0) 944 return AVIERR_OK; 945 946 /* check that's only the palette, which changes */ 947 if (lpbiOld->biSize != lpbiNew->biSize || 948 lpbiOld->biWidth != lpbiNew->biWidth || 949 lpbiOld->biHeight != lpbiNew->biHeight || 950 lpbiOld->biPlanes != lpbiNew->biPlanes || 951 lpbiOld->biBitCount != lpbiNew->biBitCount || 952 lpbiOld->biCompression != lpbiNew->biCompression || 953 lpbiOld->biClrUsed != lpbiNew->biClrUsed) 954 return AVIERR_UNSUPPORTED; 955 956 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; 957 958 /* simply say all colors have changed */ 959 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream); 960 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY); 961 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 962 if (lppc == NULL) 963 return AVIERR_MEMORY; 964 965 lppc->bFirstEntry = 0; 966 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0); 967 lppc->wFlags = 0; 968 for (n = 0; n < lpbiOld->biClrUsed; n++) { 969 lppc->peNew[n].peRed = rgbNew[n].rgbRed; 970 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen; 971 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue; 972 lppc->peNew[n].peFlags = 0; 973 } 974 975 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 || 976 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK || 977 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize || 978 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) 979 { 980 HeapFree(GetProcessHeap(), 0, lppc); 981 return AVIERR_FILEWRITE; 982 } 983 984 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD); 985 986 HeapFree(GetProcessHeap(), 0, lppc); 987 988 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0); 989 } 990 } 991 992 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer, 993 LONG buffersize, LONG *bytesread, LONG *samplesread) 994 { 995 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 996 DWORD size; 997 HRESULT hr; 998 999 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, 1000 buffersize, bytesread, samplesread); 1001 1002 /* clear return parameters if given */ 1003 if (bytesread != NULL) 1004 *bytesread = 0; 1005 if (samplesread != NULL) 1006 *samplesread = 0; 1007 1008 /* check parameters */ 1009 if ((LONG)This->sInfo.dwStart > start) 1010 return AVIERR_NODATA; /* couldn't read before start of stream */ 1011 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start) 1012 return AVIERR_NODATA; /* start is past end of stream */ 1013 1014 /* should we read as much as possible? */ 1015 if (samples == -1) { 1016 /* User should know how much we have read */ 1017 if (bytesread == NULL && samplesread == NULL) 1018 return AVIERR_BADPARAM; 1019 1020 if (This->sInfo.dwSampleSize != 0) 1021 samples = buffersize / This->sInfo.dwSampleSize; 1022 else 1023 samples = 1; 1024 } 1025 1026 /* limit to end of stream */ 1027 if ((LONG)This->sInfo.dwLength < samples) 1028 samples = This->sInfo.dwLength; 1029 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples)) 1030 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart); 1031 1032 /* nothing to read? Then leave ... */ 1033 if (samples == 0) 1034 return AVIERR_OK; 1035 1036 if (This->sInfo.dwSampleSize != 0) { 1037 /* fixed samplesize -- we can read over frame/block boundaries */ 1038 LONG block = start; 1039 LONG offset = 0; 1040 1041 if (!buffer) 1042 { 1043 if (bytesread) 1044 *bytesread = samples*This->sInfo.dwSampleSize; 1045 if (samplesread) 1046 *samplesread = samples; 1047 return AVIERR_OK; 1048 } 1049 1050 /* convert start sample to block,offset pair */ 1051 AVIFILE_SamplesToBlock(This, &block, &offset); 1052 1053 /* convert samples to bytes */ 1054 samples *= This->sInfo.dwSampleSize; 1055 1056 while (samples > 0 && buffersize > 0) { 1057 LONG blocksize; 1058 if (block != This->dwCurrentFrame) { 1059 hr = AVIFILE_ReadBlock(This, block, NULL, 0); 1060 if (FAILED(hr)) 1061 return hr; 1062 } 1063 1064 size = min((DWORD)samples, (DWORD)buffersize); 1065 blocksize = This->lpBuffer[1]; 1066 TRACE("blocksize = %u\n",blocksize); 1067 size = min(size, blocksize - offset); 1068 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size); 1069 1070 block++; 1071 offset = 0; 1072 buffer = ((LPBYTE)buffer)+size; 1073 samples -= size; 1074 buffersize -= size; 1075 1076 /* fill out return parameters if given */ 1077 if (bytesread != NULL) 1078 *bytesread += size; 1079 if (samplesread != NULL) 1080 *samplesread += size / This->sInfo.dwSampleSize; 1081 } 1082 1083 if (samples == 0) 1084 return AVIERR_OK; 1085 else 1086 return AVIERR_BUFFERTOOSMALL; 1087 } else { 1088 /* variable samplesize -- we can only read one full frame/block */ 1089 if (samples > 1) 1090 samples = 1; 1091 1092 assert(start <= This->lLastFrame); 1093 size = This->idxFrames[start].dwChunkLength; 1094 if (buffer != NULL && buffersize >= size) { 1095 hr = AVIFILE_ReadBlock(This, start, buffer, size); 1096 if (FAILED(hr)) 1097 return hr; 1098 } else if (buffer != NULL) 1099 return AVIERR_BUFFERTOOSMALL; 1100 1101 /* fill out return parameters if given */ 1102 if (bytesread != NULL) 1103 *bytesread = size; 1104 if (samplesread != NULL) 1105 *samplesread = samples; 1106 1107 return AVIERR_OK; 1108 } 1109 } 1110 1111 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer, 1112 LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten) 1113 { 1114 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 1115 FOURCC ckid; 1116 HRESULT hr; 1117 1118 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, 1119 buffer, buffersize, flags, sampwritten, byteswritten); 1120 1121 /* clear return parameters if given */ 1122 if (sampwritten != NULL) 1123 *sampwritten = 0; 1124 if (byteswritten != NULL) 1125 *byteswritten = 0; 1126 1127 /* check parameters */ 1128 if (buffer == NULL && (buffersize > 0 || samples > 0)) 1129 return AVIERR_BADPARAM; 1130 1131 /* Have we write permission? */ 1132 if ((This->paf->uMode & MMIO_RWMODE) == 0) 1133 return AVIERR_READONLY; 1134 1135 switch (This->sInfo.fccType) { 1136 case streamtypeAUDIO: 1137 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream); 1138 break; 1139 default: 1140 if ((flags & AVIIF_KEYFRAME) && buffersize != 0) 1141 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream); 1142 else 1143 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream); 1144 break; 1145 }; 1146 1147 /* append to end of stream? */ 1148 if (start == -1) { 1149 if (This->lLastFrame == -1) 1150 start = This->sInfo.dwStart; 1151 else 1152 start = This->sInfo.dwLength; 1153 } else if (This->lLastFrame == -1) 1154 This->sInfo.dwStart = start; 1155 1156 if (This->sInfo.dwSampleSize != 0) { 1157 /* fixed sample size -- audio like */ 1158 if (samples * This->sInfo.dwSampleSize != buffersize) 1159 return AVIERR_BADPARAM; 1160 1161 /* Couldn't skip audio-like data -- User must supply appropriate silence */ 1162 if (This->sInfo.dwLength != start) 1163 return AVIERR_UNSUPPORTED; 1164 1165 /* Convert position to frame/block */ 1166 start = This->lLastFrame + 1; 1167 1168 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) { 1169 FIXME(": not interleaved, could collect audio data!\n"); 1170 } 1171 } else { 1172 /* variable sample size -- video like */ 1173 if (samples > 1) 1174 return AVIERR_UNSUPPORTED; 1175 1176 /* must we fill up with empty frames? */ 1177 if (This->lLastFrame != -1) { 1178 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream); 1179 1180 while (start > This->lLastFrame + 1) { 1181 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0); 1182 if (FAILED(hr)) 1183 return hr; 1184 } 1185 } 1186 } 1187 1188 /* write the block now */ 1189 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize); 1190 if (SUCCEEDED(hr)) { 1191 /* fill out return parameters if given */ 1192 if (sampwritten != NULL) 1193 *sampwritten = samples; 1194 if (byteswritten != NULL) 1195 *byteswritten = buffersize; 1196 } 1197 1198 return hr; 1199 } 1200 1201 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples) 1202 { 1203 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 1204 1205 FIXME("(%p,%d,%d): stub\n", iface, start, samples); 1206 1207 /* check parameters */ 1208 if (start < 0 || samples < 0) 1209 return AVIERR_BADPARAM; 1210 1211 /* Delete before start of stream? */ 1212 if (start + samples < This->sInfo.dwStart) 1213 return AVIERR_OK; 1214 1215 /* Delete after end of stream? */ 1216 if (start > This->sInfo.dwLength) 1217 return AVIERR_OK; 1218 1219 /* For the rest we need write permissions */ 1220 if ((This->paf->uMode & MMIO_RWMODE) == 0) 1221 return AVIERR_READONLY; 1222 1223 /* 1. overwrite the data with JUNK 1224 * 1225 * if ISINTERLEAVED { 1226 * 2. concat all neighboured JUNK-blocks in this record to one 1227 * 3. if this record only contains JUNK and is at end set dwNextFramePos 1228 * to start of this record, repeat this. 1229 * } else { 1230 * 2. concat all neighboured JUNK-blocks. 1231 * 3. if the JUNK block is at the end, then set dwNextFramePos to 1232 * start of this block. 1233 * } 1234 */ 1235 1236 return AVIERR_UNSUPPORTED; 1237 } 1238 1239 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread) 1240 { 1241 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 1242 1243 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread); 1244 1245 if (fcc == ckidSTREAMHANDLERDATA) { 1246 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) { 1247 if (lp == NULL || *lpread <= 0) { 1248 *lpread = This->cbHandlerData; 1249 return AVIERR_OK; 1250 } 1251 1252 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread)); 1253 if (*lpread < This->cbHandlerData) 1254 return AVIERR_BUFFERTOOSMALL; 1255 return AVIERR_OK; 1256 } else 1257 return AVIERR_NODATA; 1258 } else 1259 return ReadExtraChunk(&This->extra, fcc, lp, lpread); 1260 } 1261 1262 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size) 1263 { 1264 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 1265 1266 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size); 1267 1268 /* check parameters */ 1269 if (lp == NULL) 1270 return AVIERR_BADPARAM; 1271 if (size <= 0) 1272 return AVIERR_BADSIZE; 1273 1274 /* need write permission */ 1275 if ((This->paf->uMode & MMIO_RWMODE) == 0) 1276 return AVIERR_READONLY; 1277 1278 /* already written something to this file? */ 1279 if (This->paf->dwMoviChunkPos != 0) { 1280 /* the data will be inserted before the 'movi' chunk, so check for 1281 * enough space */ 1282 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf); 1283 1284 /* ckid,size => 2 * sizeof(DWORD) */ 1285 dwPos += 2 * sizeof(DWORD) + size; 1286 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD)) 1287 return AVIERR_UNSUPPORTED; /* not enough space left */ 1288 } 1289 1290 This->paf->fDirty = TRUE; 1291 1292 if (fcc == ckidSTREAMHANDLERDATA) { 1293 if (This->lpHandlerData != NULL) { 1294 FIXME(": handler data already set -- overwrite?\n"); 1295 return AVIERR_UNSUPPORTED; 1296 } 1297 1298 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size); 1299 if (This->lpHandlerData == NULL) 1300 return AVIERR_MEMORY; 1301 This->cbHandlerData = size; 1302 memcpy(This->lpHandlerData, lp, size); 1303 1304 return AVIERR_OK; 1305 } else 1306 return WriteExtraChunk(&This->extra, fcc, lp, size); 1307 } 1308 1309 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen) 1310 { 1311 FIXME("(%p,%p,%d): stub\n", iface, info, infolen); 1312 1313 return E_FAIL; 1314 } 1315 1316 static const struct IAVIStreamVtbl avist_vt = { 1317 IAVIStream_fnQueryInterface, 1318 IAVIStream_fnAddRef, 1319 IAVIStream_fnRelease, 1320 IAVIStream_fnCreate, 1321 IAVIStream_fnInfo, 1322 IAVIStream_fnFindSample, 1323 IAVIStream_fnReadFormat, 1324 IAVIStream_fnSetFormat, 1325 IAVIStream_fnRead, 1326 IAVIStream_fnWrite, 1327 IAVIStream_fnDelete, 1328 IAVIStream_fnReadData, 1329 IAVIStream_fnWriteData, 1330 IAVIStream_fnSetInfo 1331 }; 1332 1333 1334 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags) 1335 { 1336 UINT n; 1337 1338 /* pre-conditions */ 1339 assert(This != NULL); 1340 1341 switch (TWOCCFromFOURCC(ckid)) { 1342 case cktypeDIBbits: 1343 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) 1344 flags |= AVIIF_KEYFRAME; 1345 break; 1346 case cktypeDIBcompressed: 1347 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) 1348 flags &= ~AVIIF_KEYFRAME; 1349 break; 1350 case cktypePALchange: 1351 if (This->sInfo.fccType != streamtypeVIDEO) { 1352 ERR(": found palette change in non-video stream!\n"); 1353 return AVIERR_BADFORMAT; 1354 } 1355 1356 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) { 1357 DWORD new_count = This->nIdxFmtChanges + 16; 1358 void *new_buffer; 1359 1360 if (This->idxFmtChanges == NULL) { 1361 This->idxFmtChanges = 1362 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY)); 1363 if (!This->idxFmtChanges) return AVIERR_MEMORY; 1364 } else { 1365 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges, 1366 new_count * sizeof(AVIINDEXENTRY)); 1367 if (!new_buffer) return AVIERR_MEMORY; 1368 This->idxFmtChanges = new_buffer; 1369 } 1370 This->nIdxFmtChanges = new_count; 1371 } 1372 1373 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES; 1374 n = ++This->sInfo.dwFormatChangeCount; 1375 This->idxFmtChanges[n].ckid = This->lLastFrame; 1376 This->idxFmtChanges[n].dwFlags = 0; 1377 This->idxFmtChanges[n].dwChunkOffset = offset; 1378 This->idxFmtChanges[n].dwChunkLength = size; 1379 1380 return AVIERR_OK; 1381 case cktypeWAVEbytes: 1382 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE) 1383 flags |= AVIIF_KEYFRAME; 1384 break; 1385 default: 1386 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid)); 1387 break; 1388 }; 1389 1390 /* first frame is always a keyframe */ 1391 if (This->lLastFrame == -1) 1392 flags |= AVIIF_KEYFRAME; 1393 1394 if (This->sInfo.dwSuggestedBufferSize < size) 1395 This->sInfo.dwSuggestedBufferSize = size; 1396 1397 /* get memory for index */ 1398 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) { 1399 This->nIdxFrames += 512; 1400 if (This->idxFrames == NULL) 1401 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY)); 1402 else 1403 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames, 1404 This->nIdxFrames * sizeof(AVIINDEXENTRY)); 1405 if (This->idxFrames == NULL) 1406 return AVIERR_MEMORY; 1407 } 1408 1409 This->lLastFrame++; 1410 This->idxFrames[This->lLastFrame].ckid = ckid; 1411 This->idxFrames[This->lLastFrame].dwFlags = flags; 1412 This->idxFrames[This->lLastFrame].dwChunkOffset = offset; 1413 This->idxFrames[This->lLastFrame].dwChunkLength = size; 1414 1415 /* update AVISTREAMINFO structure if necessary */ 1416 if (This->sInfo.dwLength <= This->lLastFrame) 1417 This->sInfo.dwLength = This->lLastFrame + 1; 1418 1419 return AVIERR_OK; 1420 } 1421 1422 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This) 1423 { 1424 /* pre-conditions */ 1425 assert(This != NULL && This->ppStreams[0] != NULL); 1426 1427 if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) { 1428 DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY); 1429 void *mem; 1430 if (!This->idxRecords) 1431 mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count); 1432 else 1433 mem = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxRecords, new_count); 1434 if (mem) { 1435 This->cbIdxRecords = new_count; 1436 This->idxRecords = mem; 1437 } else { 1438 HeapFree(GetProcessHeap(), 0, This->idxRecords); 1439 This->idxRecords = NULL; 1440 return AVIERR_MEMORY; 1441 } 1442 } 1443 1444 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY)); 1445 1446 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD; 1447 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST; 1448 This->idxRecords[This->nIdxRecords].dwChunkOffset = 1449 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD); 1450 This->idxRecords[This->nIdxRecords].dwChunkLength = 1451 This->ckLastRecord.cksize; 1452 This->nIdxRecords++; 1453 1454 return AVIERR_OK; 1455 } 1456 1457 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This) 1458 { 1459 DWORD dwPos; 1460 DWORD nStream; 1461 1462 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */ 1463 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader); 1464 1465 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 1466 IAVIStreamImpl *pStream = This->ppStreams[nStream]; 1467 1468 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */ 1469 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader); 1470 dwPos += ((pStream->cbFormat + 1) & ~1U); 1471 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) 1472 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U); 1473 if (pStream->sInfo.szName[0]) 1474 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U); 1475 } 1476 1477 if (This->dwMoviChunkPos == 0) { 1478 This->dwNextFramePos = dwPos; 1479 1480 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */ 1481 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD)) 1482 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1); 1483 1484 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD); 1485 } 1486 1487 return dwPos; 1488 } 1489 1490 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi) 1491 { 1492 IAVIStreamImpl *pstream; 1493 1494 /* pre-conditions */ 1495 assert(paf != NULL); 1496 assert(nr < MAX_AVISTREAMS); 1497 assert(paf->ppStreams[nr] != NULL); 1498 1499 pstream = paf->ppStreams[nr]; 1500 1501 pstream->IAVIStream_iface.lpVtbl = &avist_vt; 1502 pstream->ref = 0; 1503 pstream->paf = paf; 1504 pstream->nStream = nr; 1505 pstream->dwCurrentFrame = (DWORD)-1; 1506 pstream->lLastFrame = -1; 1507 1508 if (asi != NULL) { 1509 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo)); 1510 1511 if (asi->dwLength > 0) { 1512 /* pre-allocate mem for frame-index structure */ 1513 pstream->idxFrames = 1514 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY)); 1515 if (pstream->idxFrames != NULL) 1516 pstream->nIdxFrames = asi->dwLength; 1517 } 1518 if (asi->dwFormatChangeCount > 0) { 1519 /* pre-allocate mem for formatchange-index structure */ 1520 pstream->idxFmtChanges = 1521 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY)); 1522 if (pstream->idxFmtChanges != NULL) 1523 pstream->nIdxFmtChanges = asi->dwFormatChangeCount; 1524 } 1525 1526 /* These values will be computed */ 1527 pstream->sInfo.dwLength = 0; 1528 pstream->sInfo.dwSuggestedBufferSize = 0; 1529 pstream->sInfo.dwFormatChangeCount = 0; 1530 pstream->sInfo.dwEditCount = 1; 1531 if (pstream->sInfo.dwSampleSize > 0) 1532 SetRectEmpty(&pstream->sInfo.rcFrame); 1533 } 1534 1535 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 1536 } 1537 1538 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This) 1539 { 1540 /* pre-conditions */ 1541 assert(This != NULL); 1542 1543 This->dwCurrentFrame = (DWORD)-1; 1544 This->lLastFrame = -1; 1545 This->paf = NULL; 1546 if (This->idxFrames != NULL) { 1547 HeapFree(GetProcessHeap(), 0, This->idxFrames); 1548 This->idxFrames = NULL; 1549 This->nIdxFrames = 0; 1550 } 1551 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges); 1552 This->idxFmtChanges = NULL; 1553 if (This->lpBuffer != NULL) { 1554 HeapFree(GetProcessHeap(), 0, This->lpBuffer); 1555 This->lpBuffer = NULL; 1556 This->cbBuffer = 0; 1557 } 1558 if (This->lpHandlerData != NULL) { 1559 HeapFree(GetProcessHeap(), 0, This->lpHandlerData); 1560 This->lpHandlerData = NULL; 1561 This->cbHandlerData = 0; 1562 } 1563 if (This->extra.lp != NULL) { 1564 HeapFree(GetProcessHeap(), 0, This->extra.lp); 1565 This->extra.lp = NULL; 1566 This->extra.cb = 0; 1567 } 1568 if (This->lpFormat != NULL) { 1569 HeapFree(GetProcessHeap(), 0, This->lpFormat); 1570 This->lpFormat = NULL; 1571 This->cbFormat = 0; 1572 } 1573 } 1574 1575 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) 1576 { 1577 MainAVIHeader MainAVIHdr; 1578 MMCKINFO ckRIFF; 1579 MMCKINFO ckLIST1; 1580 MMCKINFO ckLIST2; 1581 MMCKINFO ck; 1582 IAVIStreamImpl *pStream; 1583 DWORD nStream; 1584 HRESULT hr; 1585 1586 if (This->hmmio == NULL) 1587 return AVIERR_FILEOPEN; 1588 1589 /* initialize stream ptr's */ 1590 memset(This->ppStreams, 0, sizeof(This->ppStreams)); 1591 1592 /* try to get "RIFF" chunk -- must not be at beginning of file! */ 1593 ckRIFF.fccType = formtypeAVI; 1594 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { 1595 ERR(": not an AVI!\n"); 1596 return AVIERR_FILEREAD; 1597 } 1598 1599 /* get "LIST" "hdrl" */ 1600 ckLIST1.fccType = listtypeAVIHEADER; 1601 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST); 1602 if (FAILED(hr)) 1603 return hr; 1604 1605 /* get "avih" chunk */ 1606 ck.ckid = ckidAVIMAINHDR; 1607 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK); 1608 if (FAILED(hr)) 1609 return hr; 1610 1611 if (ck.cksize != sizeof(MainAVIHdr)) { 1612 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize); 1613 return AVIERR_BADFORMAT; 1614 } 1615 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) 1616 return AVIERR_FILEREAD; 1617 1618 /* check for MAX_AVISTREAMS limit */ 1619 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) { 1620 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS); 1621 return AVIERR_UNSUPPORTED; 1622 } 1623 1624 /* adjust permissions if copyrighted material in file */ 1625 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) { 1626 This->uMode &= ~MMIO_RWMODE; 1627 This->uMode |= MMIO_READ; 1628 } 1629 1630 /* convert MainAVIHeader into AVIFILINFOW */ 1631 memset(&This->fInfo, 0, sizeof(This->fInfo)); 1632 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame; 1633 This->fInfo.dwScale = 1000000; 1634 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec; 1635 This->fInfo.dwFlags = MainAVIHdr.dwFlags; 1636 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 1637 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames; 1638 This->fInfo.dwStreams = MainAVIHdr.dwStreams; 1639 This->fInfo.dwSuggestedBufferSize = 0; 1640 This->fInfo.dwWidth = MainAVIHdr.dwWidth; 1641 This->fInfo.dwHeight = MainAVIHdr.dwHeight; 1642 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType, 1643 ARRAY_SIZE(This->fInfo.szFileType)); 1644 1645 /* go back to into header list */ 1646 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1647 return AVIERR_FILEREAD; 1648 1649 /* foreach stream exists a "LIST","strl" chunk */ 1650 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 1651 /* get next nested chunk in this "LIST","strl" */ 1652 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK) 1653 return AVIERR_FILEREAD; 1654 1655 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */ 1656 if (ckLIST2.ckid == FOURCC_LIST && 1657 ckLIST2.fccType == listtypeSTREAMHEADER) { 1658 pStream = This->ppStreams[nStream] = 1659 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl)); 1660 if (pStream == NULL) 1661 return AVIERR_MEMORY; 1662 AVIFILE_ConstructAVIStream(This, nStream, NULL); 1663 1664 ck.ckid = 0; 1665 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) { 1666 switch (ck.ckid) { 1667 case ckidSTREAMHANDLERDATA: 1668 if (pStream->lpHandlerData != NULL) 1669 return AVIERR_BADFORMAT; 1670 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 1671 if (pStream->lpHandlerData == NULL) 1672 return AVIERR_MEMORY; 1673 pStream->cbHandlerData = ck.cksize; 1674 1675 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) 1676 return AVIERR_FILEREAD; 1677 break; 1678 case ckidSTREAMFORMAT: 1679 if (pStream->lpFormat != NULL) 1680 return AVIERR_BADFORMAT; 1681 if (ck.cksize == 0) 1682 break; 1683 1684 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 1685 if (pStream->lpFormat == NULL) 1686 return AVIERR_MEMORY; 1687 pStream->cbFormat = ck.cksize; 1688 1689 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) 1690 return AVIERR_FILEREAD; 1691 1692 if (pStream->sInfo.fccType == streamtypeVIDEO) { 1693 LPBITMAPINFOHEADER lpbi = pStream->lpFormat; 1694 1695 /* some corrections to the video format */ 1696 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) 1697 lpbi->biClrUsed = 1u << lpbi->biBitCount; 1698 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0) 1699 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight; 1700 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) { 1701 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') || 1702 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' ')) 1703 lpbi->biCompression = BI_RLE8; 1704 } 1705 if (lpbi->biCompression == BI_RGB && 1706 (pStream->sInfo.fccHandler == 0 || 1707 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))) 1708 pStream->sInfo.fccHandler = comptypeDIB; 1709 1710 /* init rcFrame if it's empty */ 1711 if (IsRectEmpty(&pStream->sInfo.rcFrame)) 1712 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight); 1713 } 1714 break; 1715 case ckidSTREAMHEADER: 1716 { 1717 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0}; 1718 static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0}; 1719 1720 AVIStreamHeader streamHdr; 1721 WCHAR szType[25]; 1722 UINT count; 1723 LONG n = ck.cksize; 1724 1725 if (ck.cksize > sizeof(streamHdr)) 1726 n = sizeof(streamHdr); 1727 1728 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n) 1729 return AVIERR_FILEREAD; 1730 1731 pStream->sInfo.fccType = streamHdr.fccType; 1732 pStream->sInfo.fccHandler = streamHdr.fccHandler; 1733 pStream->sInfo.dwFlags = streamHdr.dwFlags; 1734 pStream->sInfo.wPriority = streamHdr.wPriority; 1735 pStream->sInfo.wLanguage = streamHdr.wLanguage; 1736 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames; 1737 pStream->sInfo.dwScale = streamHdr.dwScale; 1738 pStream->sInfo.dwRate = streamHdr.dwRate; 1739 pStream->sInfo.dwStart = streamHdr.dwStart; 1740 pStream->sInfo.dwLength = streamHdr.dwLength; 1741 pStream->sInfo.dwSuggestedBufferSize = 0; 1742 pStream->sInfo.dwQuality = streamHdr.dwQuality; 1743 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize; 1744 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left; 1745 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top; 1746 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right; 1747 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom; 1748 pStream->sInfo.dwEditCount = 0; 1749 pStream->sInfo.dwFormatChangeCount = 0; 1750 1751 /* generate description for stream like "filename.avi Type #n" */ 1752 if (streamHdr.fccType == streamtypeVIDEO) 1753 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, ARRAY_SIZE(szType)); 1754 else if (streamHdr.fccType == streamtypeAUDIO) 1755 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, ARRAY_SIZE(szType)); 1756 else 1757 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType); 1758 1759 /* get count of this streamtype up to this stream */ 1760 count = 0; 1761 for (n = nStream; 0 <= n; n--) { 1762 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType) 1763 count++; 1764 } 1765 1766 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName)); 1767 1768 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */ 1769 wsprintfW(pStream->sInfo.szName, streamNameFmt, 1770 AVIFILE_BasenameW(This->szFileName), szType, count); 1771 } 1772 break; 1773 case ckidSTREAMNAME: 1774 { /* streamname will be saved as ASCII string */ 1775 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 1776 if (str == NULL) 1777 return AVIERR_MEMORY; 1778 1779 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize) 1780 { 1781 HeapFree(GetProcessHeap(), 0, str); 1782 return AVIERR_FILEREAD; 1783 } 1784 1785 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName, 1786 ARRAY_SIZE(pStream->sInfo.szName)); 1787 1788 HeapFree(GetProcessHeap(), 0, str); 1789 } 1790 break; 1791 case ckidAVIPADDING: 1792 case mmioFOURCC('p','a','d','d'): 1793 break; 1794 default: 1795 WARN(": found extra chunk 0x%08X\n", ck.ckid); 1796 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck); 1797 if (FAILED(hr)) 1798 return hr; 1799 }; 1800 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO) 1801 { 1802 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */ 1803 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */ 1804 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample); 1805 pStream->sInfo.dwScale = 1; 1806 pStream->sInfo.dwRate = wfx->nSamplesPerSec; 1807 } 1808 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1809 return AVIERR_FILEREAD; 1810 } 1811 } else { 1812 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */ 1813 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2); 1814 if (FAILED(hr)) 1815 return hr; 1816 } 1817 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) 1818 return AVIERR_FILEREAD; 1819 } 1820 1821 /* read any extra headers in "LIST","hdrl" */ 1822 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0); 1823 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 1824 return AVIERR_FILEREAD; 1825 1826 /* search "LIST","movi" chunk in "RIFF","AVI " */ 1827 ckLIST1.fccType = listtypeAVIMOVIE; 1828 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, 1829 MMIO_FINDLIST); 1830 if (FAILED(hr)) 1831 return hr; 1832 1833 This->dwMoviChunkPos = ckLIST1.dwDataOffset; 1834 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset; 1835 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 1836 return AVIERR_FILEREAD; 1837 1838 /* try to find an index */ 1839 ck.ckid = ckidAVINEWINDEX; 1840 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, 1841 &ck, &ckRIFF, MMIO_FINDCHUNK); 1842 if (SUCCEEDED(hr) && ck.cksize > 0) { 1843 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset))) 1844 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX; 1845 } 1846 1847 /* when we haven't found an index or it's bad, then build one 1848 * by parsing 'movi' chunk */ 1849 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) { 1850 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) 1851 This->ppStreams[nStream]->lLastFrame = -1; 1852 1853 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) { 1854 ERR(": Oops, can't seek back to 'movi' chunk!\n"); 1855 return AVIERR_FILEREAD; 1856 } 1857 1858 /* seek through the 'movi' list until end */ 1859 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) { 1860 if (ck.ckid != FOURCC_LIST) { 1861 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) { 1862 nStream = StreamFromFOURCC(ck.ckid); 1863 1864 if (nStream > This->fInfo.dwStreams) 1865 return AVIERR_BADFORMAT; 1866 1867 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, 1868 ck.dwDataOffset - 2 * sizeof(DWORD), 0); 1869 } else { 1870 nStream = StreamFromFOURCC(ck.ckid); 1871 WARN(": file seems to be truncated!\n"); 1872 if (nStream <= This->fInfo.dwStreams && 1873 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) { 1874 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END); 1875 if (ck.cksize != -1) { 1876 ck.cksize -= ck.dwDataOffset; 1877 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1); 1878 1879 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize, 1880 ck.dwDataOffset - 2 * sizeof(DWORD), 0); 1881 } 1882 } 1883 } 1884 } 1885 } 1886 } 1887 1888 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) 1889 { 1890 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize; 1891 if (This->fInfo.dwSuggestedBufferSize < sugbuf) 1892 This->fInfo.dwSuggestedBufferSize = sugbuf; 1893 } 1894 1895 /* find other chunks */ 1896 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0); 1897 1898 return AVIERR_OK; 1899 } 1900 1901 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset) 1902 { 1903 AVIINDEXENTRY *lp; 1904 DWORD pos, n; 1905 HRESULT hr = AVIERR_OK; 1906 BOOL bAbsolute = TRUE; 1907 1908 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY)); 1909 if (lp == NULL) 1910 return AVIERR_MEMORY; 1911 1912 /* adjust limits for index tables, so that inserting will be faster */ 1913 for (n = 0; n < This->fInfo.dwStreams; n++) { 1914 IAVIStreamImpl *pStream = This->ppStreams[n]; 1915 1916 pStream->lLastFrame = -1; 1917 1918 if (pStream->idxFrames != NULL) { 1919 HeapFree(GetProcessHeap(), 0, pStream->idxFrames); 1920 pStream->idxFrames = NULL; 1921 pStream->nIdxFrames = 0; 1922 } 1923 1924 if (pStream->sInfo.dwSampleSize != 0) { 1925 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { 1926 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames; 1927 } else if (pStream->sInfo.dwSuggestedBufferSize) { 1928 pStream->nIdxFrames = 1929 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize; 1930 } 1931 } else 1932 pStream->nIdxFrames = pStream->sInfo.dwLength; 1933 1934 pStream->idxFrames = 1935 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY)); 1936 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) { 1937 pStream->nIdxFrames = 0; 1938 HeapFree(GetProcessHeap(), 0, lp); 1939 return AVIERR_MEMORY; 1940 } 1941 } 1942 1943 pos = (DWORD)-1; 1944 while (size != 0) { 1945 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size); 1946 1947 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) { 1948 hr = AVIERR_FILEREAD; 1949 break; 1950 } 1951 size -= read; 1952 1953 if (pos == (DWORD)-1) 1954 pos = offset - lp->dwChunkOffset + sizeof(DWORD); 1955 1956 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY), 1957 pos, &bAbsolute); 1958 } 1959 1960 HeapFree(GetProcessHeap(), 0, lp); 1961 1962 /* checking ... */ 1963 for (n = 0; n < This->fInfo.dwStreams; n++) { 1964 IAVIStreamImpl *pStream = This->ppStreams[n]; 1965 1966 if (pStream->sInfo.dwSampleSize == 0 && 1967 pStream->sInfo.dwLength != pStream->lLastFrame+1) 1968 ERR("stream %u length mismatch: dwLength=%u found=%d\n", 1969 n, pStream->sInfo.dwLength, pStream->lLastFrame); 1970 } 1971 1972 return hr; 1973 } 1974 1975 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp, 1976 LONG count, DWORD pos, BOOL *bAbsolute) 1977 { 1978 if (lp == NULL) 1979 return AVIERR_BADPARAM; 1980 1981 for (; count > 0; count--, lp++) { 1982 WORD nStream = StreamFromFOURCC(lp->ckid); 1983 1984 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F) 1985 continue; /* skip these */ 1986 1987 if (nStream > This->fInfo.dwStreams) 1988 return AVIERR_BADFORMAT; 1989 1990 /* Video frames can be either indexed in a relative position to the 1991 * "movi" chunk or in a absolute position in the file. If the index 1992 * is relative the frame offset will always be so small that it will 1993 * virtually never reach the "movi" offset so we can detect if the 1994 * video is relative very fast. 1995 */ 1996 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos) 1997 *bAbsolute = FALSE; 1998 1999 if (!*bAbsolute) 2000 lp->dwChunkOffset += pos; /* make the offset absolute */ 2001 2002 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags))) 2003 return AVIERR_MEMORY; 2004 } 2005 2006 return AVIERR_OK; 2007 } 2008 2009 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos, 2010 LPVOID buffer, DWORD size) 2011 { 2012 /* pre-conditions */ 2013 assert(This != NULL); 2014 assert(This->paf != NULL); 2015 assert(This->paf->hmmio != NULL); 2016 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); 2017 assert(pos <= This->lLastFrame); 2018 2019 /* should we read as much as block gives us? */ 2020 if (size == 0 || size > This->idxFrames[pos].dwChunkLength) 2021 size = This->idxFrames[pos].dwChunkLength; 2022 2023 /* read into out own buffer or given one? */ 2024 if (buffer == NULL) { 2025 /* we also read the chunk */ 2026 size += 2 * sizeof(DWORD); 2027 2028 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */ 2029 if (This->lpBuffer == NULL || This->cbBuffer < size) { 2030 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize); 2031 2032 if (This->lpBuffer == NULL) { 2033 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize); 2034 if (!This->lpBuffer) return AVIERR_MEMORY; 2035 } else { 2036 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize); 2037 if (!new_buffer) return AVIERR_MEMORY; 2038 This->lpBuffer = new_buffer; 2039 } 2040 This->cbBuffer = maxSize; 2041 } 2042 2043 /* now read the complete chunk into our buffer */ 2044 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1) 2045 return AVIERR_FILEREAD; 2046 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size) 2047 return AVIERR_FILEREAD; 2048 2049 /* check if it was the correct block which we have read */ 2050 if (This->lpBuffer[0] != This->idxFrames[pos].ckid || 2051 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) { 2052 ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset); 2053 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n", 2054 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid, 2055 This->idxFrames[pos].dwChunkLength); 2056 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n", 2057 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]); 2058 return AVIERR_FILEREAD; 2059 } 2060 } else { 2061 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1) 2062 return AVIERR_FILEREAD; 2063 if (mmioRead(This->paf->hmmio, buffer, size) != size) 2064 return AVIERR_FILEREAD; 2065 } 2066 2067 return AVIERR_OK; 2068 } 2069 2070 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset) 2071 { 2072 LONG block; 2073 2074 /* pre-conditions */ 2075 assert(This != NULL); 2076 assert(pos != NULL); 2077 assert(offset != NULL); 2078 assert(This->sInfo.dwSampleSize != 0); 2079 assert(*pos >= This->sInfo.dwStart); 2080 2081 /* convert start sample to start bytes */ 2082 (*offset) = (*pos) - This->sInfo.dwStart; 2083 (*offset) *= This->sInfo.dwSampleSize; 2084 2085 /* convert bytes to block number */ 2086 for (block = 0; block <= This->lLastFrame; block++) { 2087 if (This->idxFrames[block].dwChunkLength <= *offset) 2088 (*offset) -= This->idxFrames[block].dwChunkLength; 2089 else 2090 break; 2091 } 2092 2093 *pos = block; 2094 } 2095 2096 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) 2097 { 2098 MainAVIHeader MainAVIHdr; 2099 IAVIStreamImpl* pStream; 2100 MMCKINFO ckRIFF; 2101 MMCKINFO ckLIST1; 2102 MMCKINFO ckLIST2; 2103 MMCKINFO ck; 2104 DWORD nStream; 2105 DWORD dwPos; 2106 HRESULT hr; 2107 2108 /* initialize some things */ 2109 if (This->dwMoviChunkPos == 0) 2110 AVIFILE_ComputeMoviStart(This); 2111 2112 /* written one record too much? */ 2113 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { 2114 This->dwNextFramePos -= 3 * sizeof(DWORD); 2115 if (This->nIdxRecords > 0) 2116 This->nIdxRecords--; 2117 } 2118 2119 AVIFILE_UpdateInfo(This); 2120 2121 assert(This->fInfo.dwScale != 0); 2122 2123 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr)); 2124 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000, 2125 This->fInfo.dwScale); 2126 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec; 2127 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE; 2128 MainAVIHdr.dwFlags = This->fInfo.dwFlags; 2129 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength; 2130 MainAVIHdr.dwInitialFrames = 0; 2131 MainAVIHdr.dwStreams = This->fInfo.dwStreams; 2132 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize; 2133 MainAVIHdr.dwWidth = This->fInfo.dwWidth; 2134 MainAVIHdr.dwHeight = This->fInfo.dwHeight; 2135 MainAVIHdr.dwInitialFrames = This->dwInitialFrames; 2136 2137 /* now begin writing ... */ 2138 mmioSeek(This->hmmio, 0, SEEK_SET); 2139 2140 /* RIFF chunk */ 2141 ckRIFF.cksize = 0; 2142 ckRIFF.fccType = formtypeAVI; 2143 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) 2144 return AVIERR_FILEWRITE; 2145 2146 /* AVI headerlist */ 2147 ckLIST1.cksize = 0; 2148 ckLIST1.fccType = listtypeAVIHEADER; 2149 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) 2150 return AVIERR_FILEWRITE; 2151 2152 /* MainAVIHeader */ 2153 ck.ckid = ckidAVIMAINHDR; 2154 ck.cksize = sizeof(MainAVIHdr); 2155 ck.fccType = 0; 2156 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2157 return AVIERR_FILEWRITE; 2158 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize) 2159 return AVIERR_FILEWRITE; 2160 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2161 return AVIERR_FILEWRITE; 2162 2163 /* write the headers of each stream into a separate streamheader list */ 2164 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 2165 AVIStreamHeader strHdr; 2166 2167 pStream = This->ppStreams[nStream]; 2168 2169 /* begin the new streamheader list */ 2170 ckLIST2.cksize = 0; 2171 ckLIST2.fccType = listtypeSTREAMHEADER; 2172 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK) 2173 return AVIERR_FILEWRITE; 2174 2175 /* create an AVIStreamHeader from the AVSTREAMINFO */ 2176 strHdr.fccType = pStream->sInfo.fccType; 2177 strHdr.fccHandler = pStream->sInfo.fccHandler; 2178 strHdr.dwFlags = pStream->sInfo.dwFlags; 2179 strHdr.wPriority = pStream->sInfo.wPriority; 2180 strHdr.wLanguage = pStream->sInfo.wLanguage; 2181 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames; 2182 strHdr.dwScale = pStream->sInfo.dwScale; 2183 strHdr.dwRate = pStream->sInfo.dwRate; 2184 strHdr.dwStart = pStream->sInfo.dwStart; 2185 strHdr.dwLength = pStream->sInfo.dwLength; 2186 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize; 2187 strHdr.dwQuality = pStream->sInfo.dwQuality; 2188 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize; 2189 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left; 2190 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top; 2191 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right; 2192 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom; 2193 2194 /* now write the AVIStreamHeader */ 2195 ck.ckid = ckidSTREAMHEADER; 2196 ck.cksize = sizeof(strHdr); 2197 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2198 return AVIERR_FILEWRITE; 2199 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize) 2200 return AVIERR_FILEWRITE; 2201 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2202 return AVIERR_FILEWRITE; 2203 2204 /* ... the hopefully ever present streamformat ... */ 2205 ck.ckid = ckidSTREAMFORMAT; 2206 ck.cksize = pStream->cbFormat; 2207 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2208 return AVIERR_FILEWRITE; 2209 if (pStream->lpFormat != NULL && ck.cksize > 0) { 2210 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize) 2211 return AVIERR_FILEWRITE; 2212 } 2213 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2214 return AVIERR_FILEWRITE; 2215 2216 /* ... some optional existing handler data ... */ 2217 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) { 2218 ck.ckid = ckidSTREAMHANDLERDATA; 2219 ck.cksize = pStream->cbHandlerData; 2220 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2221 return AVIERR_FILEWRITE; 2222 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize) 2223 return AVIERR_FILEWRITE; 2224 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2225 return AVIERR_FILEWRITE; 2226 } 2227 2228 /* ... some optional additional extra chunk for this stream ... */ 2229 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) { 2230 /* the chunk header(s) are already in the structure */ 2231 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb) 2232 return AVIERR_FILEWRITE; 2233 } 2234 2235 /* ... an optional name for this stream ... */ 2236 if (pStream->sInfo.szName[0]) { 2237 LPSTR str; 2238 2239 ck.ckid = ckidSTREAMNAME; 2240 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1; 2241 if (ck.cksize & 1) /* align */ 2242 ck.cksize++; 2243 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2244 return AVIERR_FILEWRITE; 2245 2246 /* the streamname must be saved in ASCII not Unicode */ 2247 str = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 2248 if (str == NULL) 2249 return AVIERR_MEMORY; 2250 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str, 2251 ck.cksize, NULL, NULL); 2252 2253 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) { 2254 HeapFree(GetProcessHeap(), 0, str); 2255 return AVIERR_FILEWRITE; 2256 } 2257 2258 HeapFree(GetProcessHeap(), 0, str); 2259 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2260 return AVIERR_FILEWRITE; 2261 } 2262 2263 /* close streamheader list for this stream */ 2264 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK) 2265 return AVIERR_FILEWRITE; 2266 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */ 2267 2268 /* close the aviheader list */ 2269 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 2270 return AVIERR_FILEWRITE; 2271 2272 /* check for padding to pre-guessed 'movi'-chunk position */ 2273 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize; 2274 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) { 2275 ck.ckid = ckidAVIPADDING; 2276 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD); 2277 assert((LONG)ck.cksize >= 0); 2278 2279 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2280 return AVIERR_FILEWRITE; 2281 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1) 2282 return AVIERR_FILEWRITE; 2283 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2284 return AVIERR_FILEWRITE; 2285 } 2286 2287 /* now write the 'movi' chunk */ 2288 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET); 2289 ckLIST1.cksize = 0; 2290 ckLIST1.fccType = listtypeAVIMOVIE; 2291 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK) 2292 return AVIERR_FILEWRITE; 2293 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) 2294 return AVIERR_FILEWRITE; 2295 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) 2296 return AVIERR_FILEWRITE; 2297 2298 /* write 'idx1' chunk */ 2299 hr = AVIFILE_SaveIndex(This); 2300 if (FAILED(hr)) 2301 return hr; 2302 2303 /* write optional extra file chunks */ 2304 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) { 2305 /* as for the streams, are the chunk header(s) in the structure */ 2306 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb) 2307 return AVIERR_FILEWRITE; 2308 } 2309 2310 /* close RIFF chunk */ 2311 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) 2312 return AVIERR_FILEWRITE; 2313 2314 /* add some JUNK at end for bad parsers */ 2315 memset(&ckRIFF, 0, sizeof(ckRIFF)); 2316 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF)); 2317 mmioFlush(This->hmmio, 0); 2318 2319 return AVIERR_OK; 2320 } 2321 2322 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This) 2323 { 2324 IAVIStreamImpl *pStream; 2325 AVIINDEXENTRY idx; 2326 MMCKINFO ck; 2327 DWORD nStream; 2328 LONG n; 2329 2330 ck.ckid = ckidAVINEWINDEX; 2331 ck.cksize = 0; 2332 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 2333 return AVIERR_FILEWRITE; 2334 2335 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) { 2336 /* is interleaved -- write block of corresponding frames */ 2337 LONG lInitialFrames = 0; 2338 LONG stepsize; 2339 LONG i; 2340 2341 if (This->ppStreams[0]->sInfo.dwSampleSize == 0) 2342 stepsize = 1; 2343 else 2344 stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000); 2345 2346 assert(stepsize > 0); 2347 2348 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 2349 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames) 2350 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames; 2351 } 2352 2353 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames; 2354 i += stepsize) { 2355 DWORD nFrame = lInitialFrames + i; 2356 2357 assert(nFrame < This->nIdxRecords); 2358 2359 idx.ckid = listtypeAVIRECORD; 2360 idx.dwFlags = AVIIF_LIST; 2361 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength; 2362 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset 2363 - This->dwMoviChunkPos; 2364 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 2365 return AVIERR_FILEWRITE; 2366 2367 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 2368 pStream = This->ppStreams[nStream]; 2369 2370 /* heave we reached start of this stream? */ 2371 if (-(LONG)pStream->sInfo.dwInitialFrames > i) 2372 continue; 2373 2374 if (pStream->sInfo.dwInitialFrames < lInitialFrames) 2375 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames); 2376 2377 /* reached end of this stream? */ 2378 if (pStream->lLastFrame <= nFrame) 2379 continue; 2380 2381 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && 2382 pStream->sInfo.dwFormatChangeCount != 0 && 2383 pStream->idxFmtChanges != NULL) { 2384 DWORD pos; 2385 2386 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { 2387 if (pStream->idxFmtChanges[pos].ckid == nFrame) { 2388 idx.dwFlags = AVIIF_NOTIME; 2389 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); 2390 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; 2391 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset 2392 - This->dwMoviChunkPos; 2393 2394 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 2395 return AVIERR_FILEWRITE; 2396 break; 2397 } 2398 } 2399 } /* if have formatchanges */ 2400 2401 idx.ckid = pStream->idxFrames[nFrame].ckid; 2402 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags; 2403 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength; 2404 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset 2405 - This->dwMoviChunkPos; 2406 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 2407 return AVIERR_FILEWRITE; 2408 } 2409 } 2410 } else { 2411 /* not interleaved -- write index for each stream at once */ 2412 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { 2413 pStream = This->ppStreams[nStream]; 2414 2415 for (n = 0; n <= pStream->lLastFrame; n++) { 2416 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && 2417 (pStream->sInfo.dwFormatChangeCount != 0)) { 2418 DWORD pos; 2419 2420 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) { 2421 if (pStream->idxFmtChanges[pos].ckid == n) { 2422 idx.dwFlags = AVIIF_NOTIME; 2423 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream); 2424 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength; 2425 idx.dwChunkOffset = 2426 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos; 2427 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 2428 return AVIERR_FILEWRITE; 2429 break; 2430 } 2431 } 2432 } /* if have formatchanges */ 2433 2434 idx.ckid = pStream->idxFrames[n].ckid; 2435 idx.dwFlags = pStream->idxFrames[n].dwFlags; 2436 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength; 2437 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset 2438 - This->dwMoviChunkPos; 2439 2440 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) 2441 return AVIERR_FILEWRITE; 2442 } 2443 } 2444 } /* if not interleaved */ 2445 2446 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 2447 return AVIERR_FILEWRITE; 2448 2449 return AVIERR_OK; 2450 } 2451 2452 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip) 2453 { 2454 UINT i; 2455 UINT nStream; 2456 2457 /* pre-condition */ 2458 assert(lSkip >= 0); 2459 2460 if (fcc != 0) { 2461 /* search the number of the specified stream */ 2462 nStream = (ULONG)-1; 2463 for (i = 0; i < This->fInfo.dwStreams; i++) { 2464 assert(This->ppStreams[i] != NULL); 2465 2466 if (This->ppStreams[i]->sInfo.fccType == fcc) { 2467 if (lSkip == 0) { 2468 nStream = i; 2469 break; 2470 } else 2471 lSkip--; 2472 } 2473 } 2474 } else 2475 nStream = lSkip; 2476 2477 return nStream; 2478 } 2479 2480 static void AVIFILE_UpdateInfo(IAVIFileImpl *This) 2481 { 2482 UINT i; 2483 2484 /* pre-conditions */ 2485 assert(This != NULL); 2486 2487 This->fInfo.dwMaxBytesPerSec = 0; 2488 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 2489 This->fInfo.dwSuggestedBufferSize = 0; 2490 This->fInfo.dwWidth = 0; 2491 This->fInfo.dwHeight = 0; 2492 This->fInfo.dwScale = 0; 2493 This->fInfo.dwRate = 0; 2494 This->fInfo.dwLength = 0; 2495 This->dwInitialFrames = 0; 2496 2497 for (i = 0; i < This->fInfo.dwStreams; i++) { 2498 AVISTREAMINFOW *psi; 2499 DWORD n; 2500 2501 /* pre-conditions */ 2502 assert(This->ppStreams[i] != NULL); 2503 2504 psi = &This->ppStreams[i]->sInfo; 2505 assert(psi->dwScale != 0); 2506 assert(psi->dwRate != 0); 2507 2508 if (i == 0) { 2509 /* use first stream timings as base */ 2510 This->fInfo.dwScale = psi->dwScale; 2511 This->fInfo.dwRate = psi->dwRate; 2512 This->fInfo.dwLength = psi->dwLength; 2513 } else { 2514 n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface, 2515 &This->ppStreams[i]->IAVIStream_iface, psi->dwLength); 2516 if (This->fInfo.dwLength < n) 2517 This->fInfo.dwLength = n; 2518 } 2519 2520 if (This->dwInitialFrames < psi->dwInitialFrames) 2521 This->dwInitialFrames = psi->dwInitialFrames; 2522 2523 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize) 2524 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize; 2525 2526 if (psi->dwSampleSize != 0) { 2527 /* fixed sample size -- exact computation */ 2528 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate, 2529 psi->dwScale); 2530 } else { 2531 /* variable sample size -- only upper limit */ 2532 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize, 2533 psi->dwRate, psi->dwScale); 2534 2535 /* update dimensions */ 2536 n = psi->rcFrame.right - psi->rcFrame.left; 2537 if (This->fInfo.dwWidth < n) 2538 This->fInfo.dwWidth = n; 2539 n = psi->rcFrame.bottom - psi->rcFrame.top; 2540 if (This->fInfo.dwHeight < n) 2541 This->fInfo.dwHeight = n; 2542 } 2543 } 2544 } 2545 2546 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, 2547 FOURCC ckid, DWORD flags, LPCVOID buffer, 2548 LONG size) 2549 { 2550 MMCKINFO ck; 2551 2552 ck.ckid = ckid; 2553 ck.cksize = size; 2554 ck.fccType = 0; 2555 2556 /* if no frame/block is already written, we must compute start of movi chunk */ 2557 if (This->paf->dwMoviChunkPos == 0) 2558 AVIFILE_ComputeMoviStart(This->paf); 2559 2560 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1) 2561 return AVIERR_FILEWRITE; 2562 2563 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK) 2564 return AVIERR_FILEWRITE; 2565 if (buffer != NULL && size > 0) { 2566 if (mmioWrite(This->paf->hmmio, buffer, size) != size) 2567 return AVIERR_FILEWRITE; 2568 } 2569 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK) 2570 return AVIERR_FILEWRITE; 2571 2572 This->paf->fDirty = TRUE; 2573 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); 2574 2575 return AVIFILE_AddFrame(This, ckid, size, 2576 ck.dwDataOffset - 2 * sizeof(DWORD), flags); 2577 } 2578