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