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 #include <assert.h> 12 13 #include "webrtc/modules/media_file/source/media_file_impl.h" 14 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" 15 #include "webrtc/system_wrappers/interface/file_wrapper.h" 16 #include "webrtc/system_wrappers/interface/tick_util.h" 17 #include "webrtc/system_wrappers/interface/trace.h" 18 19 namespace webrtc { 20 MediaFile* MediaFile::CreateMediaFile(const int32_t id) 21 { 22 return new MediaFileImpl(id); 23 } 24 25 void MediaFile::DestroyMediaFile(MediaFile* module) 26 { 27 delete static_cast<MediaFileImpl*>(module); 28 } 29 30 MediaFileImpl::MediaFileImpl(const int32_t id) 31 : _id(id), 32 _crit(CriticalSectionWrapper::CreateCriticalSection()), 33 _callbackCrit(CriticalSectionWrapper::CreateCriticalSection()), 34 _ptrFileUtilityObj(NULL), 35 codec_info_(), 36 _ptrInStream(NULL), 37 _ptrOutStream(NULL), 38 _fileFormat((FileFormats)-1), 39 _recordDurationMs(0), 40 _playoutPositionMs(0), 41 _notificationMs(0), 42 _playingActive(false), 43 _recordingActive(false), 44 _isStereo(false), 45 _openFile(false), 46 _fileName(), 47 _ptrCallback(NULL) 48 { 49 WEBRTC_TRACE(kTraceMemory, kTraceFile, id, "Created"); 50 51 codec_info_.plname[0] = '\0'; 52 _fileName[0] = '\0'; 53 } 54 55 56 MediaFileImpl::~MediaFileImpl() 57 { 58 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, "~MediaFileImpl()"); 59 { 60 CriticalSectionScoped lock(_crit); 61 62 if(_playingActive) 63 { 64 StopPlaying(); 65 } 66 67 if(_recordingActive) 68 { 69 StopRecording(); 70 } 71 72 delete _ptrFileUtilityObj; 73 74 if(_openFile) 75 { 76 delete _ptrInStream; 77 _ptrInStream = NULL; 78 delete _ptrOutStream; 79 _ptrOutStream = NULL; 80 } 81 } 82 83 delete _crit; 84 delete _callbackCrit; 85 } 86 87 int32_t MediaFileImpl::ChangeUniqueId(const int32_t id) 88 { 89 _id = id; 90 return 0; 91 } 92 93 int32_t MediaFileImpl::TimeUntilNextProcess() 94 { 95 WEBRTC_TRACE( 96 kTraceWarning, 97 kTraceFile, 98 _id, 99 "TimeUntilNextProcess: This method is not used by MediaFile class."); 100 return -1; 101 } 102 103 int32_t MediaFileImpl::Process() 104 { 105 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 106 "Process: This method is not used by MediaFile class."); 107 return -1; 108 } 109 110 int32_t MediaFileImpl::PlayoutAVIVideoData( 111 int8_t* buffer, 112 uint32_t& dataLengthInBytes) 113 { 114 return PlayoutData( buffer, dataLengthInBytes, true); 115 } 116 117 int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer, 118 uint32_t& dataLengthInBytes) 119 { 120 return PlayoutData( buffer, dataLengthInBytes, false); 121 } 122 123 int32_t MediaFileImpl::PlayoutData(int8_t* buffer, uint32_t& dataLengthInBytes, 124 bool video) 125 { 126 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, 127 "MediaFileImpl::PlayoutData(buffer= 0x%x, bufLen= %ld)", 128 buffer, dataLengthInBytes); 129 130 const uint32_t bufferLengthInBytes = dataLengthInBytes; 131 dataLengthInBytes = 0; 132 133 if(buffer == NULL || bufferLengthInBytes == 0) 134 { 135 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 136 "Buffer pointer or length is NULL!"); 137 return -1; 138 } 139 140 int32_t bytesRead = 0; 141 { 142 CriticalSectionScoped lock(_crit); 143 144 if(!_playingActive) 145 { 146 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 147 "Not currently playing!"); 148 return -1; 149 } 150 151 if(!_ptrFileUtilityObj) 152 { 153 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 154 "Playing, but no FileUtility object!"); 155 StopPlaying(); 156 return -1; 157 } 158 159 switch(_fileFormat) 160 { 161 case kFileFormatPcm32kHzFile: 162 case kFileFormatPcm16kHzFile: 163 case kFileFormatPcm8kHzFile: 164 bytesRead = _ptrFileUtilityObj->ReadPCMData( 165 *_ptrInStream, 166 buffer, 167 bufferLengthInBytes); 168 break; 169 case kFileFormatCompressedFile: 170 bytesRead = _ptrFileUtilityObj->ReadCompressedData( 171 *_ptrInStream, 172 buffer, 173 bufferLengthInBytes); 174 break; 175 case kFileFormatWavFile: 176 bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono( 177 *_ptrInStream, 178 buffer, 179 bufferLengthInBytes); 180 break; 181 case kFileFormatPreencodedFile: 182 bytesRead = _ptrFileUtilityObj->ReadPreEncodedData( 183 *_ptrInStream, 184 buffer, 185 bufferLengthInBytes); 186 if(bytesRead > 0) 187 { 188 dataLengthInBytes = bytesRead; 189 return 0; 190 } 191 break; 192 case kFileFormatAviFile: 193 { 194 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 195 if(video) 196 { 197 bytesRead = _ptrFileUtilityObj->ReadAviVideoData( 198 buffer, 199 bufferLengthInBytes); 200 } 201 else 202 { 203 bytesRead = _ptrFileUtilityObj->ReadAviAudioData( 204 buffer, 205 bufferLengthInBytes); 206 } 207 break; 208 #else 209 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 210 "Invalid file format: %d", kFileFormatAviFile); 211 assert(false); 212 break; 213 #endif 214 } 215 } 216 217 if( bytesRead > 0) 218 { 219 dataLengthInBytes =(uint32_t) bytesRead; 220 } 221 } 222 HandlePlayCallbacks(bytesRead); 223 return 0; 224 } 225 226 void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead) 227 { 228 bool playEnded = false; 229 uint32_t callbackNotifyMs = 0; 230 231 if(bytesRead > 0) 232 { 233 // Check if it's time for PlayNotification(..). 234 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); 235 if(_notificationMs) 236 { 237 if(_playoutPositionMs >= _notificationMs) 238 { 239 _notificationMs = 0; 240 callbackNotifyMs = _playoutPositionMs; 241 } 242 } 243 } 244 else 245 { 246 // If no bytes were read assume end of file. 247 StopPlaying(); 248 playEnded = true; 249 } 250 251 // Only _callbackCrit may and should be taken when making callbacks. 252 CriticalSectionScoped lock(_callbackCrit); 253 if(_ptrCallback) 254 { 255 if(callbackNotifyMs) 256 { 257 _ptrCallback->PlayNotification(_id, callbackNotifyMs); 258 } 259 if(playEnded) 260 { 261 _ptrCallback->PlayFileEnded(_id); 262 } 263 } 264 } 265 266 int32_t MediaFileImpl::PlayoutStereoData( 267 int8_t* bufferLeft, 268 int8_t* bufferRight, 269 uint32_t& dataLengthInBytes) 270 { 271 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, 272 "MediaFileImpl::PlayoutStereoData(Left = 0x%x, Right = 0x%x,\ 273 Len= %ld)", 274 bufferLeft, 275 bufferRight, 276 dataLengthInBytes); 277 278 const uint32_t bufferLengthInBytes = dataLengthInBytes; 279 dataLengthInBytes = 0; 280 281 if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0) 282 { 283 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 284 "A buffer pointer or the length is NULL!"); 285 return -1; 286 } 287 288 bool playEnded = false; 289 uint32_t callbackNotifyMs = 0; 290 { 291 CriticalSectionScoped lock(_crit); 292 293 if(!_playingActive || !_isStereo) 294 { 295 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 296 "Not currently playing stereo!"); 297 return -1; 298 } 299 300 if(!_ptrFileUtilityObj) 301 { 302 WEBRTC_TRACE( 303 kTraceError, 304 kTraceFile, 305 _id, 306 "Playing stereo, but the FileUtility objects is NULL!"); 307 StopPlaying(); 308 return -1; 309 } 310 311 // Stereo playout only supported for WAV files. 312 int32_t bytesRead = 0; 313 switch(_fileFormat) 314 { 315 case kFileFormatWavFile: 316 bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo( 317 *_ptrInStream, 318 bufferLeft, 319 bufferRight, 320 bufferLengthInBytes); 321 break; 322 default: 323 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 324 "Trying to read non-WAV as stereo audio\ 325 (not supported)"); 326 break; 327 } 328 329 if(bytesRead > 0) 330 { 331 dataLengthInBytes = bytesRead; 332 333 // Check if it's time for PlayNotification(..). 334 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); 335 if(_notificationMs) 336 { 337 if(_playoutPositionMs >= _notificationMs) 338 { 339 _notificationMs = 0; 340 callbackNotifyMs = _playoutPositionMs; 341 } 342 } 343 } 344 else 345 { 346 // If no bytes were read assume end of file. 347 StopPlaying(); 348 playEnded = true; 349 } 350 } 351 352 CriticalSectionScoped lock(_callbackCrit); 353 if(_ptrCallback) 354 { 355 if(callbackNotifyMs) 356 { 357 _ptrCallback->PlayNotification(_id, callbackNotifyMs); 358 } 359 if(playEnded) 360 { 361 _ptrCallback->PlayFileEnded(_id); 362 } 363 } 364 return 0; 365 } 366 367 int32_t MediaFileImpl::StartPlayingAudioFile( 368 const char* fileName, 369 const uint32_t notificationTimeMs, 370 const bool loop, 371 const FileFormats format, 372 const CodecInst* codecInst, 373 const uint32_t startPointMs, 374 const uint32_t stopPointMs) 375 { 376 const bool videoOnly = false; 377 return StartPlayingFile(fileName, notificationTimeMs, loop, videoOnly, 378 format, codecInst, startPointMs, stopPointMs); 379 } 380 381 382 int32_t MediaFileImpl::StartPlayingVideoFile(const char* fileName, 383 const bool loop, 384 bool videoOnly, 385 const FileFormats format) 386 { 387 388 const uint32_t notificationTimeMs = 0; 389 const uint32_t startPointMs = 0; 390 const uint32_t stopPointMs = 0; 391 return StartPlayingFile(fileName, notificationTimeMs, loop, videoOnly, 392 format, 0, startPointMs, stopPointMs); 393 } 394 395 int32_t MediaFileImpl::StartPlayingFile( 396 const char* fileName, 397 const uint32_t notificationTimeMs, 398 const bool loop, 399 bool videoOnly, 400 const FileFormats format, 401 const CodecInst* codecInst, 402 const uint32_t startPointMs, 403 const uint32_t stopPointMs) 404 { 405 406 if(!ValidFileName(fileName)) 407 { 408 return -1; 409 } 410 if(!ValidFileFormat(format,codecInst)) 411 { 412 return -1; 413 } 414 if(!ValidFilePositions(startPointMs,stopPointMs)) 415 { 416 return -1; 417 } 418 419 // Check that the file will play longer than notificationTimeMs ms. 420 if((startPointMs && stopPointMs && !loop) && 421 (notificationTimeMs > (stopPointMs - startPointMs))) 422 { 423 WEBRTC_TRACE( 424 kTraceError, 425 kTraceFile, 426 _id, 427 "specified notification time is longer than amount of ms that will\ 428 be played"); 429 return -1; 430 } 431 432 FileWrapper* inputStream = FileWrapper::Create(); 433 if(inputStream == NULL) 434 { 435 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, 436 "Failed to allocate input stream for file %s", fileName); 437 return -1; 438 } 439 440 // TODO (hellner): make all formats support reading from stream. 441 bool useStream = (format != kFileFormatAviFile); 442 if( useStream) 443 { 444 if(inputStream->OpenFile(fileName, true, loop) != 0) 445 { 446 delete inputStream; 447 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 448 "Could not open input file %s", fileName); 449 return -1; 450 } 451 } 452 453 if(StartPlayingStream(*inputStream, fileName, loop, notificationTimeMs, 454 format, codecInst, startPointMs, stopPointMs, 455 videoOnly) == -1) 456 { 457 if( useStream) 458 { 459 inputStream->CloseFile(); 460 } 461 delete inputStream; 462 return -1; 463 } 464 465 CriticalSectionScoped lock(_crit); 466 _openFile = true; 467 strncpy(_fileName, fileName, sizeof(_fileName)); 468 _fileName[sizeof(_fileName) - 1] = '\0'; 469 return 0; 470 } 471 472 int32_t MediaFileImpl::StartPlayingAudioStream( 473 InStream& stream, 474 const uint32_t notificationTimeMs, 475 const FileFormats format, 476 const CodecInst* codecInst, 477 const uint32_t startPointMs, 478 const uint32_t stopPointMs) 479 { 480 return StartPlayingStream(stream, 0, false, notificationTimeMs, format, 481 codecInst, startPointMs, stopPointMs); 482 } 483 484 int32_t MediaFileImpl::StartPlayingStream( 485 InStream& stream, 486 const char* filename, 487 bool loop, 488 const uint32_t notificationTimeMs, 489 const FileFormats format, 490 const CodecInst* codecInst, 491 const uint32_t startPointMs, 492 const uint32_t stopPointMs, 493 bool videoOnly) 494 { 495 if(!ValidFileFormat(format,codecInst)) 496 { 497 return -1; 498 } 499 500 if(!ValidFilePositions(startPointMs,stopPointMs)) 501 { 502 return -1; 503 } 504 505 CriticalSectionScoped lock(_crit); 506 if(_playingActive || _recordingActive) 507 { 508 WEBRTC_TRACE( 509 kTraceError, 510 kTraceFile, 511 _id, 512 "StartPlaying called, but already playing or recording file %s", 513 (_fileName[0] == '\0') ? "(name not set)" : _fileName); 514 return -1; 515 } 516 517 if(_ptrFileUtilityObj != NULL) 518 { 519 WEBRTC_TRACE(kTraceError, 520 kTraceFile, 521 _id, 522 "StartPlaying called, but FileUtilityObj already exists!"); 523 StopPlaying(); 524 return -1; 525 } 526 527 _ptrFileUtilityObj = new ModuleFileUtility(_id); 528 if(_ptrFileUtilityObj == NULL) 529 { 530 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, 531 "Failed to create FileUtilityObj!"); 532 return -1; 533 } 534 535 switch(format) 536 { 537 case kFileFormatWavFile: 538 { 539 if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs, 540 stopPointMs) == -1) 541 { 542 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 543 "Not a valid WAV file!"); 544 StopPlaying(); 545 return -1; 546 } 547 _fileFormat = kFileFormatWavFile; 548 break; 549 } 550 case kFileFormatCompressedFile: 551 { 552 if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs, 553 stopPointMs) == -1) 554 { 555 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 556 "Not a valid Compressed file!"); 557 StopPlaying(); 558 return -1; 559 } 560 _fileFormat = kFileFormatCompressedFile; 561 break; 562 } 563 case kFileFormatPcm8kHzFile: 564 case kFileFormatPcm16kHzFile: 565 case kFileFormatPcm32kHzFile: 566 { 567 // ValidFileFormat() called in the beginneing of this function 568 // prevents codecInst from being NULL here. 569 assert(codecInst != NULL); 570 if(!ValidFrequency(codecInst->plfreq) || 571 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs, 572 stopPointMs, 573 codecInst->plfreq) == -1) 574 { 575 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 576 "Not a valid raw 8 or 16 KHz PCM file!"); 577 StopPlaying(); 578 return -1; 579 } 580 581 _fileFormat = format; 582 break; 583 } 584 case kFileFormatPreencodedFile: 585 { 586 // ValidFileFormat() called in the beginneing of this function 587 // prevents codecInst from being NULL here. 588 assert(codecInst != NULL); 589 if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) == 590 -1) 591 { 592 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 593 "Not a valid PreEncoded file!"); 594 StopPlaying(); 595 return -1; 596 } 597 598 _fileFormat = kFileFormatPreencodedFile; 599 break; 600 } 601 case kFileFormatAviFile: 602 { 603 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 604 if(_ptrFileUtilityObj->InitAviReading( filename, videoOnly, loop)) 605 { 606 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 607 "Not a valid AVI file!"); 608 StopPlaying(); 609 610 return -1; 611 } 612 613 _ptrFileUtilityObj->codec_info(codec_info_); 614 615 _fileFormat = kFileFormatAviFile; 616 break; 617 #else 618 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 619 "Invalid file format: %d", kFileFormatAviFile); 620 assert(false); 621 break; 622 #endif 623 } 624 } 625 if(_ptrFileUtilityObj->codec_info(codec_info_) == -1) 626 { 627 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 628 "Failed to retrieve codec info!"); 629 StopPlaying(); 630 return -1; 631 } 632 633 _isStereo = (codec_info_.channels == 2); 634 if(_isStereo && (_fileFormat != kFileFormatWavFile)) 635 { 636 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 637 "Stereo is only allowed for WAV files"); 638 StopPlaying(); 639 return -1; 640 } 641 _playingActive = true; 642 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); 643 _ptrInStream = &stream; 644 _notificationMs = notificationTimeMs; 645 646 return 0; 647 } 648 649 int32_t MediaFileImpl::StopPlaying() 650 { 651 652 CriticalSectionScoped lock(_crit); 653 _isStereo = false; 654 if(_ptrFileUtilityObj) 655 { 656 delete _ptrFileUtilityObj; 657 _ptrFileUtilityObj = NULL; 658 } 659 if(_ptrInStream) 660 { 661 // If MediaFileImpl opened the InStream it must be reclaimed here. 662 if(_openFile) 663 { 664 delete _ptrInStream; 665 _openFile = false; 666 } 667 _ptrInStream = NULL; 668 } 669 670 codec_info_.pltype = 0; 671 codec_info_.plname[0] = '\0'; 672 673 if(!_playingActive) 674 { 675 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 676 "playing is not active!"); 677 return -1; 678 } 679 680 _playingActive = false; 681 return 0; 682 } 683 684 bool MediaFileImpl::IsPlaying() 685 { 686 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()"); 687 CriticalSectionScoped lock(_crit); 688 return _playingActive; 689 } 690 691 int32_t MediaFileImpl::IncomingAudioData( 692 const int8_t* buffer, 693 const uint32_t bufferLengthInBytes) 694 { 695 return IncomingAudioVideoData( buffer, bufferLengthInBytes, false); 696 } 697 698 int32_t MediaFileImpl::IncomingAVIVideoData( 699 const int8_t* buffer, 700 const uint32_t bufferLengthInBytes) 701 { 702 return IncomingAudioVideoData( buffer, bufferLengthInBytes, true); 703 } 704 705 int32_t MediaFileImpl::IncomingAudioVideoData( 706 const int8_t* buffer, 707 const uint32_t bufferLengthInBytes, 708 const bool video) 709 { 710 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, 711 "MediaFile::IncomingData(buffer= 0x%x, bufLen= %hd", 712 buffer, bufferLengthInBytes); 713 714 if(buffer == NULL || bufferLengthInBytes == 0) 715 { 716 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 717 "Buffer pointer or length is NULL!"); 718 return -1; 719 } 720 721 bool recordingEnded = false; 722 uint32_t callbackNotifyMs = 0; 723 { 724 CriticalSectionScoped lock(_crit); 725 726 if(!_recordingActive) 727 { 728 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 729 "Not currently recording!"); 730 return -1; 731 } 732 if(_ptrOutStream == NULL) 733 { 734 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 735 "Recording is active, but output stream is NULL!"); 736 assert(false); 737 return -1; 738 } 739 740 int32_t bytesWritten = 0; 741 uint32_t samplesWritten = codec_info_.pacsize; 742 if(_ptrFileUtilityObj) 743 { 744 switch(_fileFormat) 745 { 746 case kFileFormatPcm8kHzFile: 747 case kFileFormatPcm16kHzFile: 748 case kFileFormatPcm32kHzFile: 749 bytesWritten = _ptrFileUtilityObj->WritePCMData( 750 *_ptrOutStream, 751 buffer, 752 bufferLengthInBytes); 753 754 // Sample size is 2 bytes. 755 if(bytesWritten > 0) 756 { 757 samplesWritten = bytesWritten/sizeof(int16_t); 758 } 759 break; 760 case kFileFormatCompressedFile: 761 bytesWritten = _ptrFileUtilityObj->WriteCompressedData( 762 *_ptrOutStream, buffer, bufferLengthInBytes); 763 break; 764 case kFileFormatWavFile: 765 bytesWritten = _ptrFileUtilityObj->WriteWavData( 766 *_ptrOutStream, 767 buffer, 768 bufferLengthInBytes); 769 if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname, 770 "L16", 4) == 0) 771 { 772 // Sample size is 2 bytes. 773 samplesWritten = bytesWritten/sizeof(int16_t); 774 } 775 break; 776 case kFileFormatPreencodedFile: 777 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData( 778 *_ptrOutStream, buffer, bufferLengthInBytes); 779 break; 780 case kFileFormatAviFile: 781 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 782 if(video) 783 { 784 bytesWritten = _ptrFileUtilityObj->WriteAviVideoData( 785 buffer, bufferLengthInBytes); 786 }else 787 { 788 bytesWritten = _ptrFileUtilityObj->WriteAviAudioData( 789 buffer, bufferLengthInBytes); 790 } 791 break; 792 #else 793 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 794 "Invalid file format: %d", kFileFormatAviFile); 795 assert(false); 796 break; 797 #endif 798 } 799 } else { 800 // TODO (hellner): quick look at the code makes me think that this 801 // code is never executed. Remove? 802 if(_ptrOutStream) 803 { 804 if(_ptrOutStream->Write(buffer, bufferLengthInBytes)) 805 { 806 bytesWritten = bufferLengthInBytes; 807 } 808 } 809 } 810 811 if(!video) 812 { 813 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000); 814 } 815 816 // Check if it's time for RecordNotification(..). 817 if(_notificationMs) 818 { 819 if(_recordDurationMs >= _notificationMs) 820 { 821 _notificationMs = 0; 822 callbackNotifyMs = _recordDurationMs; 823 } 824 } 825 if(bytesWritten < (int32_t)bufferLengthInBytes) 826 { 827 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 828 "Failed to write all requested bytes!"); 829 StopRecording(); 830 recordingEnded = true; 831 } 832 } 833 834 // Only _callbackCrit may and should be taken when making callbacks. 835 CriticalSectionScoped lock(_callbackCrit); 836 if(_ptrCallback) 837 { 838 if(callbackNotifyMs) 839 { 840 _ptrCallback->RecordNotification(_id, callbackNotifyMs); 841 } 842 if(recordingEnded) 843 { 844 _ptrCallback->RecordFileEnded(_id); 845 return -1; 846 } 847 } 848 return 0; 849 } 850 851 int32_t MediaFileImpl::StartRecordingAudioFile( 852 const char* fileName, 853 const FileFormats format, 854 const CodecInst& codecInst, 855 const uint32_t notificationTimeMs, 856 const uint32_t maxSizeBytes) 857 { 858 VideoCodec dummyCodecInst; 859 return StartRecordingFile(fileName, format, codecInst, dummyCodecInst, 860 notificationTimeMs, maxSizeBytes); 861 } 862 863 864 int32_t MediaFileImpl::StartRecordingVideoFile( 865 const char* fileName, 866 const FileFormats format, 867 const CodecInst& codecInst, 868 const VideoCodec& videoCodecInst, 869 bool videoOnly) 870 { 871 const uint32_t notificationTimeMs = 0; 872 const uint32_t maxSizeBytes = 0; 873 874 return StartRecordingFile(fileName, format, codecInst, videoCodecInst, 875 notificationTimeMs, maxSizeBytes, videoOnly); 876 } 877 878 int32_t MediaFileImpl::StartRecordingFile( 879 const char* fileName, 880 const FileFormats format, 881 const CodecInst& codecInst, 882 const VideoCodec& videoCodecInst, 883 const uint32_t notificationTimeMs, 884 const uint32_t maxSizeBytes, 885 bool videoOnly) 886 { 887 888 if(!ValidFileName(fileName)) 889 { 890 return -1; 891 } 892 if(!ValidFileFormat(format,&codecInst)) 893 { 894 return -1; 895 } 896 897 FileWrapper* outputStream = FileWrapper::Create(); 898 if(outputStream == NULL) 899 { 900 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, 901 "Failed to allocate memory for output stream"); 902 return -1; 903 } 904 905 // TODO (hellner): make all formats support writing to stream. 906 const bool useStream = ( format != kFileFormatAviFile); 907 if( useStream) 908 { 909 if(outputStream->OpenFile(fileName, false) != 0) 910 { 911 delete outputStream; 912 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 913 "Could not open output file '%s' for writing!", 914 fileName); 915 return -1; 916 } 917 } 918 if(maxSizeBytes) 919 { 920 outputStream->SetMaxFileSize(maxSizeBytes); 921 } 922 923 if(StartRecordingStream(*outputStream, fileName, format, codecInst, 924 videoCodecInst, notificationTimeMs, 925 videoOnly) == -1) 926 { 927 if( useStream) 928 { 929 outputStream->CloseFile(); 930 } 931 delete outputStream; 932 return -1; 933 } 934 935 CriticalSectionScoped lock(_crit); 936 _openFile = true; 937 strncpy(_fileName, fileName, sizeof(_fileName)); 938 _fileName[sizeof(_fileName) - 1] = '\0'; 939 return 0; 940 } 941 942 int32_t MediaFileImpl::StartRecordingAudioStream( 943 OutStream& stream, 944 const FileFormats format, 945 const CodecInst& codecInst, 946 const uint32_t notificationTimeMs) 947 { 948 VideoCodec dummyCodecInst; 949 return StartRecordingStream(stream, 0, format, codecInst, dummyCodecInst, 950 notificationTimeMs); 951 } 952 953 int32_t MediaFileImpl::StartRecordingStream( 954 OutStream& stream, 955 const char* fileName, 956 const FileFormats format, 957 const CodecInst& codecInst, 958 const VideoCodec& videoCodecInst, 959 const uint32_t notificationTimeMs, 960 bool videoOnly) 961 { 962 963 // Check codec info 964 if(!ValidFileFormat(format,&codecInst)) 965 { 966 return -1; 967 } 968 969 CriticalSectionScoped lock(_crit); 970 if(_recordingActive || _playingActive) 971 { 972 WEBRTC_TRACE( 973 kTraceError, 974 kTraceFile, 975 _id, 976 "StartRecording called, but already recording or playing file %s!", 977 _fileName); 978 return -1; 979 } 980 981 if(_ptrFileUtilityObj != NULL) 982 { 983 WEBRTC_TRACE( 984 kTraceError, 985 kTraceFile, 986 _id, 987 "StartRecording called, but fileUtilityObj already exists!"); 988 StopRecording(); 989 return -1; 990 } 991 992 _ptrFileUtilityObj = new ModuleFileUtility(_id); 993 if(_ptrFileUtilityObj == NULL) 994 { 995 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, 996 "Cannot allocate fileUtilityObj!"); 997 return -1; 998 } 999 1000 CodecInst tmpAudioCodec; 1001 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst)); 1002 switch(format) 1003 { 1004 case kFileFormatWavFile: 1005 { 1006 if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1) 1007 { 1008 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1009 "Failed to initialize WAV file!"); 1010 delete _ptrFileUtilityObj; 1011 _ptrFileUtilityObj = NULL; 1012 return -1; 1013 } 1014 _fileFormat = kFileFormatWavFile; 1015 break; 1016 } 1017 case kFileFormatCompressedFile: 1018 { 1019 // Write compression codec name at beginning of file 1020 if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) == 1021 -1) 1022 { 1023 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1024 "Failed to initialize Compressed file!"); 1025 delete _ptrFileUtilityObj; 1026 _ptrFileUtilityObj = NULL; 1027 return -1; 1028 } 1029 _fileFormat = kFileFormatCompressedFile; 1030 break; 1031 } 1032 case kFileFormatPcm8kHzFile: 1033 case kFileFormatPcm16kHzFile: 1034 { 1035 if(!ValidFrequency(codecInst.plfreq) || 1036 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) == 1037 -1) 1038 { 1039 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1040 "Failed to initialize 8 or 16KHz PCM file!"); 1041 delete _ptrFileUtilityObj; 1042 _ptrFileUtilityObj = NULL; 1043 return -1; 1044 } 1045 _fileFormat = format; 1046 break; 1047 } 1048 case kFileFormatPreencodedFile: 1049 { 1050 if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) == 1051 -1) 1052 { 1053 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1054 "Failed to initialize Pre-Encoded file!"); 1055 delete _ptrFileUtilityObj; 1056 _ptrFileUtilityObj = NULL; 1057 return -1; 1058 } 1059 1060 _fileFormat = kFileFormatPreencodedFile; 1061 break; 1062 } 1063 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 1064 case kFileFormatAviFile: 1065 { 1066 if( (_ptrFileUtilityObj->InitAviWriting( 1067 fileName, 1068 codecInst, 1069 videoCodecInst,videoOnly) == -1) || 1070 (_ptrFileUtilityObj->codec_info(tmpAudioCodec) != 0)) 1071 { 1072 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1073 "Failed to initialize AVI file!"); 1074 delete _ptrFileUtilityObj; 1075 _ptrFileUtilityObj = NULL; 1076 return -1; 1077 } 1078 _fileFormat = kFileFormatAviFile; 1079 break; 1080 } 1081 #endif 1082 default: 1083 { 1084 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1085 "Invalid file format %d specified!", format); 1086 delete _ptrFileUtilityObj; 1087 _ptrFileUtilityObj = NULL; 1088 return -1; 1089 } 1090 } 1091 _isStereo = (tmpAudioCodec.channels == 2); 1092 if(_isStereo) 1093 { 1094 if(_fileFormat != kFileFormatWavFile) 1095 { 1096 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 1097 "Stereo is only allowed for WAV files"); 1098 StopRecording(); 1099 return -1; 1100 } 1101 if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) && 1102 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) && 1103 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0)) 1104 { 1105 WEBRTC_TRACE( 1106 kTraceWarning, 1107 kTraceFile, 1108 _id, 1109 "Stereo is only allowed for codec PCMU, PCMA and L16 "); 1110 StopRecording(); 1111 return -1; 1112 } 1113 } 1114 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst)); 1115 _recordingActive = true; 1116 _ptrOutStream = &stream; 1117 _notificationMs = notificationTimeMs; 1118 _recordDurationMs = 0; 1119 return 0; 1120 } 1121 1122 int32_t MediaFileImpl::StopRecording() 1123 { 1124 1125 CriticalSectionScoped lock(_crit); 1126 if(!_recordingActive) 1127 { 1128 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, 1129 "recording is not active!"); 1130 return -1; 1131 } 1132 1133 _isStereo = false; 1134 1135 if(_ptrFileUtilityObj != NULL) 1136 { 1137 // Both AVI and WAV header has to be updated before closing the stream 1138 // because they contain size information. 1139 if((_fileFormat == kFileFormatWavFile) && 1140 (_ptrOutStream != NULL)) 1141 { 1142 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream); 1143 } 1144 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 1145 else if( _fileFormat == kFileFormatAviFile) 1146 { 1147 _ptrFileUtilityObj->CloseAviFile( ); 1148 } 1149 #endif 1150 delete _ptrFileUtilityObj; 1151 _ptrFileUtilityObj = NULL; 1152 } 1153 1154 if(_ptrOutStream != NULL) 1155 { 1156 // If MediaFileImpl opened the OutStream it must be reclaimed here. 1157 if(_openFile) 1158 { 1159 delete _ptrOutStream; 1160 _openFile = false; 1161 } 1162 _ptrOutStream = NULL; 1163 } 1164 1165 _recordingActive = false; 1166 codec_info_.pltype = 0; 1167 codec_info_.plname[0] = '\0'; 1168 1169 return 0; 1170 } 1171 1172 bool MediaFileImpl::IsRecording() 1173 { 1174 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()"); 1175 CriticalSectionScoped lock(_crit); 1176 return _recordingActive; 1177 } 1178 1179 int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs) 1180 { 1181 1182 CriticalSectionScoped lock(_crit); 1183 if(!_recordingActive) 1184 { 1185 durationMs = 0; 1186 return -1; 1187 } 1188 durationMs = _recordDurationMs; 1189 return 0; 1190 } 1191 1192 bool MediaFileImpl::IsStereo() 1193 { 1194 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()"); 1195 CriticalSectionScoped lock(_crit); 1196 return _isStereo; 1197 } 1198 1199 int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback) 1200 { 1201 1202 CriticalSectionScoped lock(_callbackCrit); 1203 1204 _ptrCallback = callback; 1205 return 0; 1206 } 1207 1208 int32_t MediaFileImpl::FileDurationMs(const char* fileName, 1209 uint32_t& durationMs, 1210 const FileFormats format, 1211 const uint32_t freqInHz) 1212 { 1213 1214 if(!ValidFileName(fileName)) 1215 { 1216 return -1; 1217 } 1218 if(!ValidFrequency(freqInHz)) 1219 { 1220 return -1; 1221 } 1222 1223 ModuleFileUtility* utilityObj = new ModuleFileUtility(_id); 1224 if(utilityObj == NULL) 1225 { 1226 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1227 "failed to allocate utility object!"); 1228 return -1; 1229 } 1230 1231 const int32_t duration = utilityObj->FileDurationMs(fileName, format, 1232 freqInHz); 1233 delete utilityObj; 1234 if(duration == -1) 1235 { 1236 durationMs = 0; 1237 return -1; 1238 } 1239 1240 durationMs = duration; 1241 return 0; 1242 } 1243 1244 int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const 1245 { 1246 CriticalSectionScoped lock(_crit); 1247 if(!_playingActive) 1248 { 1249 positionMs = 0; 1250 return -1; 1251 } 1252 positionMs = _playoutPositionMs; 1253 return 0; 1254 } 1255 1256 int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const 1257 { 1258 CriticalSectionScoped lock(_crit); 1259 if(!_playingActive && !_recordingActive) 1260 { 1261 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1262 "Neither playout nor recording has been initialized!"); 1263 return -1; 1264 } 1265 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0') 1266 { 1267 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1268 "The CodecInst for %s is unknown!", 1269 _playingActive ? "Playback" : "Recording"); 1270 return -1; 1271 } 1272 memcpy(&codecInst,&codec_info_,sizeof(CodecInst)); 1273 return 0; 1274 } 1275 1276 int32_t MediaFileImpl::VideoCodecInst(VideoCodec& codecInst) const 1277 { 1278 CriticalSectionScoped lock(_crit); 1279 if(!_playingActive && !_recordingActive) 1280 { 1281 WEBRTC_TRACE(kTraceError, kTraceFile, _id, 1282 "Neither playout nor recording has been initialized!"); 1283 return -1; 1284 } 1285 if( _ptrFileUtilityObj == NULL) 1286 { 1287 return -1; 1288 } 1289 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 1290 VideoCodec videoCodec; 1291 if( _ptrFileUtilityObj->VideoCodecInst( videoCodec) != 0) 1292 { 1293 return -1; 1294 } 1295 memcpy(&codecInst,&videoCodec,sizeof(VideoCodec)); 1296 return 0; 1297 #else 1298 return -1; 1299 #endif 1300 } 1301 1302 bool MediaFileImpl::ValidFileFormat(const FileFormats format, 1303 const CodecInst* codecInst) 1304 { 1305 if(codecInst == NULL) 1306 { 1307 if(format == kFileFormatPreencodedFile || 1308 format == kFileFormatPcm8kHzFile || 1309 format == kFileFormatPcm16kHzFile || 1310 format == kFileFormatPcm32kHzFile) 1311 { 1312 WEBRTC_TRACE(kTraceError, kTraceFile, -1, 1313 "Codec info required for file format specified!"); 1314 return false; 1315 } 1316 } 1317 return true; 1318 } 1319 1320 bool MediaFileImpl::ValidFileName(const char* fileName) 1321 { 1322 if((fileName == NULL) ||(fileName[0] == '\0')) 1323 { 1324 WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!"); 1325 return false; 1326 } 1327 return true; 1328 } 1329 1330 1331 bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs, 1332 const uint32_t stopPointMs) 1333 { 1334 if(startPointMs == 0 && stopPointMs == 0) // Default values 1335 { 1336 return true; 1337 } 1338 if(stopPointMs &&(startPointMs >= stopPointMs)) 1339 { 1340 WEBRTC_TRACE(kTraceError, kTraceFile, -1, 1341 "startPointMs must be less than stopPointMs!"); 1342 return false; 1343 } 1344 if(stopPointMs &&((stopPointMs - startPointMs) < 20)) 1345 { 1346 WEBRTC_TRACE(kTraceError, kTraceFile, -1, 1347 "minimum play duration for files is 20 ms!"); 1348 return false; 1349 } 1350 return true; 1351 } 1352 1353 bool MediaFileImpl::ValidFrequency(const uint32_t frequency) 1354 { 1355 if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000)) 1356 { 1357 return true; 1358 } 1359 WEBRTC_TRACE(kTraceError, kTraceFile, -1, 1360 "Frequency should be 8000, 16000 or 32000 (Hz)"); 1361 return false; 1362 } 1363 } // namespace webrtc 1364