1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // TODO(henrike): reassess the error handling in this class. Currently failure 12 // is detected by asserts in many places. Also a refactoring of this class would 13 // be beneficial. 14 15 #include "webrtc/modules/media_file/source/avi_file.h" 16 17 #include <assert.h> 18 #include <string.h> 19 20 #ifdef _WIN32 21 #include <windows.h> 22 #endif 23 24 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 25 #include "webrtc/system_wrappers/interface/file_wrapper.h" 26 #include "webrtc/system_wrappers/interface/trace.h" 27 28 // http://msdn2.microsoft.com/en-us/library/ms779636.aspx 29 // A chunk has the following form: 30 // ckID ckSize ckData 31 // where ckID is a FOURCC that identifies the data contained in the 32 // chunk, ckData is a 4-byte value giving the size of the data in 33 // ckData, and ckData is zero or more bytes of data. The data is always 34 // padded to nearest WORD boundary. ckSize gives the size of the valid 35 // data in the chunk; it does not include the padding, the size of 36 // ckID, or the size of ckSize. 37 //http://msdn2.microsoft.com/en-us/library/ms779632.aspx 38 //NOTE: Workaround to make MPEG4 files play on WMP. MPEG files 39 // place the config parameters efter the BITMAPINFOHEADER and 40 // *NOT* in the 'strd'! 41 // http://msdn.microsoft.com/en-us/library/dd183375.aspx 42 // http://msdn.microsoft.com/en-us/library/dd183376.aspx 43 44 namespace webrtc { 45 namespace { 46 static const uint32_t kAvifHasindex = 0x00000010; 47 static const uint32_t kAvifMustuseindex = 0x00000020; 48 static const uint32_t kAvifIsinterleaved = 0x00000100; 49 static const uint32_t kAvifTrustcktype = 0x00000800; 50 static const uint32_t kAvifWascapturefile = 0x00010000; 51 52 template <class T> 53 T MinValue(T a, T b) 54 { 55 return a < b ? a : b; 56 } 57 } // namespace 58 59 AviFile::AVIMAINHEADER::AVIMAINHEADER() 60 : fcc( 0), 61 cb( 0), 62 dwMicroSecPerFrame( 0), 63 dwMaxBytesPerSec( 0), 64 dwPaddingGranularity( 0), 65 dwFlags( 0), 66 dwTotalFrames( 0), 67 dwInitialFrames( 0), 68 dwStreams( 0), 69 dwSuggestedBufferSize(0), 70 dwWidth( 0), 71 dwHeight( 0) 72 { 73 dwReserved[0] = 0; 74 dwReserved[1] = 0; 75 dwReserved[2] = 0; 76 dwReserved[3] = 0; 77 } 78 79 AVISTREAMHEADER::AVISTREAMHEADER() 80 : fcc( 0), 81 cb( 0), 82 fccType( 0), 83 fccHandler( 0), 84 dwFlags( 0), 85 wPriority( 0), 86 wLanguage( 0), 87 dwInitialFrames( 0), 88 dwScale( 0), 89 dwRate( 0), 90 dwStart( 0), 91 dwLength( 0), 92 dwSuggestedBufferSize(0), 93 dwQuality( 0), 94 dwSampleSize( 0) 95 { 96 rcFrame.left = 0; 97 rcFrame.top = 0; 98 rcFrame.right = 0; 99 rcFrame.bottom = 0; 100 } 101 102 BITMAPINFOHEADER::BITMAPINFOHEADER() 103 : biSize( 0), 104 biWidth( 0), 105 biHeight( 0), 106 biPlanes( 0), 107 biBitCount( 0), 108 biCompression( 0), 109 biSizeImage( 0), 110 biXPelsPerMeter(0), 111 biYPelsPerMeter(0), 112 biClrUsed( 0), 113 biClrImportant( 0) 114 { 115 } 116 117 WAVEFORMATEX::WAVEFORMATEX() 118 : wFormatTag( 0), 119 nChannels( 0), 120 nSamplesPerSec( 0), 121 nAvgBytesPerSec(0), 122 nBlockAlign( 0), 123 wBitsPerSample( 0), 124 cbSize( 0) 125 { 126 } 127 128 AviFile::AVIINDEXENTRY::AVIINDEXENTRY(uint32_t inckid, 129 uint32_t indwFlags, 130 uint32_t indwChunkOffset, 131 uint32_t indwChunkLength) 132 : ckid(inckid), 133 dwFlags(indwFlags), 134 dwChunkOffset(indwChunkOffset), 135 dwChunkLength(indwChunkLength) 136 { 137 } 138 139 AviFile::AviFile() 140 : _crit(CriticalSectionWrapper::CreateCriticalSection()), 141 _aviFile(NULL), 142 _aviHeader(), 143 _videoStreamHeader(), 144 _audioStreamHeader(), 145 _videoFormatHeader(), 146 _audioFormatHeader(), 147 _videoConfigParameters(), 148 _videoConfigLength(0), 149 _videoStreamName(), 150 _audioConfigParameters(), 151 _audioStreamName(), 152 _videoStream(), 153 _audioStream(), 154 _nrStreams(0), 155 _aviLength(0), 156 _dataLength(0), 157 _bytesRead(0), 158 _dataStartByte(0), 159 _framesRead(0), 160 _videoFrames(0), 161 _audioFrames(0), 162 _reading(false), 163 _openedAs(AVI_AUDIO), 164 _loop(false), 165 _writing(false), 166 _bytesWritten(0), 167 _riffSizeMark(0), 168 _moviSizeMark(0), 169 _totNumFramesMark(0), 170 _videoStreamLengthMark(0), 171 _audioStreamLengthMark(0), 172 _moviListOffset(0), 173 _writeAudioStream(false), 174 _writeVideoStream(false), 175 _aviMode(NotSet), 176 _videoCodecConfigParams(NULL), 177 _videoCodecConfigParamsLength(0), 178 _videoStreamDataChunkPrefix(0), 179 _audioStreamDataChunkPrefix(0), 180 _created(false) 181 { 182 ResetComplexMembers(); 183 } 184 185 AviFile::~AviFile() 186 { 187 Close(); 188 189 delete[] _videoCodecConfigParams; 190 delete _crit; 191 } 192 193 int32_t AviFile::Open(AVIStreamType streamType, const char* fileName, bool loop) 194 { 195 WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "OpenAVIFile(%s)", 196 fileName); 197 _crit->Enter(); 198 199 if (_aviMode != NotSet) 200 { 201 _crit->Leave(); 202 return -1; 203 } 204 205 _aviMode = Read; 206 207 if (!fileName) 208 { 209 _crit->Leave(); 210 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "\tfileName not valid!"); 211 return -1; 212 } 213 214 #ifdef _WIN32 215 // fopen does not support wide characters on Windows, ergo _wfopen. 216 wchar_t wideFileName[FileWrapper::kMaxFileNameSize]; 217 wideFileName[0] = 0; 218 MultiByteToWideChar(CP_UTF8,0,fileName, -1, // convert the whole string 219 wideFileName, FileWrapper::kMaxFileNameSize); 220 221 _aviFile = _wfopen(wideFileName, L"rb"); 222 #else 223 _aviFile = fopen(fileName, "rb"); 224 #endif 225 226 if (!_aviFile) 227 { 228 _crit->Leave(); 229 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Could not open file!"); 230 return -1; 231 } 232 233 // ReadRIFF verifies that the file is AVI and figures out the file length. 234 int32_t err = ReadRIFF(); 235 if (err) 236 { 237 if (_aviFile) 238 { 239 fclose(_aviFile); 240 _aviFile = NULL; 241 } 242 _crit->Leave(); 243 return -1; 244 } 245 246 err = ReadHeaders(); 247 if (err) 248 { 249 if (_aviFile) 250 { 251 fclose(_aviFile); 252 _aviFile = NULL; 253 } 254 _crit->Leave(); 255 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, 256 "Unsupported or corrupt AVI format"); 257 return -1; 258 } 259 260 _dataStartByte = _bytesRead; 261 _reading = true; 262 _openedAs = streamType; 263 _loop = loop; 264 _crit->Leave(); 265 return 0; 266 } 267 268 int32_t AviFile::Close() 269 { 270 _crit->Enter(); 271 switch (_aviMode) 272 { 273 case Read: 274 CloseRead(); 275 break; 276 case Write: 277 CloseWrite(); 278 break; 279 default: 280 break; 281 } 282 283 if (_videoCodecConfigParams) 284 { 285 delete [] _videoCodecConfigParams; 286 _videoCodecConfigParams = 0; 287 } 288 ResetMembers(); 289 _crit->Leave(); 290 return 0; 291 } 292 293 uint32_t AviFile::MakeFourCc(uint8_t ch0, uint8_t ch1, uint8_t ch2, uint8_t ch3) 294 { 295 return ((uint32_t)(uint8_t)(ch0) | 296 ((uint32_t)(uint8_t)(ch1) << 8) | 297 ((uint32_t)(uint8_t)(ch2) << 16) | 298 ((uint32_t)(uint8_t)(ch3) << 24 )); 299 } 300 301 int32_t AviFile::GetVideoStreamInfo(AVISTREAMHEADER& videoStreamHeader, 302 BITMAPINFOHEADER& bitmapInfo, 303 char* codecConfigParameters, 304 int32_t& configLength) 305 { 306 _crit->Enter(); 307 if (!_reading && !_created) 308 { 309 _crit->Leave(); 310 return -1; 311 } 312 313 memcpy(&videoStreamHeader, &_videoStreamHeader, sizeof(_videoStreamHeader)); 314 memcpy(&bitmapInfo, &_videoFormatHeader, sizeof(_videoFormatHeader)); 315 316 if (configLength <= _videoConfigLength) 317 { 318 memcpy(codecConfigParameters, _videoConfigParameters, 319 _videoConfigLength); 320 configLength = _videoConfigLength; 321 } 322 else 323 { 324 configLength = 0; 325 } 326 _crit->Leave(); 327 return 0; 328 } 329 330 int32_t AviFile::GetDuration(int32_t& durationMs) 331 { 332 _crit->Enter(); 333 if (_videoStreamHeader.dwRate==0 || _videoStreamHeader.dwScale==0) 334 { 335 _crit->Leave(); 336 return -1; 337 } 338 339 durationMs = _videoStreamHeader.dwLength * 1000 / 340 (_videoStreamHeader.dwRate/_videoStreamHeader.dwScale); 341 _crit->Leave(); 342 return 0; 343 } 344 345 int32_t AviFile::GetAudioStreamInfo(WAVEFORMATEX& waveHeader) 346 { 347 _crit->Enter(); 348 if (_aviMode != Read) 349 { 350 _crit->Leave(); 351 return -1; 352 } 353 if (!_reading && !_created) 354 { 355 _crit->Leave(); 356 return -1; 357 } 358 memcpy(&waveHeader, &_audioFormatHeader, sizeof(_audioFormatHeader)); 359 _crit->Leave(); 360 return 0; 361 } 362 363 int32_t AviFile::WriteAudio(const uint8_t* data, int32_t length) 364 { 365 _crit->Enter(); 366 size_t newBytesWritten = _bytesWritten; 367 368 if (_aviMode != Write) 369 { 370 _crit->Leave(); 371 return -1; 372 } 373 if (!_created) 374 { 375 _crit->Leave(); 376 return -1; 377 } 378 if (!_writeAudioStream) 379 { 380 _crit->Leave(); 381 return -1; 382 } 383 384 // Start of chunk. 385 const uint32_t chunkOffset = ftell(_aviFile) - _moviListOffset; 386 _bytesWritten += PutLE32(_audioStreamDataChunkPrefix); 387 // Size is unknown at this point. Update later. 388 _bytesWritten += PutLE32(0); 389 const size_t chunkSizeMark = _bytesWritten; 390 391 _bytesWritten += PutBuffer(data, length); 392 393 const long chunkSize = PutLE32LengthFromCurrent( 394 static_cast<long>(chunkSizeMark)); 395 396 // Make sure that the chunk is aligned on 2 bytes (= 1 sample). 397 if (chunkSize % 2) 398 { 399 _bytesWritten += PutByte(0); 400 } 401 // End of chunk 402 403 // Save chunk information for use when closing file. 404 AddChunkToIndexList(_audioStreamDataChunkPrefix, 0, // No flags. 405 chunkOffset, chunkSize); 406 407 ++_audioFrames; 408 newBytesWritten = _bytesWritten - newBytesWritten; 409 _crit->Leave(); 410 return static_cast<int32_t>(newBytesWritten); 411 } 412 413 int32_t AviFile::WriteVideo(const uint8_t* data, int32_t length) 414 { 415 _crit->Enter(); 416 size_t newBytesWritten = _bytesWritten; 417 if (_aviMode != Write) 418 { 419 _crit->Leave(); 420 return -1; 421 } 422 if (!_created) 423 { 424 _crit->Leave(); 425 return -1; 426 } 427 if (!_writeVideoStream) 428 { 429 _crit->Leave(); 430 return -1; 431 } 432 433 // Start of chunk. 434 const uint32_t chunkOffset = ftell(_aviFile) - _moviListOffset; 435 _bytesWritten += PutLE32(_videoStreamDataChunkPrefix); 436 // Size is unknown at this point. Update later. 437 _bytesWritten += PutLE32(0); 438 const size_t chunkSizeMark = _bytesWritten; 439 440 _bytesWritten += PutBuffer(data, length); 441 442 const long chunkSize = PutLE32LengthFromCurrent( 443 static_cast<long>(chunkSizeMark)); 444 445 // Make sure that the chunk is aligned on 2 bytes (= 1 sample). 446 if (chunkSize % 2) 447 { 448 //Pad one byte, to WORD align. 449 _bytesWritten += PutByte(0); 450 } 451 //End chunk! 452 AddChunkToIndexList(_videoStreamDataChunkPrefix, 0, // No flags. 453 chunkOffset, static_cast<uint32_t>(chunkSize)); 454 455 ++_videoFrames; 456 newBytesWritten = _bytesWritten - newBytesWritten; 457 _crit->Leave(); 458 return static_cast<int32_t>(newBytesWritten); 459 } 460 461 int32_t AviFile::PrepareDataChunkHeaders() 462 { 463 // 00 video stream, 01 audio stream. 464 // db uncompresses video, dc compressed video, wb WAV audio 465 if (_writeVideoStream) 466 { 467 if (strncmp((const char*) &_videoStreamHeader.fccHandler, "I420", 4) == 468 0) 469 { 470 _videoStreamDataChunkPrefix = MakeFourCc('0', '0', 'd', 'b'); 471 } 472 else 473 { 474 _videoStreamDataChunkPrefix = MakeFourCc('0', '0', 'd', 'c'); 475 } 476 _audioStreamDataChunkPrefix = MakeFourCc('0', '1', 'w', 'b'); 477 } 478 else 479 { 480 _audioStreamDataChunkPrefix = MakeFourCc('0', '0', 'w', 'b'); 481 } 482 return 0; 483 } 484 485 int32_t AviFile::ReadMoviSubChunk(uint8_t* data, int32_t& length, uint32_t tag1, 486 uint32_t tag2) 487 { 488 if (!_reading) 489 { 490 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, 491 "AviFile::ReadMoviSubChunk(): File not open!"); 492 length = 0; 493 return -1; 494 } 495 496 uint32_t size; 497 bool isEOFReached = false; 498 // Try to read one data chunk header 499 while (true) 500 { 501 // TODO (hellner): what happens if an empty AVI file is opened with 502 // _loop set to true? Seems like this while-loop would never exit! 503 504 // tag = db uncompresses video, dc compressed video or wb WAV audio. 505 uint32_t tag; 506 _bytesRead += GetLE32(tag); 507 _bytesRead += GetLE32(size); 508 509 const int32_t eof = feof(_aviFile); 510 if (!eof) 511 { 512 if (tag == tag1) 513 { 514 // Supported tag found. 515 break; 516 } 517 else if ((tag == tag2) && (tag2 != 0)) 518 { 519 // Supported tag found. 520 break; 521 } 522 523 // Jump to next chunk. The size is in bytes but chunks are aligned 524 // on 2 byte boundaries. 525 const uint32_t seekSize = (size % 2) ? size + 1 : size; 526 const int32_t err = fseek(_aviFile, seekSize, SEEK_CUR); 527 528 if (err) 529 { 530 isEOFReached = true; 531 } 532 } 533 else 534 { 535 isEOFReached = true; 536 } 537 538 if (isEOFReached) 539 { 540 clearerr(_aviFile); 541 542 if (_loop) 543 { 544 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, 545 "AviFile::ReadMoviSubChunk(): Reached end of AVI\ 546 data file, starting from the beginning."); 547 548 fseek(_aviFile, static_cast<long>(_dataStartByte), SEEK_SET); 549 550 _bytesRead = _dataStartByte; 551 _framesRead = 0; 552 isEOFReached = false; 553 } 554 else 555 { 556 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, 557 "AviFile::ReadMoviSubChunk(): Reached end of AVI\ 558 file!"); 559 length = 0; 560 return -1; 561 } 562 } 563 _bytesRead += size; 564 } 565 566 if (static_cast<int32_t>(size) > length) 567 { 568 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, 569 "AviFile::ReadMoviSubChunk(): AVI read buffer too small!"); 570 571 // Jump to next chunk. The size is in bytes but chunks are aligned 572 // on 2 byte boundaries. 573 const uint32_t seekSize = (size % 2) ? size + 1 : size; 574 fseek(_aviFile, seekSize, SEEK_CUR); 575 _bytesRead += seekSize; 576 length = 0; 577 return -1; 578 } 579 _bytesRead += GetBuffer(data, size); 580 581 // The size is in bytes but chunks are aligned on 2 byte boundaries. 582 if (size % 2) 583 { 584 uint8_t dummy_byte; 585 _bytesRead += GetByte(dummy_byte); 586 } 587 length = size; 588 ++_framesRead; 589 return 0; 590 } 591 592 int32_t AviFile::ReadAudio(uint8_t* data, int32_t& length) 593 { 594 _crit->Enter(); 595 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "AviFile::ReadAudio()"); 596 597 if (_aviMode != Read) 598 { 599 _crit->Leave(); 600 return -1; 601 } 602 if (_openedAs != AVI_AUDIO) 603 { 604 length = 0; 605 _crit->Leave(); 606 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "File not open as audio!"); 607 return -1; 608 } 609 610 const int32_t ret = ReadMoviSubChunk( 611 data, 612 length, 613 StreamAndTwoCharCodeToTag(_audioStream.streamNumber, "wb")); 614 615 _crit->Leave(); 616 return ret; 617 } 618 619 int32_t AviFile::ReadVideo(uint8_t* data, int32_t& length) 620 { 621 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "AviFile::ReadVideo()"); 622 623 _crit->Enter(); 624 if (_aviMode != Read) 625 { 626 //Has to be Read! 627 _crit->Leave(); 628 return -1; 629 } 630 if (_openedAs != AVI_VIDEO) 631 { 632 length = 0; 633 _crit->Leave(); 634 WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "File not open as video!"); 635 return -1; 636 } 637 638 const int32_t ret = ReadMoviSubChunk( 639 data, 640 length, 641 StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "dc"), 642 StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "db")); 643 _crit->Leave(); 644 return ret; 645 } 646 647 int32_t AviFile::Create(const char* fileName) 648 { 649 _crit->Enter(); 650 if (_aviMode != Write) 651 { 652 _crit->Leave(); 653 return -1; 654 } 655 656 if (!_writeVideoStream && !_writeAudioStream) 657 { 658 _crit->Leave(); 659 return -1; 660 } 661 if (_created) 662 { 663 _crit->Leave(); 664 return -1; 665 } 666 667 #ifdef _WIN32 668 // fopen does not support wide characters on Windows, ergo _wfopen. 669 wchar_t wideFileName[FileWrapper::kMaxFileNameSize]; 670 wideFileName[0] = 0; 671 672 MultiByteToWideChar(CP_UTF8,0,fileName, -1, // convert the whole string 673 wideFileName, FileWrapper::kMaxFileNameSize); 674 675 _aviFile = _wfopen(wideFileName, L"w+b"); 676 if (!_aviFile) 677 { 678 _crit->Leave(); 679 return -1; 680 } 681 #else 682 _aviFile = fopen(fileName, "w+b"); 683 if (!_aviFile) 684 { 685 _crit->Leave(); 686 return -1; 687 } 688 #endif 689 690 WriteRIFF(); 691 WriteHeaders(); 692 693 _created = true; 694 695 PrepareDataChunkHeaders(); 696 ClearIndexList(); 697 WriteMoviStart(); 698 _aviMode = Write; 699 _crit->Leave(); 700 return 0; 701 } 702 703 int32_t AviFile::CreateVideoStream( 704 const AVISTREAMHEADER& videoStreamHeader, 705 const BITMAPINFOHEADER& bitMapInfoHeader, 706 const uint8_t* codecConfigParams, 707 int32_t codecConfigParamsLength) 708 { 709 _crit->Enter(); 710 if (_aviMode == Read) 711 { 712 _crit->Leave(); 713 return -1; 714 } 715 716 if (_created) 717 { 718 _crit->Leave(); 719 return -1; 720 } 721 722 _aviMode = Write; 723 _writeVideoStream = true; 724 725 _videoStreamHeader = videoStreamHeader; 726 _videoFormatHeader = bitMapInfoHeader; 727 728 if (codecConfigParams && codecConfigParamsLength > 0) 729 { 730 if (_videoCodecConfigParams) 731 { 732 delete [] _videoCodecConfigParams; 733 _videoCodecConfigParams = 0; 734 } 735 736 _videoCodecConfigParams = new uint8_t[codecConfigParamsLength]; 737 _videoCodecConfigParamsLength = codecConfigParamsLength; 738 739 memcpy(_videoCodecConfigParams, codecConfigParams, 740 _videoCodecConfigParamsLength); 741 } 742 _crit->Leave(); 743 return 0; 744 } 745 746 int32_t AviFile::CreateAudioStream( 747 const AVISTREAMHEADER& audioStreamHeader, 748 const WAVEFORMATEX& waveFormatHeader) 749 { 750 _crit->Enter(); 751 752 if (_aviMode == Read) 753 { 754 _crit->Leave(); 755 return -1; 756 } 757 758 if (_created) 759 { 760 _crit->Leave(); 761 return -1; 762 } 763 764 _aviMode = Write; 765 _writeAudioStream = true; 766 _audioStreamHeader = audioStreamHeader; 767 _audioFormatHeader = waveFormatHeader; 768 _crit->Leave(); 769 return 0; 770 } 771 772 int32_t AviFile::WriteRIFF() 773 { 774 const uint32_t riffTag = MakeFourCc('R', 'I', 'F', 'F'); 775 _bytesWritten += PutLE32(riffTag); 776 777 // Size is unknown at this point. Update later. 778 _bytesWritten += PutLE32(0); 779 _riffSizeMark = _bytesWritten; 780 781 const uint32_t aviTag = MakeFourCc('A', 'V', 'I', ' '); 782 _bytesWritten += PutLE32(aviTag); 783 784 return 0; 785 } 786 787 788 int32_t AviFile::WriteHeaders() 789 { 790 // Main AVI header list. 791 const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T'); 792 _bytesWritten += PutLE32(listTag); 793 794 // Size is unknown at this point. Update later. 795 _bytesWritten += PutLE32(0); 796 const size_t listhdrlSizeMark = _bytesWritten; 797 798 const uint32_t hdrlTag = MakeFourCc('h', 'd', 'r', 'l'); 799 _bytesWritten += PutLE32(hdrlTag); 800 801 WriteAVIMainHeader(); 802 WriteAVIStreamHeaders(); 803 804 const long hdrlLen = PutLE32LengthFromCurrent( 805 static_cast<long>(listhdrlSizeMark)); 806 807 // Junk chunk to align on 2048 boundry (CD-ROM sector boundary). 808 const uint32_t junkTag = MakeFourCc('J', 'U', 'N', 'K'); 809 _bytesWritten += PutLE32(junkTag); 810 // Size is unknown at this point. Update later. 811 _bytesWritten += PutLE32(0); 812 const size_t junkSizeMark = _bytesWritten; 813 814 const uint32_t junkBufferSize = 815 0x800 // 2048 byte alignment 816 - 12 // RIFF SIZE 'AVI ' 817 - 8 // LIST SIZE 818 - hdrlLen // 819 - 8 // JUNK SIZE 820 - 12; // LIST SIZE 'MOVI' 821 822 // TODO (hellner): why not just fseek here? 823 uint8_t* junkBuffer = new uint8_t[junkBufferSize]; 824 memset(junkBuffer, 0, junkBufferSize); 825 _bytesWritten += PutBuffer(junkBuffer, junkBufferSize); 826 delete [] junkBuffer; 827 828 PutLE32LengthFromCurrent(static_cast<long>(junkSizeMark)); 829 // End of JUNK chunk. 830 // End of main AVI header list. 831 return 0; 832 } 833 834 int32_t AviFile::WriteAVIMainHeader() 835 { 836 const uint32_t avihTag = MakeFourCc('a', 'v', 'i', 'h'); 837 _bytesWritten += PutLE32(avihTag); 838 _bytesWritten += PutLE32(14 * sizeof(uint32_t)); 839 840 const uint32_t scale = _videoStreamHeader.dwScale ? 841 _videoStreamHeader.dwScale : 1; 842 const uint32_t microSecPerFrame = 1000000 / 843 (_videoStreamHeader.dwRate / scale); 844 _bytesWritten += PutLE32(microSecPerFrame); 845 _bytesWritten += PutLE32(0); 846 _bytesWritten += PutLE32(0); 847 848 uint32_t numStreams = 0; 849 if (_writeVideoStream) 850 { 851 ++numStreams; 852 } 853 if (_writeAudioStream) 854 { 855 ++numStreams; 856 } 857 858 if (numStreams == 1) 859 { 860 _bytesWritten += PutLE32( 861 kAvifTrustcktype 862 | kAvifHasindex 863 | kAvifWascapturefile); 864 } 865 else 866 { 867 _bytesWritten += PutLE32( 868 kAvifTrustcktype 869 | kAvifHasindex 870 | kAvifWascapturefile 871 | kAvifIsinterleaved); 872 } 873 874 _totNumFramesMark = _bytesWritten; 875 _bytesWritten += PutLE32(0); 876 _bytesWritten += PutLE32(0); 877 _bytesWritten += PutLE32(numStreams); 878 879 if (_writeVideoStream) 880 { 881 _bytesWritten += PutLE32( 882 _videoStreamHeader.dwSuggestedBufferSize); 883 _bytesWritten += PutLE32( 884 _videoStreamHeader.rcFrame.right-_videoStreamHeader.rcFrame.left); 885 _bytesWritten += PutLE32( 886 _videoStreamHeader.rcFrame.bottom-_videoStreamHeader.rcFrame.top); 887 } else { 888 _bytesWritten += PutLE32(0); 889 _bytesWritten += PutLE32(0); 890 _bytesWritten += PutLE32(0); 891 } 892 _bytesWritten += PutLE32(0); 893 _bytesWritten += PutLE32(0); 894 _bytesWritten += PutLE32(0); 895 _bytesWritten += PutLE32(0); 896 return 0; 897 } 898 899 int32_t AviFile::WriteAVIStreamHeaders() 900 { 901 if (_writeVideoStream) 902 { 903 WriteAVIVideoStreamHeaders(); 904 } 905 if (_writeAudioStream) 906 { 907 WriteAVIAudioStreamHeaders(); 908 } 909 return 0; 910 } 911 912 int32_t AviFile::WriteAVIVideoStreamHeaders() 913 { 914 const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T'); 915 _bytesWritten += PutLE32(listTag); 916 917 // Size is unknown at this point. Update later. 918 _bytesWritten += PutLE32(0); 919 const size_t liststrlSizeMark = _bytesWritten; 920 921 const uint32_t hdrlTag = MakeFourCc('s', 't', 'r', 'l'); 922 _bytesWritten += PutLE32(hdrlTag); 923 924 WriteAVIVideoStreamHeaderChunks(); 925 926 PutLE32LengthFromCurrent(static_cast<long>(liststrlSizeMark)); 927 928 return 0; 929 } 930 931 int32_t AviFile::WriteAVIVideoStreamHeaderChunks() 932 { 933 // Start of strh 934 const uint32_t strhTag = MakeFourCc('s', 't', 'r', 'h'); 935 _bytesWritten += PutLE32(strhTag); 936 937 // Size is unknown at this point. Update later. 938 _bytesWritten += PutLE32(0); 939 const size_t strhSizeMark = _bytesWritten; 940 941 _bytesWritten += PutLE32(_videoStreamHeader.fccType); 942 _bytesWritten += PutLE32(_videoStreamHeader.fccHandler); 943 _bytesWritten += PutLE32(_videoStreamHeader.dwFlags); 944 _bytesWritten += PutLE16(_videoStreamHeader.wPriority); 945 _bytesWritten += PutLE16(_videoStreamHeader.wLanguage); 946 _bytesWritten += PutLE32(_videoStreamHeader.dwInitialFrames); 947 _bytesWritten += PutLE32(_videoStreamHeader.dwScale); 948 _bytesWritten += PutLE32(_videoStreamHeader.dwRate); 949 _bytesWritten += PutLE32(_videoStreamHeader.dwStart); 950 951 _videoStreamLengthMark = _bytesWritten; 952 _bytesWritten += PutLE32(_videoStreamHeader.dwLength); 953 954 _bytesWritten += PutLE32(_videoStreamHeader.dwSuggestedBufferSize); 955 _bytesWritten += PutLE32(_videoStreamHeader.dwQuality); 956 _bytesWritten += PutLE32(_videoStreamHeader.dwSampleSize); 957 _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.left); 958 _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.top); 959 _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.right); 960 _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.bottom); 961 962 PutLE32LengthFromCurrent(static_cast<long>(strhSizeMark)); 963 // End of strh 964 965 // Start of strf 966 const uint32_t strfTag = MakeFourCc('s', 't', 'r', 'f'); 967 _bytesWritten += PutLE32(strfTag); 968 969 // Size is unknown at this point. Update later. 970 _bytesWritten += PutLE32(0); 971 const size_t strfSizeMark = _bytesWritten; 972 973 _bytesWritten += PutLE32(_videoFormatHeader.biSize); 974 _bytesWritten += PutLE32(_videoFormatHeader.biWidth); 975 _bytesWritten += PutLE32(_videoFormatHeader.biHeight); 976 _bytesWritten += PutLE16(_videoFormatHeader.biPlanes); 977 _bytesWritten += PutLE16(_videoFormatHeader.biBitCount); 978 _bytesWritten += PutLE32(_videoFormatHeader.biCompression); 979 _bytesWritten += PutLE32(_videoFormatHeader.biSizeImage); 980 _bytesWritten += PutLE32(_videoFormatHeader.biXPelsPerMeter); 981 _bytesWritten += PutLE32(_videoFormatHeader.biYPelsPerMeter); 982 _bytesWritten += PutLE32(_videoFormatHeader.biClrUsed); 983 _bytesWritten += PutLE32(_videoFormatHeader.biClrImportant); 984 985 const bool isMpegFile = _videoStreamHeader.fccHandler == 986 AviFile::MakeFourCc('M','4','S','2'); 987 if (isMpegFile) 988 { 989 if (_videoCodecConfigParams && _videoCodecConfigParamsLength > 0) 990 { 991 _bytesWritten += PutBuffer(_videoCodecConfigParams, 992 _videoCodecConfigParamsLength); 993 } 994 } 995 996 PutLE32LengthFromCurrent(static_cast<long>(strfSizeMark)); 997 // End of strf 998 999 if ( _videoCodecConfigParams 1000 && (_videoCodecConfigParamsLength > 0) 1001 && !isMpegFile) 1002 { 1003 // Write strd, unless it's an MPEG file 1004 const uint32_t strdTag = MakeFourCc('s', 't', 'r', 'd'); 1005 _bytesWritten += PutLE32(strdTag); 1006 1007 // Size is unknown at this point. Update later. 1008 _bytesWritten += PutLE32(0); 1009 const size_t strdSizeMark = _bytesWritten; 1010 1011 _bytesWritten += PutBuffer(_videoCodecConfigParams, 1012 _videoCodecConfigParamsLength); 1013 1014 PutLE32LengthFromCurrent(static_cast<long>(strdSizeMark)); 1015 // End of strd 1016 } 1017 1018 // Start of strn 1019 const uint32_t strnTag = MakeFourCc('s', 't', 'r', 'n'); 1020 _bytesWritten += PutLE32(strnTag); 1021 1022 // Size is unknown at this point. Update later. 1023 _bytesWritten += PutLE32(0); 1024 const size_t strnSizeMark = _bytesWritten; 1025 1026 _bytesWritten += PutBufferZ("WebRtc.avi "); 1027 1028 PutLE32LengthFromCurrent(static_cast<long>(strnSizeMark)); 1029 // End of strd 1030 1031 return 0; 1032 } 1033 1034 int32_t AviFile::WriteAVIAudioStreamHeaders() 1035 { 1036 // Start of LIST 1037 uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T'); 1038 _bytesWritten += PutLE32(listTag); 1039 1040 // Size is unknown at this point. Update later. 1041 _bytesWritten += PutLE32(0); 1042 const size_t liststrlSizeMark = _bytesWritten; 1043 1044 uint32_t hdrlTag = MakeFourCc('s', 't', 'r', 'l'); 1045 _bytesWritten += PutLE32(hdrlTag); 1046 1047 WriteAVIAudioStreamHeaderChunks(); 1048 1049 PutLE32LengthFromCurrent(static_cast<long>(liststrlSizeMark)); 1050 //End of LIST 1051 return 0; 1052 } 1053 1054 int32_t AviFile::WriteAVIAudioStreamHeaderChunks() 1055 { 1056 // Start of strh 1057 const uint32_t strhTag = MakeFourCc('s', 't', 'r', 'h'); 1058 _bytesWritten += PutLE32(strhTag); 1059 1060 // Size is unknown at this point. Update later. 1061 _bytesWritten += PutLE32(0); 1062 const size_t strhSizeMark = _bytesWritten; 1063 1064 _bytesWritten += PutLE32(_audioStreamHeader.fccType); 1065 _bytesWritten += PutLE32(_audioStreamHeader.fccHandler); 1066 _bytesWritten += PutLE32(_audioStreamHeader.dwFlags); 1067 _bytesWritten += PutLE16(_audioStreamHeader.wPriority); 1068 _bytesWritten += PutLE16(_audioStreamHeader.wLanguage); 1069 _bytesWritten += PutLE32(_audioStreamHeader.dwInitialFrames); 1070 _bytesWritten += PutLE32(_audioStreamHeader.dwScale); 1071 _bytesWritten += PutLE32(_audioStreamHeader.dwRate); 1072 _bytesWritten += PutLE32(_audioStreamHeader.dwStart); 1073 1074 _audioStreamLengthMark = _bytesWritten; 1075 _bytesWritten += PutLE32(_audioStreamHeader.dwLength); 1076 1077 _bytesWritten += PutLE32(_audioStreamHeader.dwSuggestedBufferSize); 1078 _bytesWritten += PutLE32(_audioStreamHeader.dwQuality); 1079 _bytesWritten += PutLE32(_audioStreamHeader.dwSampleSize); 1080 _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.left); 1081 _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.top); 1082 _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.right); 1083 _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.bottom); 1084 1085 PutLE32LengthFromCurrent(static_cast<long>(strhSizeMark)); 1086 // End of strh 1087 1088 // Start of strf 1089 const uint32_t strfTag = MakeFourCc('s', 't', 'r', 'f'); 1090 _bytesWritten += PutLE32(strfTag); 1091 1092 // Size is unknown at this point. Update later. 1093 _bytesWritten += PutLE32(0); 1094 const size_t strfSizeMark = _bytesWritten; 1095 1096 _bytesWritten += PutLE16(_audioFormatHeader.wFormatTag); 1097 _bytesWritten += PutLE16(_audioFormatHeader.nChannels); 1098 _bytesWritten += PutLE32(_audioFormatHeader.nSamplesPerSec); 1099 _bytesWritten += PutLE32(_audioFormatHeader.nAvgBytesPerSec); 1100 _bytesWritten += PutLE16(_audioFormatHeader.nBlockAlign); 1101 _bytesWritten += PutLE16(_audioFormatHeader.wBitsPerSample); 1102 _bytesWritten += PutLE16(_audioFormatHeader.cbSize); 1103 1104 PutLE32LengthFromCurrent(static_cast<long>(strfSizeMark)); 1105 // End end of strf. 1106 1107 // Audio doesn't have strd. 1108 1109 // Start of strn 1110 const uint32_t strnTag = MakeFourCc('s', 't', 'r', 'n'); 1111 _bytesWritten += PutLE32(strnTag); 1112 1113 // Size is unknown at this point. Update later. 1114 _bytesWritten += PutLE32(0); 1115 const size_t strnSizeMark = _bytesWritten; 1116 1117 _bytesWritten += PutBufferZ("WebRtc.avi "); 1118 1119 PutLE32LengthFromCurrent(static_cast<long>(strnSizeMark)); 1120 // End of strd. 1121 1122 return 0; 1123 } 1124 1125 int32_t AviFile::WriteMoviStart() 1126 { 1127 // Create template movi list. Fill out size when known (i.e. when closing 1128 // file). 1129 const uint32_t listTag = MakeFourCc('L', 'I', 'S', 'T'); 1130 _bytesWritten += PutLE32(listTag); 1131 1132 _bytesWritten += PutLE32(0); //Size! Change later! 1133 _moviSizeMark = _bytesWritten; 1134 _moviListOffset = ftell(_aviFile); 1135 1136 const uint32_t moviTag = MakeFourCc('m', 'o', 'v', 'i'); 1137 _bytesWritten += PutLE32(moviTag); 1138 1139 return 0; 1140 } 1141 1142 size_t AviFile::PutByte(uint8_t byte) 1143 { 1144 return fwrite(&byte, sizeof(uint8_t), sizeof(uint8_t), 1145 _aviFile); 1146 } 1147 1148 size_t AviFile::PutLE16(uint16_t word) 1149 { 1150 return fwrite(&word, sizeof(uint8_t), sizeof(uint16_t), 1151 _aviFile); 1152 } 1153 1154 size_t AviFile::PutLE32(uint32_t word) 1155 { 1156 return fwrite(&word, sizeof(uint8_t), sizeof(uint32_t), 1157 _aviFile); 1158 } 1159 1160 size_t AviFile::PutBuffer(const uint8_t* str, size_t size) 1161 { 1162 return fwrite(str, sizeof(uint8_t), size, 1163 _aviFile); 1164 } 1165 1166 size_t AviFile::PutBufferZ(const char* str) 1167 { 1168 // Include NULL charachter, hence the + 1 1169 return PutBuffer(reinterpret_cast<const uint8_t*>(str), 1170 strlen(str) + 1); 1171 } 1172 1173 long AviFile::PutLE32LengthFromCurrent(long startPos) 1174 { 1175 const long endPos = ftell(_aviFile); 1176 if (endPos < 0) { 1177 return 0; 1178 } 1179 bool success = (0 == fseek(_aviFile, startPos - 4, SEEK_SET)); 1180 if (!success) { 1181 assert(false); 1182 return 0; 1183 } 1184 const long len = endPos - startPos; 1185 if (endPos > startPos) { 1186 PutLE32(len); 1187 } 1188 else { 1189 assert(false); 1190 } 1191 success = (0 == fseek(_aviFile, endPos, SEEK_SET)); 1192 assert(success); 1193 return len; 1194 } 1195 1196 void AviFile::PutLE32AtPos(long pos, uint32_t word) 1197 { 1198 const long currPos = ftell(_aviFile); 1199 if (currPos < 0) { 1200 assert(false); 1201 return; 1202 } 1203 bool success = (0 == fseek(_aviFile, pos, SEEK_SET)); 1204 if (!success) { 1205 assert(false); 1206 return; 1207 } 1208 PutLE32(word); 1209 success = (0 == fseek(_aviFile, currPos, SEEK_SET)); 1210 assert(success); 1211 } 1212 1213 void AviFile::CloseRead() 1214 { 1215 if (_aviFile) 1216 { 1217 fclose(_aviFile); 1218 _aviFile = NULL; 1219 } 1220 } 1221 1222 void AviFile::CloseWrite() 1223 { 1224 if (_created) 1225 { 1226 // Update everything that isn't known until the file is closed. The 1227 // marks indicate where in the headers this update should be. 1228 PutLE32LengthFromCurrent(static_cast<long>(_moviSizeMark)); 1229 1230 PutLE32AtPos(static_cast<long>(_totNumFramesMark), _videoFrames); 1231 1232 if (_writeVideoStream) 1233 { 1234 PutLE32AtPos(static_cast<long>(_videoStreamLengthMark), 1235 _videoFrames); 1236 } 1237 1238 if (_writeAudioStream) 1239 { 1240 PutLE32AtPos(static_cast<long>(_audioStreamLengthMark), 1241 _audioFrames); 1242 } 1243 1244 WriteIndex(); 1245 PutLE32LengthFromCurrent(static_cast<long>(_riffSizeMark)); 1246 ClearIndexList(); 1247 1248 if (_aviFile) 1249 { 1250 fclose(_aviFile); 1251 _aviFile = NULL; 1252 } 1253 } 1254 } 1255 1256 void AviFile::ResetMembers() 1257 { 1258 ResetComplexMembers(); 1259 1260 _aviFile = NULL; 1261 1262 _nrStreams = 0; 1263 _aviLength = 0; 1264 _dataLength = 0; 1265 _bytesRead = 0; 1266 _dataStartByte = 0; 1267 _framesRead = 0; 1268 _videoFrames = 0; 1269 _audioFrames = 0; 1270 1271 _reading = false; 1272 _openedAs = AVI_AUDIO; 1273 _loop = false; 1274 _writing = false; 1275 1276 _bytesWritten = 0; 1277 1278 _riffSizeMark = 0; 1279 _moviSizeMark = 0; 1280 _totNumFramesMark = 0; 1281 _videoStreamLengthMark = 0; 1282 _audioStreamLengthMark = 0; 1283 1284 _writeAudioStream = false; 1285 _writeVideoStream = false; 1286 1287 _aviMode = NotSet; 1288 _videoCodecConfigParams = 0; 1289 _videoCodecConfigParamsLength = 0; 1290 1291 _videoStreamDataChunkPrefix = 0; 1292 _audioStreamDataChunkPrefix = 0; 1293 1294 _created = false; 1295 1296 _moviListOffset = 0; 1297 1298 _videoConfigLength = 0; 1299 } 1300 1301 void AviFile::ResetComplexMembers() 1302 { 1303 memset(&_aviHeader, 0, sizeof(AVIMAINHEADER)); 1304 memset(&_videoStreamHeader, 0, sizeof(AVISTREAMHEADER)); 1305 memset(&_audioStreamHeader, 0, sizeof(AVISTREAMHEADER)); 1306 memset(&_videoFormatHeader, 0, sizeof(BITMAPINFOHEADER)); 1307 memset(&_audioFormatHeader, 0, sizeof(WAVEFORMATEX)); 1308 memset(_videoConfigParameters, 0, CODEC_CONFIG_LENGTH); 1309 memset(_videoStreamName, 0, STREAM_NAME_LENGTH); 1310 memset(_audioStreamName, 0, STREAM_NAME_LENGTH); 1311 memset(&_videoStream, 0, sizeof(AVIStream)); 1312 memset(&_audioStream, 0, sizeof(AVIStream)); 1313 } 1314 1315 size_t AviFile::GetByte(uint8_t& word) 1316 { 1317 return fread(&word, sizeof(uint8_t), sizeof(uint8_t), _aviFile); 1318 } 1319 1320 size_t AviFile::GetLE16(uint16_t& word) 1321 { 1322 return fread(&word, sizeof(uint8_t), sizeof(uint16_t), 1323 _aviFile); 1324 } 1325 1326 size_t AviFile::GetLE32(uint32_t& word) 1327 { 1328 return fread(&word, sizeof(uint8_t), sizeof(uint32_t), 1329 _aviFile); 1330 } 1331 1332 size_t AviFile::GetBuffer(uint8_t* str, size_t size) 1333 { 1334 return fread(str, sizeof(uint8_t), size, _aviFile); 1335 } 1336 1337 int32_t AviFile::ReadRIFF() 1338 { 1339 uint32_t tag; 1340 _bytesRead = GetLE32(tag); 1341 if (tag != MakeFourCc('R', 'I', 'F', 'F')) 1342 { 1343 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not a RIFF file!"); 1344 return -1; 1345 } 1346 1347 uint32_t size; 1348 _bytesRead += GetLE32(size); 1349 _aviLength = size; 1350 1351 _bytesRead += GetLE32(tag); 1352 if (tag != MakeFourCc('A', 'V', 'I', ' ')) 1353 { 1354 WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not an AVI file!"); 1355 return -1; 1356 } 1357 1358 return 0; 1359 } 1360 1361 int32_t AviFile::ReadHeaders() 1362 { 1363 uint32_t tag; 1364 _bytesRead += GetLE32(tag); 1365 uint32_t size; 1366 _bytesRead += GetLE32(size); 1367 1368 if (tag != MakeFourCc('L', 'I', 'S', 'T')) 1369 { 1370 return -1; 1371 } 1372 1373 uint32_t listTag; 1374 _bytesRead += GetLE32(listTag); 1375 if (listTag != MakeFourCc('h', 'd', 'r', 'l')) 1376 { 1377 return -1; 1378 } 1379 1380 int32_t err = ReadAVIMainHeader(); 1381 if (err) 1382 { 1383 return -1; 1384 } 1385 1386 return 0; 1387 } 1388 1389 int32_t AviFile::ReadAVIMainHeader() 1390 { 1391 _bytesRead += GetLE32(_aviHeader.fcc); 1392 _bytesRead += GetLE32(_aviHeader.cb); 1393 _bytesRead += GetLE32(_aviHeader.dwMicroSecPerFrame); 1394 _bytesRead += GetLE32(_aviHeader.dwMaxBytesPerSec); 1395 _bytesRead += GetLE32(_aviHeader.dwPaddingGranularity); 1396 _bytesRead += GetLE32(_aviHeader.dwFlags); 1397 _bytesRead += GetLE32(_aviHeader.dwTotalFrames); 1398 _bytesRead += GetLE32(_aviHeader.dwInitialFrames); 1399 _bytesRead += GetLE32(_aviHeader.dwStreams); 1400 _bytesRead += GetLE32(_aviHeader.dwSuggestedBufferSize); 1401 _bytesRead += GetLE32(_aviHeader.dwWidth); 1402 _bytesRead += GetLE32(_aviHeader.dwHeight); 1403 _bytesRead += GetLE32(_aviHeader.dwReserved[0]); 1404 _bytesRead += GetLE32(_aviHeader.dwReserved[1]); 1405 _bytesRead += GetLE32(_aviHeader.dwReserved[2]); 1406 _bytesRead += GetLE32(_aviHeader.dwReserved[3]); 1407 1408 if (_aviHeader.fcc != MakeFourCc('a', 'v', 'i', 'h')) 1409 { 1410 return -1; 1411 } 1412 1413 if (_aviHeader.dwFlags & kAvifMustuseindex) 1414 { 1415 return -1; 1416 } 1417 1418 bool readVideoStreamHeader = false; 1419 bool readAudioStreamHeader = false; 1420 unsigned int streamsRead = 0; 1421 while (_aviHeader.dwStreams > streamsRead) 1422 { 1423 uint32_t strltag; 1424 _bytesRead += GetLE32(strltag); 1425 uint32_t strlsize; 1426 _bytesRead += GetLE32(strlsize); 1427 const long endSeekPos = ftell(_aviFile) + 1428 static_cast<int32_t>(strlsize); 1429 1430 if (strltag != MakeFourCc('L', 'I', 'S', 'T')) 1431 { 1432 return -1; 1433 } 1434 1435 uint32_t listTag; 1436 _bytesRead += GetLE32(listTag); 1437 if (listTag != MakeFourCc('s', 't', 'r', 'l')) 1438 { 1439 return -1; 1440 } 1441 1442 uint32_t chunktag; 1443 _bytesRead += GetLE32(chunktag); 1444 uint32_t chunksize; 1445 _bytesRead += GetLE32(chunksize); 1446 1447 if (chunktag != MakeFourCc('s', 't', 'r', 'h')) 1448 { 1449 return -1; 1450 } 1451 1452 AVISTREAMHEADER tmpStreamHeader; 1453 tmpStreamHeader.fcc = chunktag; 1454 tmpStreamHeader.cb = chunksize; 1455 1456 _bytesRead += GetLE32(tmpStreamHeader.fccType); 1457 _bytesRead += GetLE32(tmpStreamHeader.fccHandler); 1458 _bytesRead += GetLE32(tmpStreamHeader.dwFlags); 1459 _bytesRead += GetLE16(tmpStreamHeader.wPriority); 1460 _bytesRead += GetLE16(tmpStreamHeader.wLanguage); 1461 _bytesRead += GetLE32(tmpStreamHeader.dwInitialFrames); 1462 _bytesRead += GetLE32(tmpStreamHeader.dwScale); 1463 _bytesRead += GetLE32(tmpStreamHeader.dwRate); 1464 _bytesRead += GetLE32(tmpStreamHeader.dwStart); 1465 _bytesRead += GetLE32(tmpStreamHeader.dwLength); 1466 _bytesRead += GetLE32(tmpStreamHeader.dwSuggestedBufferSize); 1467 _bytesRead += GetLE32(tmpStreamHeader.dwQuality); 1468 _bytesRead += GetLE32(tmpStreamHeader.dwSampleSize); 1469 1470 uint16_t left; 1471 _bytesRead += GetLE16(left); 1472 tmpStreamHeader.rcFrame.left = left; 1473 uint16_t top; 1474 _bytesRead += GetLE16(top); 1475 tmpStreamHeader.rcFrame.top = top; 1476 uint16_t right; 1477 _bytesRead += GetLE16(right); 1478 tmpStreamHeader.rcFrame.right = right; 1479 uint16_t bottom; 1480 _bytesRead += GetLE16(bottom); 1481 tmpStreamHeader.rcFrame.bottom = bottom; 1482 1483 if (!readVideoStreamHeader 1484 && (tmpStreamHeader.fccType == MakeFourCc('v', 'i', 'd', 's'))) 1485 { 1486 _videoStreamHeader = tmpStreamHeader; //Bitwise copy is OK! 1487 const int32_t err = ReadAVIVideoStreamHeader(endSeekPos); 1488 if (err) 1489 { 1490 return -1; 1491 } 1492 // Make sure there actually is video data in the file... 1493 if (_videoStreamHeader.dwLength == 0) 1494 { 1495 return -1; 1496 } 1497 readVideoStreamHeader = true; 1498 } else if(!readAudioStreamHeader && 1499 (tmpStreamHeader.fccType == MakeFourCc('a', 'u', 'd', 's'))) { 1500 _audioStreamHeader = tmpStreamHeader; 1501 const int32_t err = ReadAVIAudioStreamHeader(endSeekPos); 1502 if (err) 1503 { 1504 return -1; 1505 } 1506 readAudioStreamHeader = true; 1507 } 1508 else 1509 { 1510 fseek(_aviFile, endSeekPos, SEEK_SET); 1511 _bytesRead += endSeekPos; 1512 } 1513 1514 ++streamsRead; 1515 } 1516 1517 if (!readVideoStreamHeader && !readAudioStreamHeader) 1518 { 1519 return -1; 1520 } 1521 1522 uint32_t tag; 1523 _bytesRead += GetLE32(tag); 1524 uint32_t size; 1525 _bytesRead += GetLE32(size); 1526 1527 if (tag == MakeFourCc('J', 'U', 'N', 'K')) 1528 { 1529 fseek(_aviFile, size, SEEK_CUR); 1530 _bytesRead += size; 1531 _bytesRead += GetLE32(tag); 1532 _bytesRead += GetLE32(size); 1533 } 1534 if (tag != MakeFourCc('L', 'I', 'S', 'T')) 1535 { 1536 return -1; 1537 } 1538 uint32_t listTag; 1539 _bytesRead += GetLE32(listTag); 1540 if (listTag != MakeFourCc('m', 'o', 'v', 'i')) 1541 { 1542 return -1; 1543 } 1544 _dataLength = size; 1545 return 0; 1546 } 1547 1548 int32_t AviFile::ReadAVIVideoStreamHeader(int32_t endpos) 1549 { 1550 uint32_t chunktag; 1551 _bytesRead += GetLE32(chunktag); 1552 uint32_t chunksize; 1553 _bytesRead += GetLE32(chunksize); 1554 1555 if (chunktag != MakeFourCc('s', 't', 'r', 'f')) 1556 { 1557 return -1; 1558 } 1559 1560 _bytesRead += GetLE32(_videoFormatHeader.biSize); 1561 _bytesRead += GetLE32(_videoFormatHeader.biWidth); 1562 _bytesRead += GetLE32(_videoFormatHeader.biHeight); 1563 _bytesRead += GetLE16(_videoFormatHeader.biPlanes); 1564 _bytesRead += GetLE16(_videoFormatHeader.biBitCount); 1565 _bytesRead += GetLE32(_videoFormatHeader.biCompression); 1566 _bytesRead += GetLE32(_videoFormatHeader.biSizeImage); 1567 _bytesRead += GetLE32(_videoFormatHeader.biXPelsPerMeter); 1568 _bytesRead += GetLE32(_videoFormatHeader.biYPelsPerMeter); 1569 _bytesRead += GetLE32(_videoFormatHeader.biClrUsed); 1570 _bytesRead += GetLE32(_videoFormatHeader.biClrImportant); 1571 1572 if (chunksize > _videoFormatHeader.biSize) 1573 { 1574 const uint32_t size = chunksize - _videoFormatHeader.biSize; 1575 const uint32_t readSize = MinValue(size, CODEC_CONFIG_LENGTH); 1576 _bytesRead += GetBuffer( 1577 reinterpret_cast<uint8_t*>(_videoConfigParameters), readSize); 1578 _videoConfigLength = readSize; 1579 int32_t skipSize = chunksize - _videoFormatHeader.biSize - 1580 readSize; 1581 if (skipSize > 0) 1582 { 1583 fseek(_aviFile, skipSize, SEEK_CUR); 1584 _bytesRead += skipSize; 1585 } 1586 } 1587 1588 while (static_cast<long>(_bytesRead) < endpos) 1589 { 1590 uint32_t chunktag; 1591 _bytesRead += GetLE32(chunktag); 1592 uint32_t chunksize; 1593 _bytesRead += GetLE32(chunksize); 1594 1595 if (chunktag == MakeFourCc('s', 't', 'r', 'n')) 1596 { 1597 const uint32_t size = MinValue(chunksize, STREAM_NAME_LENGTH); 1598 _bytesRead += GetBuffer( 1599 reinterpret_cast<uint8_t*>(_videoStreamName), size); 1600 } 1601 else if (chunktag == MakeFourCc('s', 't', 'r', 'd')) 1602 { 1603 const uint32_t size = MinValue(chunksize, CODEC_CONFIG_LENGTH); 1604 _bytesRead += GetBuffer( 1605 reinterpret_cast<uint8_t*>(_videoConfigParameters), size); 1606 _videoConfigLength = size; 1607 } 1608 else 1609 { 1610 fseek(_aviFile, chunksize, SEEK_CUR); 1611 _bytesRead += chunksize; 1612 } 1613 1614 if (feof(_aviFile)) 1615 { 1616 return -1; 1617 } 1618 } 1619 _videoStream.streamType = AviFile::AVI_VIDEO; 1620 _videoStream.streamNumber = _nrStreams++; 1621 1622 return 0; 1623 } 1624 1625 int32_t AviFile::ReadAVIAudioStreamHeader(int32_t endpos) 1626 { 1627 uint32_t chunktag; 1628 _bytesRead += GetLE32(chunktag); 1629 uint32_t chunksize; 1630 _bytesRead += GetLE32(chunksize); 1631 1632 if (chunktag != MakeFourCc('s', 't', 'r', 'f')) 1633 { 1634 return -1; 1635 } 1636 1637 const size_t startRead = _bytesRead; 1638 _bytesRead += GetLE16(_audioFormatHeader.wFormatTag); 1639 _bytesRead += GetLE16(_audioFormatHeader.nChannels); 1640 _bytesRead += GetLE32(_audioFormatHeader.nSamplesPerSec); 1641 _bytesRead += GetLE32(_audioFormatHeader.nAvgBytesPerSec); 1642 _bytesRead += GetLE16(_audioFormatHeader.nBlockAlign); 1643 _bytesRead += GetLE16(_audioFormatHeader.wBitsPerSample); 1644 if (chunksize > 0x10) { 1645 _bytesRead += GetLE16(_audioFormatHeader.cbSize); 1646 } 1647 1648 const uint32_t diffRead = chunksize - (_bytesRead - startRead); 1649 if (diffRead > 0) 1650 { 1651 const uint32_t size = MinValue(diffRead, CODEC_CONFIG_LENGTH); 1652 _bytesRead += GetBuffer( 1653 reinterpret_cast<uint8_t*>(_audioConfigParameters), size); 1654 } 1655 1656 while (static_cast<long>(_bytesRead) < endpos) 1657 { 1658 uint32_t chunktag; 1659 _bytesRead += GetLE32(chunktag); 1660 uint32_t chunksize; 1661 _bytesRead += GetLE32(chunksize); 1662 1663 if (chunktag == MakeFourCc('s', 't', 'r', 'n')) 1664 { 1665 const uint32_t size = MinValue(chunksize, STREAM_NAME_LENGTH); 1666 _bytesRead += GetBuffer( 1667 reinterpret_cast<uint8_t*>(_audioStreamName), size); 1668 } 1669 else if (chunktag == MakeFourCc('s', 't', 'r', 'd')) 1670 { 1671 const uint32_t size = MinValue(chunksize, CODEC_CONFIG_LENGTH); 1672 _bytesRead += GetBuffer( 1673 reinterpret_cast<uint8_t*>(_audioConfigParameters), size); 1674 } 1675 else 1676 { 1677 fseek(_aviFile, chunksize, SEEK_CUR); 1678 _bytesRead += chunksize; 1679 } 1680 1681 if (feof(_aviFile)) 1682 { 1683 return -1; 1684 } 1685 } 1686 _audioStream.streamType = AviFile::AVI_AUDIO; 1687 _audioStream.streamNumber = _nrStreams++; 1688 return 0; 1689 } 1690 1691 uint32_t AviFile::StreamAndTwoCharCodeToTag(int32_t streamNum, 1692 const char* twoCharCode) 1693 { 1694 uint8_t a = '0'; 1695 uint8_t b; 1696 switch (streamNum) 1697 { 1698 case 1: 1699 b = '1'; 1700 break; 1701 case 2: 1702 b = '2'; 1703 break; 1704 default: 1705 b = '0'; 1706 } 1707 return MakeFourCc(a, b, twoCharCode[0], twoCharCode[1]); 1708 } 1709 1710 void AviFile::ClearIndexList() 1711 { 1712 for (IndexList::iterator iter = _indexList.begin(); 1713 iter != _indexList.end(); ++iter) { 1714 delete *iter; 1715 } 1716 _indexList.clear(); 1717 } 1718 1719 void AviFile::AddChunkToIndexList(uint32_t inChunkId, 1720 uint32_t inFlags, 1721 uint32_t inOffset, 1722 uint32_t inSize) 1723 { 1724 _indexList.push_back(new AVIINDEXENTRY(inChunkId, inFlags, inOffset, 1725 inSize)); 1726 } 1727 1728 void AviFile::WriteIndex() 1729 { 1730 const uint32_t idxTag = MakeFourCc('i', 'd', 'x', '1'); 1731 _bytesWritten += PutLE32(idxTag); 1732 1733 // Size is unknown at this point. Update later. 1734 _bytesWritten += PutLE32(0); 1735 const size_t idxChunkSize = _bytesWritten; 1736 1737 for (IndexList::iterator iter = _indexList.begin(); 1738 iter != _indexList.end(); ++iter) { 1739 const AVIINDEXENTRY* item = *iter; 1740 _bytesWritten += PutLE32(item->ckid); 1741 _bytesWritten += PutLE32(item->dwFlags); 1742 _bytesWritten += PutLE32(item->dwChunkOffset); 1743 _bytesWritten += PutLE32(item->dwChunkLength); 1744 } 1745 PutLE32LengthFromCurrent(static_cast<long>(idxChunkSize)); 1746 } 1747 } // namespace webrtc 1748