1 /* 2 * Copyright 2002 Michael Günnewig 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #define COBJMACROS 20 #include <assert.h> 21 #include <stdarg.h> 22 23 #include "windef.h" 24 #include "winbase.h" 25 #include "wingdi.h" 26 #include "winuser.h" 27 #include "winnls.h" 28 #include "winerror.h" 29 #include "mmsystem.h" 30 #include "vfw.h" 31 #include "msacm.h" 32 33 #include "avifile_private.h" 34 #include "extrachunk.h" 35 36 #include "wine/debug.h" 37 38 WINE_DEFAULT_DEBUG_CHANNEL(avifile); 39 40 /***********************************************************************/ 41 42 #define formtypeWAVE mmioFOURCC('W','A','V','E') 43 #define ckidWAVEFORMAT mmioFOURCC('f','m','t',' ') 44 #define ckidWAVEFACT mmioFOURCC('f','a','c','t') 45 #define ckidWAVEDATA mmioFOURCC('d','a','t','a') 46 47 /***********************************************************************/ 48 49 #define ENDIAN_SWAPWORD(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) 50 #define ENDIAN_SWAPDWORD(x) (ENDIAN_SWAPWORD((x >> 16) & 0xFFFF) | \ 51 ENDIAN_SWAPWORD(x & 0xFFFF) << 16) 52 53 #ifdef WORDS_BIGENDIAN 54 #define BE2H_WORD(x) (x) 55 #define BE2H_DWORD(x) (x) 56 #define LE2H_WORD(x) ENDIAN_SWAPWORD(x) 57 #define LE2H_DWORD(x) ENDIAN_SWAPDWORD(x) 58 #else 59 #define BE2H_WORD(x) ENDIAN_SWAPWORD(x) 60 #define BE2H_DWORD(x) ENDIAN_SWAPDWORD(x) 61 #define LE2H_WORD(x) (x) 62 #define LE2H_DWORD(x) (x) 63 #endif 64 65 typedef struct { 66 FOURCC fccType; 67 DWORD offset; 68 DWORD size; 69 INT encoding; 70 DWORD sampleRate; 71 DWORD channels; 72 } SUNAUDIOHEADER; 73 74 #define AU_ENCODING_ULAW_8 1 75 #define AU_ENCODING_PCM_8 2 76 #define AU_ENCODING_PCM_16 3 77 #define AU_ENCODING_PCM_24 4 78 #define AU_ENCODING_PCM_32 5 79 #define AU_ENCODING_FLOAT 6 80 #define AU_ENCODING_DOUBLE 7 81 #define AU_ENCODING_ADPCM_G721_32 23 82 #define AU_ENCODING_ADPCM_G722 24 83 #define AU_ENCODING_ADPCM_G723_24 25 84 #define AU_ENCODING_ADPCM_G723_5 26 85 #define AU_ENCODING_ALAW_8 27 86 87 /***********************************************************************/ 88 89 typedef struct _IAVIFileImpl { 90 IUnknown IUnknown_inner; 91 IAVIFile IAVIFile_iface; 92 IPersistFile IPersistFile_iface; 93 IAVIStream IAVIStream_iface; 94 IUnknown *outer_unk; 95 LONG ref; 96 /* IAVIFile, IAVIStream stuff... */ 97 AVIFILEINFOW fInfo; 98 AVISTREAMINFOW sInfo; 99 100 LPWAVEFORMATEX lpFormat; 101 LONG cbFormat; 102 103 MMCKINFO ckData; 104 105 EXTRACHUNKS extra; 106 107 /* IPersistFile stuff ... */ 108 HMMIO hmmio; 109 LPWSTR szFileName; 110 UINT uMode; 111 BOOL fDirty; 112 } IAVIFileImpl; 113 114 /***********************************************************************/ 115 116 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); 117 static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This); 118 static HRESULT AVIFILE_SaveFile(const IAVIFileImpl *This); 119 120 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface) 121 { 122 return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner); 123 } 124 125 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ret_iface) 126 { 127 IAVIFileImpl *This = impl_from_IUnknown(iface); 128 129 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ret_iface); 130 131 if (IsEqualGUID(&IID_IUnknown, riid)) 132 *ret_iface = &This->IUnknown_inner; 133 else if (IsEqualGUID(&IID_IAVIFile, riid)) 134 *ret_iface = &This->IAVIFile_iface; 135 else if (IsEqualGUID(&IID_IAVIStream, riid)) 136 *ret_iface = &This->IAVIStream_iface; 137 else if (IsEqualGUID(&IID_IPersistFile, riid)) 138 *ret_iface = &This->IPersistFile_iface; 139 else { 140 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ret_iface); 141 *ret_iface = NULL; 142 return E_NOINTERFACE; 143 } 144 145 /* Violation of the COM aggregation ref counting rule */ 146 IUnknown_AddRef(&This->IUnknown_inner); 147 return S_OK; 148 } 149 150 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface) 151 { 152 IAVIFileImpl *This = impl_from_IUnknown(iface); 153 ULONG ref = InterlockedIncrement(&This->ref); 154 155 TRACE("(%p) ref=%d\n", This, ref); 156 157 return ref; 158 } 159 160 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface) 161 { 162 IAVIFileImpl *This = impl_from_IUnknown(iface); 163 ULONG ref = InterlockedDecrement(&This->ref); 164 165 TRACE("(%p) ref=%d\n", This, ref); 166 167 if (!ref) { 168 /* need to write headers to file */ 169 if (This->fDirty) 170 AVIFILE_SaveFile(This); 171 172 HeapFree(GetProcessHeap(), 0, This->lpFormat); 173 This->lpFormat = NULL; 174 This->cbFormat = 0; 175 HeapFree(GetProcessHeap(), 0, This->extra.lp); 176 This->extra.lp = NULL; 177 This->extra.cb = 0; 178 HeapFree(GetProcessHeap(), 0, This->szFileName); 179 This->szFileName = NULL; 180 if (This->hmmio) { 181 mmioClose(This->hmmio, 0); 182 This->hmmio = NULL; 183 } 184 HeapFree(GetProcessHeap(), 0, This); 185 } 186 187 return ref; 188 } 189 190 static const IUnknownVtbl unk_vtbl = 191 { 192 IUnknown_fnQueryInterface, 193 IUnknown_fnAddRef, 194 IUnknown_fnRelease 195 }; 196 197 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface) 198 { 199 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface); 200 } 201 202 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ret_iface) 203 { 204 IAVIFileImpl *This = impl_from_IAVIFile(iface); 205 206 return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); 207 } 208 209 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) 210 { 211 IAVIFileImpl *This = impl_from_IAVIFile(iface); 212 213 return IUnknown_AddRef(This->outer_unk); 214 } 215 216 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) 217 { 218 IAVIFileImpl *This = impl_from_IAVIFile(iface); 219 220 return IUnknown_Release(This->outer_unk); 221 } 222 223 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size) 224 { 225 IAVIFileImpl *This = impl_from_IAVIFile(iface); 226 227 TRACE("(%p,%p,%d)\n",iface,afi,size); 228 229 if (afi == NULL) 230 return AVIERR_BADPARAM; 231 if (size < 0) 232 return AVIERR_BADSIZE; 233 234 /* update file info */ 235 This->fInfo.dwFlags = 0; 236 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 237 if (This->lpFormat != NULL) { 238 assert(This->sInfo.dwScale != 0); 239 240 This->fInfo.dwStreams = 1; 241 This->fInfo.dwScale = This->sInfo.dwScale; 242 This->fInfo.dwRate = This->sInfo.dwRate; 243 This->fInfo.dwLength = This->sInfo.dwLength; 244 This->fInfo.dwSuggestedBufferSize = This->ckData.cksize; 245 This->fInfo.dwMaxBytesPerSec = 246 MulDiv(This->sInfo.dwSampleSize,This->sInfo.dwRate,This->sInfo.dwScale); 247 } 248 249 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); 250 251 if ((DWORD)size < sizeof(This->fInfo)) 252 return AVIERR_BUFFERTOOSMALL; 253 return AVIERR_OK; 254 } 255 256 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType, 257 LONG lParam) 258 { 259 IAVIFileImpl *This = impl_from_IAVIFile(iface); 260 261 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam); 262 263 /* check parameter */ 264 if (avis == NULL) 265 return AVIERR_BADPARAM; 266 267 *avis = NULL; 268 269 /* Does our stream exists? */ 270 if (lParam != 0 || This->fInfo.dwStreams == 0) 271 return AVIERR_NODATA; 272 if (fccType != 0 && fccType != streamtypeAUDIO) 273 return AVIERR_NODATA; 274 275 *avis = &This->IAVIStream_iface; 276 IAVIStream_AddRef(*avis); 277 278 return AVIERR_OK; 279 } 280 281 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis, 282 AVISTREAMINFOW *asi) 283 { 284 IAVIFileImpl *This = impl_from_IAVIFile(iface); 285 286 TRACE("(%p,%p,%p)\n", iface, avis, asi); 287 288 /* check parameters */ 289 if (avis == NULL || asi == NULL) 290 return AVIERR_BADPARAM; 291 292 *avis = NULL; 293 294 /* We only support one audio stream */ 295 if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL) 296 return AVIERR_UNSUPPORTED; 297 if (asi->fccType != streamtypeAUDIO) 298 return AVIERR_UNSUPPORTED; 299 300 /* Does the user have write permission? */ 301 if ((This->uMode & MMIO_RWMODE) == 0) 302 return AVIERR_READONLY; 303 304 This->cbFormat = 0; 305 This->lpFormat = NULL; 306 307 memcpy(&This->sInfo, asi, sizeof(This->sInfo)); 308 309 /* make sure streaminfo if okay for us */ 310 This->sInfo.fccHandler = 0; 311 This->sInfo.dwFlags = 0; 312 This->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; 313 This->sInfo.dwStart = 0; 314 This->sInfo.dwInitialFrames = 0; 315 This->sInfo.dwFormatChangeCount = 0; 316 SetRectEmpty(&This->sInfo.rcFrame); 317 318 This->fInfo.dwStreams = 1; 319 This->fInfo.dwScale = This->sInfo.dwScale; 320 This->fInfo.dwRate = This->sInfo.dwRate; 321 This->fInfo.dwLength = This->sInfo.dwLength; 322 323 This->ckData.dwDataOffset = 0; 324 This->ckData.cksize = 0; 325 326 *avis = &This->IAVIStream_iface; 327 IAVIStream_AddRef(*avis); 328 329 return AVIERR_OK; 330 } 331 332 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size) 333 { 334 IAVIFileImpl *This = impl_from_IAVIFile(iface); 335 336 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size); 337 338 /* check parameters */ 339 if (lpData == NULL) 340 return AVIERR_BADPARAM; 341 if (size < 0) 342 return AVIERR_BADSIZE; 343 344 /* Do we have write permission? */ 345 if ((This->uMode & MMIO_RWMODE) == 0) 346 return AVIERR_READONLY; 347 348 This->fDirty = TRUE; 349 350 return WriteExtraChunk(&This->extra, ckid, lpData, size); 351 } 352 353 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size) 354 { 355 IAVIFileImpl *This = impl_from_IAVIFile(iface); 356 357 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size); 358 359 return ReadExtraChunk(&This->extra, ckid, lpData, size); 360 } 361 362 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) 363 { 364 TRACE("(%p)\n",iface); 365 366 /* This is only needed for interleaved files. 367 * We have only one stream, which can't be interleaved. 368 */ 369 return AVIERR_OK; 370 } 371 372 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam) 373 { 374 IAVIFileImpl *This = impl_from_IAVIFile(iface); 375 376 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam); 377 378 /* check parameter */ 379 if (lParam < 0) 380 return AVIERR_BADPARAM; 381 382 /* Do we have our audio stream? */ 383 if (lParam != 0 || This->fInfo.dwStreams == 0 || 384 (fccType != 0 && fccType != streamtypeAUDIO)) 385 return AVIERR_NODATA; 386 387 /* Have user write permissions? */ 388 if ((This->uMode & MMIO_RWMODE) == 0) 389 return AVIERR_READONLY; 390 391 HeapFree(GetProcessHeap(), 0, This->lpFormat); 392 This->lpFormat = NULL; 393 This->cbFormat = 0; 394 395 /* update infos */ 396 This->ckData.dwDataOffset = 0; 397 This->ckData.cksize = 0; 398 399 This->sInfo.dwScale = 0; 400 This->sInfo.dwRate = 0; 401 This->sInfo.dwLength = 0; 402 This->sInfo.dwSuggestedBufferSize = 0; 403 404 This->fInfo.dwStreams = 0; 405 This->fInfo.dwEditCount++; 406 407 This->fDirty = TRUE; 408 409 return AVIERR_OK; 410 } 411 412 static const struct IAVIFileVtbl iwavft = { 413 IAVIFile_fnQueryInterface, 414 IAVIFile_fnAddRef, 415 IAVIFile_fnRelease, 416 IAVIFile_fnInfo, 417 IAVIFile_fnGetStream, 418 IAVIFile_fnCreateStream, 419 IAVIFile_fnWriteData, 420 IAVIFile_fnReadData, 421 IAVIFile_fnEndRecord, 422 IAVIFile_fnDeleteStream 423 }; 424 425 /***********************************************************************/ 426 427 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface) 428 { 429 return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface); 430 } 431 432 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, 433 void **ret_iface) 434 { 435 IAVIFileImpl *This = impl_from_IPersistFile(iface); 436 437 return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); 438 } 439 440 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) 441 { 442 IAVIFileImpl *This = impl_from_IPersistFile(iface); 443 444 return IUnknown_AddRef(This->outer_unk); 445 } 446 447 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) 448 { 449 IAVIFileImpl *This = impl_from_IPersistFile(iface); 450 451 return IUnknown_Release(This->outer_unk); 452 } 453 454 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, 455 LPCLSID pClassID) 456 { 457 TRACE("(%p,%p)\n", iface, pClassID); 458 459 if (pClassID == NULL) 460 return AVIERR_BADPARAM; 461 462 *pClassID = CLSID_WAVFile; 463 464 return AVIERR_OK; 465 } 466 467 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) 468 { 469 IAVIFileImpl *This = impl_from_IPersistFile(iface); 470 471 TRACE("(%p)\n", iface); 472 473 return (This->fDirty ? S_OK : S_FALSE); 474 } 475 476 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode) 477 { 478 IAVIFileImpl *This = impl_from_IPersistFile(iface); 479 WCHAR wszStreamFmt[50]; 480 INT len; 481 482 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode); 483 484 /* check parameter */ 485 if (pszFileName == NULL) 486 return AVIERR_BADPARAM; 487 488 if (This->hmmio != NULL) 489 return AVIERR_ERROR; /* No reuse of this object for another file! */ 490 491 /* remember mode and name */ 492 This->uMode = dwMode; 493 494 len = lstrlenW(pszFileName) + 1; 495 This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 496 if (This->szFileName == NULL) 497 return AVIERR_MEMORY; 498 lstrcpyW(This->szFileName, pszFileName); 499 500 /* try to open the file */ 501 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode); 502 if (This->hmmio == NULL) { 503 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ 504 LPSTR szFileName; 505 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, 506 NULL, 0, NULL, NULL); 507 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR)); 508 if (szFileName == NULL) 509 return AVIERR_MEMORY; 510 511 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, 512 len, NULL, NULL); 513 514 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); 515 HeapFree(GetProcessHeap(), 0, szFileName); 516 if (This->hmmio == NULL) 517 return AVIERR_FILEOPEN; 518 } 519 520 memset(& This->fInfo, 0, sizeof(This->fInfo)); 521 memset(& This->sInfo, 0, sizeof(This->sInfo)); 522 523 LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType, 524 ARRAY_SIZE(This->fInfo.szFileType)); 525 if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT, 526 wszStreamFmt, ARRAY_SIZE(wszStreamFmt)) > 0) { 527 wsprintfW(This->sInfo.szName, wszStreamFmt, 528 AVIFILE_BasenameW(This->szFileName)); 529 } 530 531 /* should we create a new file? */ 532 if (dwMode & OF_CREATE) { 533 /* nothing more to do */ 534 return AVIERR_OK; 535 } else 536 return AVIFILE_LoadFile(This); 537 } 538 539 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, 540 LPCOLESTR pszFileName,BOOL fRemember) 541 { 542 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); 543 544 /* We write directly to disk, so nothing to do. */ 545 546 return AVIERR_OK; 547 } 548 549 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, 550 LPCOLESTR pszFileName) 551 { 552 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); 553 554 /* We write directly to disk, so nothing to do. */ 555 556 return AVIERR_OK; 557 } 558 559 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName) 560 { 561 IAVIFileImpl *This = impl_from_IPersistFile(iface); 562 563 TRACE("(%p,%p)\n", iface, ppszFileName); 564 565 if (ppszFileName == NULL) 566 return AVIERR_BADPARAM; 567 568 *ppszFileName = NULL; 569 570 if (This->szFileName) { 571 int len = lstrlenW(This->szFileName) + 1; 572 573 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR)); 574 if (*ppszFileName == NULL) 575 return AVIERR_MEMORY; 576 577 lstrcpyW(*ppszFileName, This->szFileName); 578 } 579 580 return AVIERR_OK; 581 } 582 583 static const struct IPersistFileVtbl iwavpft = { 584 IPersistFile_fnQueryInterface, 585 IPersistFile_fnAddRef, 586 IPersistFile_fnRelease, 587 IPersistFile_fnGetClassID, 588 IPersistFile_fnIsDirty, 589 IPersistFile_fnLoad, 590 IPersistFile_fnSave, 591 IPersistFile_fnSaveCompleted, 592 IPersistFile_fnGetCurFile 593 }; 594 595 /***********************************************************************/ 596 597 static inline IAVIFileImpl *impl_from_IAVIStream(IAVIStream *iface) 598 { 599 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIStream_iface); 600 } 601 602 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ret_iface) 603 { 604 IAVIFileImpl *This = impl_from_IAVIStream(iface); 605 606 return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); 607 } 608 609 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) 610 { 611 IAVIFileImpl *This = impl_from_IAVIStream(iface); 612 613 return IUnknown_AddRef(This->outer_unk); 614 } 615 616 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) 617 { 618 IAVIFileImpl *This = impl_from_IAVIStream(iface); 619 620 return IUnknown_Release(This->outer_unk); 621 } 622 623 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, 624 LPARAM lParam2) 625 { 626 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); 627 628 /* This IAVIStream interface needs an WAVFile */ 629 return AVIERR_UNSUPPORTED; 630 } 631 632 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size) 633 { 634 IAVIFileImpl *This = impl_from_IAVIStream(iface); 635 636 TRACE("(%p,%p,%d)\n", iface, psi, size); 637 638 if (psi == NULL) 639 return AVIERR_BADPARAM; 640 if (size < 0) 641 return AVIERR_BADSIZE; 642 643 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); 644 645 if ((DWORD)size < sizeof(This->sInfo)) 646 return AVIERR_BUFFERTOOSMALL; 647 return AVIERR_OK; 648 } 649 650 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags) 651 { 652 IAVIFileImpl *This = impl_from_IAVIStream(iface); 653 654 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); 655 656 /* Do we have data? */ 657 if (This->lpFormat == NULL) 658 return -1; 659 660 /* We don't have an index */ 661 if (flags & FIND_INDEX) 662 return -1; 663 664 if (flags & FIND_FROM_START) { 665 pos = This->sInfo.dwStart; 666 flags &= ~(FIND_FROM_START|FIND_PREV); 667 flags |= FIND_NEXT; 668 } 669 670 if (flags & FIND_FORMAT) { 671 if ((flags & FIND_NEXT) && pos > 0) 672 pos = -1; 673 else 674 pos = 0; 675 } 676 677 if ((flags & FIND_RET) == FIND_LENGTH || 678 (flags & FIND_RET) == FIND_SIZE) 679 return This->sInfo.dwSampleSize; 680 if ((flags & FIND_RET) == FIND_OFFSET) 681 return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize; 682 683 return pos; 684 } 685 686 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format, 687 LONG *formatsize) 688 { 689 IAVIFileImpl *This = impl_from_IAVIStream(iface); 690 691 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); 692 693 if (formatsize == NULL) 694 return AVIERR_BADPARAM; 695 696 /* only interested in needed buffersize? */ 697 if (format == NULL || *formatsize <= 0) { 698 *formatsize = This->cbFormat; 699 700 return AVIERR_OK; 701 } 702 703 /* copy initial format (only as much as will fit) */ 704 memcpy(format, This->lpFormat, min(*formatsize, This->cbFormat)); 705 if (*formatsize < This->cbFormat) { 706 *formatsize = This->cbFormat; 707 return AVIERR_BUFFERTOOSMALL; 708 } 709 710 *formatsize = This->cbFormat; 711 return AVIERR_OK; 712 } 713 714 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format, 715 LONG formatsize) 716 { 717 IAVIFileImpl *This = impl_from_IAVIStream(iface); 718 719 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); 720 721 /* check parameters */ 722 if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT)) 723 return AVIERR_BADPARAM; 724 725 /* We can only do this to an empty wave file, but ignore call 726 * if still same format */ 727 if (This->lpFormat != NULL) { 728 if (formatsize != This->cbFormat || 729 memcmp(format, This->lpFormat, formatsize) != 0) 730 return AVIERR_UNSUPPORTED; 731 732 return AVIERR_OK; 733 } 734 735 /* only support start at position 0 */ 736 if (pos != 0) 737 return AVIERR_UNSUPPORTED; 738 739 /* Do we have write permission? */ 740 if ((This->uMode & MMIO_RWMODE) == 0) 741 return AVIERR_READONLY; 742 743 /* get memory for format and copy it */ 744 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); 745 if (This->lpFormat == NULL) 746 return AVIERR_MEMORY; 747 748 This->cbFormat = formatsize; 749 memcpy(This->lpFormat, format, formatsize); 750 751 /* update info's about 'data' chunk */ 752 This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD); 753 This->ckData.cksize = 0; 754 755 /* for non-pcm format we need also a 'fact' chunk */ 756 if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) 757 This->ckData.dwDataOffset += 3 * sizeof(DWORD); 758 759 /* update stream and file info */ 760 This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; 761 This->sInfo.dwScale = This->lpFormat->nBlockAlign; 762 This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; 763 This->sInfo.dwLength = 0; 764 This->sInfo.dwSuggestedBufferSize = 0; 765 766 return AVIERR_OK; 767 } 768 769 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer, 770 LONG buffersize, LONG *bytesread, LONG *samplesread) 771 { 772 IAVIFileImpl *This = impl_from_IAVIStream(iface); 773 774 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, 775 buffersize, bytesread, samplesread); 776 777 /* clear return parameters if given */ 778 if (bytesread != NULL) 779 *bytesread = 0; 780 if (samplesread != NULL) 781 *samplesread = 0; 782 783 /* positions without data */ 784 if (start < 0 || (DWORD)start > This->sInfo.dwLength) 785 return AVIERR_OK; 786 787 /* check samples */ 788 if (samples < 0) 789 samples = 0; 790 if (buffersize > 0) { 791 if (samples > 0) 792 samples = min((DWORD)samples, buffersize / This->sInfo.dwSampleSize); 793 else 794 samples = buffersize / This->sInfo.dwSampleSize; 795 } 796 797 /* limit to end of stream */ 798 if ((DWORD)(start + samples) > This->sInfo.dwLength) 799 samples = This->sInfo.dwLength - start; 800 801 /* request only the sizes? */ 802 if (buffer == NULL || buffersize <= 0) { 803 /* then I need at least one parameter for it */ 804 if (bytesread == NULL && samplesread == NULL) 805 return AVIERR_BADPARAM; 806 807 if (bytesread != NULL) 808 *bytesread = samples * This->sInfo.dwSampleSize; 809 if (samplesread != NULL) 810 *samplesread = samples; 811 812 return AVIERR_OK; 813 } 814 815 /* nothing to read? */ 816 if (samples == 0) 817 return AVIERR_OK; 818 819 /* Can I read at least one sample? */ 820 if ((DWORD)buffersize < This->sInfo.dwSampleSize) 821 return AVIERR_BUFFERTOOSMALL; 822 823 buffersize = samples * This->sInfo.dwSampleSize; 824 825 if (mmioSeek(This->hmmio, This->ckData.dwDataOffset 826 + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) 827 return AVIERR_FILEREAD; 828 if (mmioRead(This->hmmio, buffer, buffersize) != buffersize) 829 return AVIERR_FILEREAD; 830 831 /* fill out return parameters if given */ 832 if (bytesread != NULL) 833 *bytesread = buffersize; 834 if (samplesread != NULL) 835 *samplesread = samples; 836 837 return AVIERR_OK; 838 } 839 840 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer, 841 LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten) 842 { 843 IAVIFileImpl *This = impl_from_IAVIStream(iface); 844 845 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, 846 buffer, buffersize, flags, sampwritten, byteswritten); 847 848 /* clear return parameters if given */ 849 if (sampwritten != NULL) 850 *sampwritten = 0; 851 if (byteswritten != NULL) 852 *byteswritten = 0; 853 854 /* check parameters */ 855 if (buffer == NULL && (buffersize > 0 || samples > 0)) 856 return AVIERR_BADPARAM; 857 858 /* Do we have write permission? */ 859 if ((This->uMode & MMIO_RWMODE) == 0) 860 return AVIERR_READONLY; 861 862 /* < 0 means "append" */ 863 if (start < 0) 864 start = This->sInfo.dwStart + This->sInfo.dwLength; 865 866 /* check buffersize -- must multiple of samplesize */ 867 if (buffersize & ~(This->sInfo.dwSampleSize - 1)) 868 return AVIERR_BADSIZE; 869 870 /* do we have anything to write? */ 871 if (buffer != NULL && buffersize > 0) { 872 This->fDirty = TRUE; 873 874 if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + 875 start * This->sInfo.dwSampleSize, SEEK_SET) == -1) 876 return AVIERR_FILEWRITE; 877 if (mmioWrite(This->hmmio, buffer, buffersize) != buffersize) 878 return AVIERR_FILEWRITE; 879 880 This->sInfo.dwLength = max(This->sInfo.dwLength, (DWORD)start + samples); 881 This->ckData.cksize = max(This->ckData.cksize, 882 start * This->sInfo.dwSampleSize + buffersize); 883 884 /* fill out return parameters if given */ 885 if (sampwritten != NULL) 886 *sampwritten = samples; 887 if (byteswritten != NULL) 888 *byteswritten = buffersize; 889 } 890 891 return AVIERR_OK; 892 } 893 894 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples) 895 { 896 IAVIFileImpl *This = impl_from_IAVIStream(iface); 897 898 TRACE("(%p,%d,%d)\n", iface, start, samples); 899 900 /* check parameters */ 901 if (start < 0 || samples < 0) 902 return AVIERR_BADPARAM; 903 904 /* Delete before start of stream? */ 905 if ((DWORD)(start + samples) < This->sInfo.dwStart) 906 return AVIERR_OK; 907 908 /* Delete after end of stream? */ 909 if ((DWORD)start > This->sInfo.dwLength) 910 return AVIERR_OK; 911 912 /* For the rest we need write permissions */ 913 if ((This->uMode & MMIO_RWMODE) == 0) 914 return AVIERR_READONLY; 915 916 if ((DWORD)(start + samples) >= This->sInfo.dwLength) { 917 /* deletion at end */ 918 samples = This->sInfo.dwLength - start; 919 This->sInfo.dwLength -= samples; 920 This->ckData.cksize -= samples * This->sInfo.dwSampleSize; 921 } else if ((DWORD)start <= This->sInfo.dwStart) { 922 /* deletion at start */ 923 samples = This->sInfo.dwStart - start; 924 start = This->sInfo.dwStart; 925 This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize; 926 This->ckData.cksize -= samples * This->sInfo.dwSampleSize; 927 } else { 928 /* deletion inside stream -- needs playlist and cue's */ 929 FIXME(": deletion inside of stream not supported!\n"); 930 931 return AVIERR_UNSUPPORTED; 932 } 933 934 This->fDirty = TRUE; 935 936 return AVIERR_OK; 937 } 938 939 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread) 940 { 941 IAVIFileImpl *This = impl_from_IAVIStream(iface); 942 943 return IAVIFile_ReadData(&This->IAVIFile_iface, fcc, lp, lpread); 944 } 945 946 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size) 947 { 948 IAVIFileImpl *This = impl_from_IAVIStream(iface); 949 950 return IAVIFile_WriteData(&This->IAVIFile_iface, fcc, lp, size); 951 } 952 953 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, 954 LPAVISTREAMINFOW info, LONG infolen) 955 { 956 FIXME("(%p,%p,%d): stub\n", iface, info, infolen); 957 958 return E_FAIL; 959 } 960 961 static const struct IAVIStreamVtbl iwavst = { 962 IAVIStream_fnQueryInterface, 963 IAVIStream_fnAddRef, 964 IAVIStream_fnRelease, 965 IAVIStream_fnCreate, 966 IAVIStream_fnInfo, 967 IAVIStream_fnFindSample, 968 IAVIStream_fnReadFormat, 969 IAVIStream_fnSetFormat, 970 IAVIStream_fnRead, 971 IAVIStream_fnWrite, 972 IAVIStream_fnDelete, 973 IAVIStream_fnReadData, 974 IAVIStream_fnWriteData, 975 IAVIStream_fnSetInfo 976 }; 977 978 HRESULT AVIFILE_CreateWAVFile(IUnknown *outer_unk, REFIID riid, void **ret_iface) 979 { 980 IAVIFileImpl *pfile; 981 HRESULT hr; 982 983 *ret_iface = NULL; 984 985 pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pfile)); 986 if (!pfile) 987 return AVIERR_MEMORY; 988 989 pfile->IUnknown_inner.lpVtbl = &unk_vtbl; 990 pfile->IAVIFile_iface.lpVtbl = &iwavft; 991 pfile->IPersistFile_iface.lpVtbl = &iwavpft; 992 pfile->IAVIStream_iface.lpVtbl = &iwavst; 993 pfile->ref = 1; 994 if (outer_unk) 995 pfile->outer_unk = outer_unk; 996 else 997 pfile->outer_unk = &pfile->IUnknown_inner; 998 999 hr = IUnknown_QueryInterface(&pfile->IUnknown_inner, riid, ret_iface); 1000 IUnknown_Release(&pfile->IUnknown_inner); 1001 1002 return hr; 1003 } 1004 1005 /***********************************************************************/ 1006 1007 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) 1008 { 1009 MMCKINFO ckRIFF; 1010 MMCKINFO ck; 1011 1012 This->sInfo.dwLength = 0; /* just to be sure */ 1013 This->fDirty = FALSE; 1014 1015 /* search for RIFF chunk */ 1016 ckRIFF.fccType = 0; /* find any */ 1017 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { 1018 return AVIFILE_LoadSunFile(This); 1019 } 1020 1021 if (ckRIFF.fccType != formtypeWAVE) 1022 return AVIERR_BADFORMAT; 1023 1024 /* search WAVE format chunk */ 1025 ck.ckid = ckidWAVEFORMAT; 1026 if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, 1027 &ckRIFF, MMIO_FINDCHUNK) != S_OK) 1028 return AVIERR_FILEREAD; 1029 1030 /* get memory for format and read it */ 1031 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize); 1032 if (This->lpFormat == NULL) 1033 return AVIERR_FILEREAD; 1034 This->cbFormat = ck.cksize; 1035 1036 if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) 1037 return AVIERR_FILEREAD; 1038 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1039 return AVIERR_FILEREAD; 1040 1041 /* Non-pcm formats have a fact chunk. 1042 * We don't need it, so simply add it to the extra chunks. 1043 */ 1044 1045 /* find the big data chunk */ 1046 This->ckData.ckid = ckidWAVEDATA; 1047 if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData, 1048 &ckRIFF, MMIO_FINDCHUNK) != S_OK) 1049 return AVIERR_FILEREAD; 1050 1051 memset(&This->sInfo, 0, sizeof(This->sInfo)); 1052 This->sInfo.fccType = streamtypeAUDIO; 1053 This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; 1054 This->sInfo.dwSampleSize = 1055 This->sInfo.dwScale = This->lpFormat->nBlockAlign; 1056 This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; 1057 This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; 1058 1059 This->fInfo.dwStreams = 1; 1060 1061 if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) { 1062 /* seems to be truncated */ 1063 WARN(": file seems to be truncated!\n"); 1064 This->ckData.cksize = mmioSeek(This->hmmio, 0, SEEK_END) - 1065 This->ckData.dwDataOffset; 1066 This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; 1067 This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; 1068 } 1069 1070 /* ignore errors */ 1071 FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0); 1072 1073 return AVIERR_OK; 1074 } 1075 1076 static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This) 1077 { 1078 SUNAUDIOHEADER auhdr; 1079 1080 mmioSeek(This->hmmio, 0, SEEK_SET); 1081 if (mmioRead(This->hmmio, (HPSTR)&auhdr, sizeof(auhdr)) != sizeof(auhdr)) 1082 return AVIERR_FILEREAD; 1083 1084 if (auhdr.fccType == 0x0064732E) { 1085 /* header in little endian */ 1086 This->ckData.dwDataOffset = LE2H_DWORD(auhdr.offset); 1087 This->ckData.cksize = LE2H_DWORD(auhdr.size); 1088 1089 auhdr.encoding = LE2H_DWORD(auhdr.encoding); 1090 auhdr.sampleRate = LE2H_DWORD(auhdr.sampleRate); 1091 auhdr.channels = LE2H_DWORD(auhdr.channels); 1092 } else if (auhdr.fccType == mmioFOURCC('.','s','n','d')) { 1093 /* header in big endian */ 1094 This->ckData.dwDataOffset = BE2H_DWORD(auhdr.offset); 1095 This->ckData.cksize = BE2H_DWORD(auhdr.size); 1096 1097 auhdr.encoding = BE2H_DWORD(auhdr.encoding); 1098 auhdr.sampleRate = BE2H_DWORD(auhdr.sampleRate); 1099 auhdr.channels = BE2H_DWORD(auhdr.channels); 1100 } else 1101 return AVIERR_FILEREAD; 1102 1103 if (auhdr.channels < 1) 1104 return AVIERR_BADFORMAT; 1105 1106 /* get size of header */ 1107 switch(auhdr.encoding) { 1108 case AU_ENCODING_ADPCM_G721_32: 1109 This->cbFormat = sizeof(G721_ADPCMWAVEFORMAT); break; 1110 case AU_ENCODING_ADPCM_G723_24: 1111 This->cbFormat = sizeof(G723_ADPCMWAVEFORMAT); break; 1112 case AU_ENCODING_ADPCM_G722: 1113 case AU_ENCODING_ADPCM_G723_5: 1114 WARN("unsupported Sun audio format %d\n", auhdr.encoding); 1115 return AVIERR_UNSUPPORTED; /* FIXME */ 1116 default: 1117 This->cbFormat = sizeof(WAVEFORMATEX); break; 1118 }; 1119 1120 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, This->cbFormat); 1121 if (This->lpFormat == NULL) 1122 return AVIERR_MEMORY; 1123 1124 This->lpFormat->nChannels = auhdr.channels; 1125 This->lpFormat->nSamplesPerSec = auhdr.sampleRate; 1126 switch(auhdr.encoding) { 1127 case AU_ENCODING_ULAW_8: 1128 This->lpFormat->wFormatTag = WAVE_FORMAT_MULAW; 1129 This->lpFormat->wBitsPerSample = 8; 1130 break; 1131 case AU_ENCODING_PCM_8: 1132 This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; 1133 This->lpFormat->wBitsPerSample = 8; 1134 break; 1135 case AU_ENCODING_PCM_16: 1136 This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; 1137 This->lpFormat->wBitsPerSample = 16; 1138 break; 1139 case AU_ENCODING_PCM_24: 1140 This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; 1141 This->lpFormat->wBitsPerSample = 24; 1142 break; 1143 case AU_ENCODING_PCM_32: 1144 This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; 1145 This->lpFormat->wBitsPerSample = 32; 1146 break; 1147 case AU_ENCODING_ALAW_8: 1148 This->lpFormat->wFormatTag = WAVE_FORMAT_ALAW; 1149 This->lpFormat->wBitsPerSample = 8; 1150 break; 1151 case AU_ENCODING_ADPCM_G721_32: 1152 This->lpFormat->wFormatTag = WAVE_FORMAT_G721_ADPCM; 1153 This->lpFormat->wBitsPerSample = (3*5*8); 1154 This->lpFormat->nBlockAlign = 15*15*8; 1155 This->lpFormat->cbSize = sizeof(WORD); 1156 ((LPG721_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; 1157 break; 1158 case AU_ENCODING_ADPCM_G723_24: 1159 This->lpFormat->wFormatTag = WAVE_FORMAT_G723_ADPCM; 1160 This->lpFormat->wBitsPerSample = (3*5*8); 1161 This->lpFormat->nBlockAlign = 15*15*8; 1162 This->lpFormat->cbSize = 2*sizeof(WORD); 1163 ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->cbExtraSize = 0; 1164 ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; 1165 break; 1166 default: 1167 WARN("unsupported Sun audio format %d\n", auhdr.encoding); 1168 return AVIERR_UNSUPPORTED; 1169 }; 1170 1171 This->lpFormat->nBlockAlign = 1172 (This->lpFormat->nChannels * This->lpFormat->wBitsPerSample) / 8; 1173 if (This->lpFormat->nBlockAlign == 0 && This->lpFormat->wBitsPerSample < 8) 1174 This->lpFormat->nBlockAlign++; 1175 This->lpFormat->nAvgBytesPerSec = 1176 This->lpFormat->nBlockAlign * This->lpFormat->nSamplesPerSec; 1177 1178 This->fDirty = FALSE; 1179 1180 This->sInfo.fccType = streamtypeAUDIO; 1181 This->sInfo.fccHandler = 0; 1182 This->sInfo.dwFlags = 0; 1183 This->sInfo.wPriority = 0; 1184 This->sInfo.wLanguage = 0; 1185 This->sInfo.dwInitialFrames = 0; 1186 This->sInfo.dwScale = This->lpFormat->nBlockAlign; 1187 This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; 1188 This->sInfo.dwStart = 0; 1189 This->sInfo.dwLength = 1190 This->ckData.cksize / This->lpFormat->nBlockAlign; 1191 This->sInfo.dwSuggestedBufferSize = This->sInfo.dwLength; 1192 This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; 1193 1194 This->fInfo.dwStreams = 1; 1195 This->fInfo.dwScale = 1; 1196 This->fInfo.dwRate = This->lpFormat->nSamplesPerSec; 1197 This->fInfo.dwLength = 1198 MulDiv(This->ckData.cksize, This->lpFormat->nSamplesPerSec, 1199 This->lpFormat->nAvgBytesPerSec); 1200 1201 return AVIERR_OK; 1202 } 1203 1204 static HRESULT AVIFILE_SaveFile(const IAVIFileImpl *This) 1205 { 1206 MMCKINFO ckRIFF; 1207 MMCKINFO ck; 1208 1209 mmioSeek(This->hmmio, 0, SEEK_SET); 1210 1211 /* create the RIFF chunk with formtype WAVE */ 1212 ckRIFF.fccType = formtypeWAVE; 1213 ckRIFF.cksize = 0; 1214 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) 1215 return AVIERR_FILEWRITE; 1216 1217 /* the next chunk is the format */ 1218 ck.ckid = ckidWAVEFORMAT; 1219 ck.cksize = This->cbFormat; 1220 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 1221 return AVIERR_FILEWRITE; 1222 if (This->lpFormat != NULL && This->cbFormat > 0) { 1223 if (mmioWrite(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) 1224 return AVIERR_FILEWRITE; 1225 } 1226 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1227 return AVIERR_FILEWRITE; 1228 1229 /* fact chunk is needed for non-pcm waveforms */ 1230 if (This->lpFormat != NULL && This->cbFormat > sizeof(PCMWAVEFORMAT) && 1231 This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) { 1232 WAVEFORMATEX wfx; 1233 DWORD dwFactLength; 1234 HACMSTREAM has; 1235 1236 /* try to open an appropriate audio codec to figure out 1237 * data for fact-chunk */ 1238 wfx.wFormatTag = WAVE_FORMAT_PCM; 1239 if (acmFormatSuggest(NULL, This->lpFormat, &wfx, 1240 sizeof(wfx), ACM_FORMATSUGGESTF_WFORMATTAG)) { 1241 acmStreamOpen(&has, NULL, This->lpFormat, &wfx, NULL, 1242 0, 0, ACM_STREAMOPENF_NONREALTIME); 1243 acmStreamSize(has, This->ckData.cksize, &dwFactLength, 1244 ACM_STREAMSIZEF_SOURCE); 1245 dwFactLength /= wfx.nBlockAlign; 1246 acmStreamClose(has, 0); 1247 1248 /* create the fact chunk */ 1249 ck.ckid = ckidWAVEFACT; 1250 ck.cksize = sizeof(dwFactLength); 1251 1252 /* test for enough space before data chunk */ 1253 if (mmioSeek(This->hmmio, 0, SEEK_CUR) > This->ckData.dwDataOffset 1254 - ck.cksize - 4 * sizeof(DWORD)) 1255 return AVIERR_FILEWRITE; 1256 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 1257 return AVIERR_FILEWRITE; 1258 if (mmioWrite(This->hmmio, (HPSTR)&dwFactLength, ck.cksize) != ck.cksize) 1259 return AVIERR_FILEWRITE; 1260 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1261 return AVIERR_FILEWRITE; 1262 } else 1263 ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n"); 1264 } 1265 1266 /* if there was extra stuff, we need to fill it with JUNK */ 1267 if (mmioSeek(This->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) < This->ckData.dwDataOffset) { 1268 ck.ckid = ckidAVIPADDING; 1269 ck.cksize = 0; 1270 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 1271 return AVIERR_FILEWRITE; 1272 1273 if (mmioSeek(This->hmmio, This->ckData.dwDataOffset 1274 - 2 * sizeof(DWORD), SEEK_SET) == -1) 1275 return AVIERR_FILEWRITE; 1276 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1277 return AVIERR_FILEWRITE; 1278 } 1279 1280 /* create the data chunk */ 1281 ck.ckid = ckidWAVEDATA; 1282 ck.cksize = This->ckData.cksize; 1283 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) 1284 return AVIERR_FILEWRITE; 1285 if (mmioSeek(This->hmmio, This->ckData.cksize, SEEK_CUR) == -1) 1286 return AVIERR_FILEWRITE; 1287 if (mmioAscend(This->hmmio, &ck, 0) != S_OK) 1288 return AVIERR_FILEWRITE; 1289 1290 /* some optional extra chunks? */ 1291 if (This->extra.lp != NULL && This->extra.cb > 0) { 1292 /* chunk headers are already in structure */ 1293 if (mmioWrite(This->hmmio, This->extra.lp, This->extra.cb) != This->extra.cb) 1294 return AVIERR_FILEWRITE; 1295 } 1296 1297 /* close RIFF chunk */ 1298 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) 1299 return AVIERR_FILEWRITE; 1300 if (mmioFlush(This->hmmio, 0) != S_OK) 1301 return AVIERR_FILEWRITE; 1302 1303 return AVIERR_OK; 1304 } 1305