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