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 "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 12 #include "webrtc/engine_configurations.h" 13 #include "webrtc/modules/media_file/interface/media_file.h" 14 #include "webrtc/modules/utility/source/file_recorder_impl.h" 15 #include "webrtc/system_wrappers/interface/logging.h" 16 17 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 18 #include "critical_section_wrapper.h" 19 #include "frame_scaler.h" 20 #include "video_coder.h" 21 #include "video_frames_queue.h" 22 #endif 23 24 namespace webrtc { 25 FileRecorder* FileRecorder::CreateFileRecorder(uint32_t instanceID, 26 FileFormats fileFormat) 27 { 28 switch(fileFormat) 29 { 30 case kFileFormatWavFile: 31 case kFileFormatCompressedFile: 32 case kFileFormatPreencodedFile: 33 case kFileFormatPcm16kHzFile: 34 case kFileFormatPcm8kHzFile: 35 case kFileFormatPcm32kHzFile: 36 return new FileRecorderImpl(instanceID, fileFormat); 37 case kFileFormatAviFile: 38 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 39 return new AviRecorder(instanceID, fileFormat); 40 #else 41 assert(false); 42 return NULL; 43 #endif 44 } 45 assert(false); 46 return NULL; 47 } 48 49 void FileRecorder::DestroyFileRecorder(FileRecorder* recorder) 50 { 51 delete recorder; 52 } 53 54 FileRecorderImpl::FileRecorderImpl(uint32_t instanceID, 55 FileFormats fileFormat) 56 : _instanceID(instanceID), 57 _fileFormat(fileFormat), 58 _moduleFile(MediaFile::CreateMediaFile(_instanceID)), 59 codec_info_(), 60 _amrFormat(AMRFileStorage), 61 _audioBuffer(), 62 _audioEncoder(instanceID), 63 _audioResampler() 64 { 65 } 66 67 FileRecorderImpl::~FileRecorderImpl() 68 { 69 MediaFile::DestroyMediaFile(_moduleFile); 70 } 71 72 FileFormats FileRecorderImpl::RecordingFileFormat() const 73 { 74 return _fileFormat; 75 } 76 77 int32_t FileRecorderImpl::RegisterModuleFileCallback( 78 FileCallback* callback) 79 { 80 if(_moduleFile == NULL) 81 { 82 return -1; 83 } 84 return _moduleFile->SetModuleFileCallback(callback); 85 } 86 87 int32_t FileRecorderImpl::StartRecordingAudioFile( 88 const char* fileName, 89 const CodecInst& codecInst, 90 uint32_t notificationTimeMs, 91 ACMAMRPackingFormat amrFormat) 92 { 93 if(_moduleFile == NULL) 94 { 95 return -1; 96 } 97 codec_info_ = codecInst; 98 _amrFormat = amrFormat; 99 100 int32_t retVal = 0; 101 if(_fileFormat != kFileFormatAviFile) 102 { 103 // AVI files should be started using StartRecordingVideoFile(..) all 104 // other formats should use this API. 105 retVal =_moduleFile->StartRecordingAudioFile(fileName, _fileFormat, 106 codecInst, 107 notificationTimeMs); 108 } 109 110 if( retVal == 0) 111 { 112 retVal = SetUpAudioEncoder(); 113 } 114 if( retVal != 0) 115 { 116 LOG(LS_WARNING) << "Failed to initialize file " << fileName 117 << " for recording."; 118 119 if(IsRecording()) 120 { 121 StopRecording(); 122 } 123 } 124 return retVal; 125 } 126 127 int32_t FileRecorderImpl::StartRecordingAudioFile( 128 OutStream& destStream, 129 const CodecInst& codecInst, 130 uint32_t notificationTimeMs, 131 ACMAMRPackingFormat amrFormat) 132 { 133 codec_info_ = codecInst; 134 _amrFormat = amrFormat; 135 136 int32_t retVal = _moduleFile->StartRecordingAudioStream( 137 destStream, 138 _fileFormat, 139 codecInst, 140 notificationTimeMs); 141 142 if( retVal == 0) 143 { 144 retVal = SetUpAudioEncoder(); 145 } 146 if( retVal != 0) 147 { 148 LOG(LS_WARNING) << "Failed to initialize outStream for recording."; 149 150 if(IsRecording()) 151 { 152 StopRecording(); 153 } 154 } 155 return retVal; 156 } 157 158 int32_t FileRecorderImpl::StopRecording() 159 { 160 memset(&codec_info_, 0, sizeof(CodecInst)); 161 return _moduleFile->StopRecording(); 162 } 163 164 bool FileRecorderImpl::IsRecording() const 165 { 166 return _moduleFile->IsRecording(); 167 } 168 169 int32_t FileRecorderImpl::RecordAudioToFile( 170 const AudioFrame& incomingAudioFrame, 171 const TickTime* playoutTS) 172 { 173 if (codec_info_.plfreq == 0) 174 { 175 LOG(LS_WARNING) << "RecordAudioToFile() recording audio is not " 176 << "turned on."; 177 return -1; 178 } 179 AudioFrame tempAudioFrame; 180 tempAudioFrame.samples_per_channel_ = 0; 181 if( incomingAudioFrame.num_channels_ == 2 && 182 !_moduleFile->IsStereo()) 183 { 184 // Recording mono but incoming audio is (interleaved) stereo. 185 tempAudioFrame.num_channels_ = 1; 186 tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_; 187 tempAudioFrame.samples_per_channel_ = 188 incomingAudioFrame.samples_per_channel_; 189 for (uint16_t i = 0; 190 i < (incomingAudioFrame.samples_per_channel_); i++) 191 { 192 // Sample value is the average of left and right buffer rounded to 193 // closest integer value. Note samples can be either 1 or 2 byte. 194 tempAudioFrame.data_[i] = 195 ((incomingAudioFrame.data_[2 * i] + 196 incomingAudioFrame.data_[(2 * i) + 1] + 1) >> 1); 197 } 198 } 199 else if( incomingAudioFrame.num_channels_ == 1 && 200 _moduleFile->IsStereo()) 201 { 202 // Recording stereo but incoming audio is mono. 203 tempAudioFrame.num_channels_ = 2; 204 tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_; 205 tempAudioFrame.samples_per_channel_ = 206 incomingAudioFrame.samples_per_channel_; 207 for (uint16_t i = 0; 208 i < (incomingAudioFrame.samples_per_channel_); i++) 209 { 210 // Duplicate sample to both channels 211 tempAudioFrame.data_[2*i] = 212 incomingAudioFrame.data_[i]; 213 tempAudioFrame.data_[2*i+1] = 214 incomingAudioFrame.data_[i]; 215 } 216 } 217 218 const AudioFrame* ptrAudioFrame = &incomingAudioFrame; 219 if(tempAudioFrame.samples_per_channel_ != 0) 220 { 221 // If ptrAudioFrame is not empty it contains the audio to be recorded. 222 ptrAudioFrame = &tempAudioFrame; 223 } 224 225 // Encode the audio data before writing to file. Don't encode if the codec 226 // is PCM. 227 // NOTE: stereo recording is only supported for WAV files. 228 // TODO (hellner): WAV expect PCM in little endian byte order. Not 229 // "encoding" with PCM coder should be a problem for big endian systems. 230 uint32_t encodedLenInBytes = 0; 231 if (_fileFormat == kFileFormatPreencodedFile || 232 STR_CASE_CMP(codec_info_.plname, "L16") != 0) 233 { 234 if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer, 235 encodedLenInBytes) == -1) 236 { 237 LOG(LS_WARNING) << "RecordAudioToFile() codec " 238 << codec_info_.plname 239 << " not supported or failed to encode stream."; 240 return -1; 241 } 242 } else { 243 int outLen = 0; 244 if(ptrAudioFrame->num_channels_ == 2) 245 { 246 // ptrAudioFrame contains interleaved stereo audio. 247 _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_, 248 codec_info_.plfreq, 249 kResamplerSynchronousStereo); 250 _audioResampler.Push(ptrAudioFrame->data_, 251 ptrAudioFrame->samples_per_channel_ * 252 ptrAudioFrame->num_channels_, 253 (int16_t*)_audioBuffer, 254 MAX_AUDIO_BUFFER_IN_BYTES, outLen); 255 } else { 256 _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_, 257 codec_info_.plfreq, 258 kResamplerSynchronous); 259 _audioResampler.Push(ptrAudioFrame->data_, 260 ptrAudioFrame->samples_per_channel_, 261 (int16_t*)_audioBuffer, 262 MAX_AUDIO_BUFFER_IN_BYTES, outLen); 263 } 264 encodedLenInBytes = outLen * sizeof(int16_t); 265 } 266 267 // Codec may not be operating at a frame rate of 10 ms. Whenever enough 268 // 10 ms chunks of data has been pushed to the encoder an encoded frame 269 // will be available. Wait until then. 270 if (encodedLenInBytes) 271 { 272 uint16_t msOfData = 273 ptrAudioFrame->samples_per_channel_ / 274 uint16_t(ptrAudioFrame->sample_rate_hz_ / 1000); 275 if (WriteEncodedAudioData(_audioBuffer, 276 (uint16_t)encodedLenInBytes, 277 msOfData, playoutTS) == -1) 278 { 279 return -1; 280 } 281 } 282 return 0; 283 } 284 285 int32_t FileRecorderImpl::SetUpAudioEncoder() 286 { 287 if (_fileFormat == kFileFormatPreencodedFile || 288 STR_CASE_CMP(codec_info_.plname, "L16") != 0) 289 { 290 if(_audioEncoder.SetEncodeCodec(codec_info_,_amrFormat) == -1) 291 { 292 LOG(LS_ERROR) << "SetUpAudioEncoder() codec " 293 << codec_info_.plname << " not supported."; 294 return -1; 295 } 296 } 297 return 0; 298 } 299 300 int32_t FileRecorderImpl::codec_info(CodecInst& codecInst) const 301 { 302 if(codec_info_.plfreq == 0) 303 { 304 return -1; 305 } 306 codecInst = codec_info_; 307 return 0; 308 } 309 310 int32_t FileRecorderImpl::WriteEncodedAudioData( 311 const int8_t* audioBuffer, 312 uint16_t bufferLength, 313 uint16_t /*millisecondsOfData*/, 314 const TickTime* /*playoutTS*/) 315 { 316 return _moduleFile->IncomingAudioData(audioBuffer, bufferLength); 317 } 318 319 320 #ifdef WEBRTC_MODULE_UTILITY_VIDEO 321 AviRecorder::AviRecorder(uint32_t instanceID, FileFormats fileFormat) 322 : FileRecorderImpl(instanceID, fileFormat), 323 _videoOnly(false), 324 _thread( 0), 325 _timeEvent(*EventWrapper::Create()), 326 _critSec(CriticalSectionWrapper::CreateCriticalSection()), 327 _writtenVideoFramesCounter(0), 328 _writtenAudioMS(0), 329 _writtenVideoMS(0) 330 { 331 _videoEncoder = new VideoCoder(); 332 _frameScaler = new FrameScaler(); 333 _videoFramesQueue = new VideoFramesQueue(); 334 _thread = ThreadWrapper::CreateThread(Run, this, kNormalPriority, 335 "AviRecorder()"); 336 } 337 338 AviRecorder::~AviRecorder( ) 339 { 340 StopRecording( ); 341 342 delete _videoEncoder; 343 delete _frameScaler; 344 delete _videoFramesQueue; 345 delete _thread; 346 delete &_timeEvent; 347 delete _critSec; 348 } 349 350 int32_t AviRecorder::StartRecordingVideoFile( 351 const char* fileName, 352 const CodecInst& audioCodecInst, 353 const VideoCodec& videoCodecInst, 354 ACMAMRPackingFormat amrFormat, 355 bool videoOnly) 356 { 357 _firstAudioFrameReceived = false; 358 _videoCodecInst = videoCodecInst; 359 _videoOnly = videoOnly; 360 361 if(_moduleFile->StartRecordingVideoFile(fileName, _fileFormat, 362 audioCodecInst, videoCodecInst, 363 videoOnly) != 0) 364 { 365 return -1; 366 } 367 368 if(!videoOnly) 369 { 370 if(FileRecorderImpl::StartRecordingAudioFile(fileName,audioCodecInst, 0, 371 amrFormat) !=0) 372 { 373 StopRecording(); 374 return -1; 375 } 376 } 377 if( SetUpVideoEncoder() != 0) 378 { 379 StopRecording(); 380 return -1; 381 } 382 if(_videoOnly) 383 { 384 // Writing to AVI file is non-blocking. 385 // Start non-blocking timer if video only. If recording both video and 386 // audio let the pushing of audio frames be the timer. 387 _timeEvent.StartTimer(true, 1000 / _videoCodecInst.maxFramerate); 388 } 389 StartThread(); 390 return 0; 391 } 392 393 int32_t AviRecorder::StopRecording() 394 { 395 _timeEvent.StopTimer(); 396 397 StopThread(); 398 return FileRecorderImpl::StopRecording(); 399 } 400 401 int32_t AviRecorder::CalcI420FrameSize( ) const 402 { 403 return 3 * _videoCodecInst.width * _videoCodecInst.height / 2; 404 } 405 406 int32_t AviRecorder::SetUpVideoEncoder() 407 { 408 // Size of unencoded data (I420) should be the largest possible frame size 409 // in a file. 410 _videoMaxPayloadSize = CalcI420FrameSize(); 411 _videoEncodedData.VerifyAndAllocate(_videoMaxPayloadSize); 412 413 _videoCodecInst.plType = _videoEncoder->DefaultPayloadType( 414 _videoCodecInst.plName); 415 416 int32_t useNumberOfCores = 1; 417 // Set the max payload size to 16000. This means that the codec will try to 418 // create slices that will fit in 16000 kByte packets. However, the 419 // Encode() call will still generate one full frame. 420 if(_videoEncoder->SetEncodeCodec(_videoCodecInst, useNumberOfCores, 421 16000)) 422 { 423 return -1; 424 } 425 return 0; 426 } 427 428 int32_t AviRecorder::RecordVideoToFile(const I420VideoFrame& videoFrame) 429 { 430 CriticalSectionScoped lock(_critSec); 431 if(!IsRecording() || videoFrame.IsZeroSize()) 432 { 433 return -1; 434 } 435 // The frame is written to file in AviRecorder::Process(). 436 int32_t retVal = _videoFramesQueue->AddFrame(videoFrame); 437 if(retVal != 0) 438 { 439 StopRecording(); 440 } 441 return retVal; 442 } 443 444 bool AviRecorder::StartThread() 445 { 446 unsigned int id; 447 if( _thread == 0) 448 { 449 return false; 450 } 451 452 return _thread->Start(id); 453 } 454 455 bool AviRecorder::StopThread() 456 { 457 _critSec->Enter(); 458 459 if(_thread) 460 { 461 _thread->SetNotAlive(); 462 463 ThreadWrapper* thread = _thread; 464 _thread = NULL; 465 466 _timeEvent.Set(); 467 468 _critSec->Leave(); 469 470 if(thread->Stop()) 471 { 472 delete thread; 473 } else { 474 return false; 475 } 476 } else { 477 _critSec->Leave(); 478 } 479 return true; 480 } 481 482 bool AviRecorder::Run( ThreadObj threadObj) 483 { 484 return static_cast<AviRecorder*>( threadObj)->Process(); 485 } 486 487 int32_t AviRecorder::ProcessAudio() 488 { 489 if (_writtenVideoFramesCounter == 0) 490 { 491 // Get the most recent frame that is due for writing to file. Since 492 // frames are unencoded it's safe to throw away frames if necessary 493 // for synchronizing audio and video. 494 I420VideoFrame* frameToProcess = _videoFramesQueue->FrameToRecord(); 495 if(frameToProcess) 496 { 497 // Syncronize audio to the current frame to process by throwing away 498 // audio samples with older timestamp than the video frame. 499 size_t numberOfAudioElements = 500 _audioFramesToWrite.size(); 501 for (size_t i = 0; i < numberOfAudioElements; ++i) 502 { 503 AudioFrameFileInfo* frameInfo = _audioFramesToWrite.front(); 504 if(TickTime::TicksToMilliseconds( 505 frameInfo->_playoutTS.Ticks()) < 506 frameToProcess->render_time_ms()) 507 { 508 delete frameInfo; 509 _audioFramesToWrite.pop_front(); 510 } else 511 { 512 break; 513 } 514 } 515 } 516 } 517 // Write all audio up to current timestamp. 518 int32_t error = 0; 519 size_t numberOfAudioElements = _audioFramesToWrite.size(); 520 for (size_t i = 0; i < numberOfAudioElements; ++i) 521 { 522 AudioFrameFileInfo* frameInfo = _audioFramesToWrite.front(); 523 if((TickTime::Now() - frameInfo->_playoutTS).Milliseconds() > 0) 524 { 525 _moduleFile->IncomingAudioData(frameInfo->_audioData, 526 frameInfo->_audioSize); 527 _writtenAudioMS += frameInfo->_audioMS; 528 delete frameInfo; 529 _audioFramesToWrite.pop_front(); 530 } else { 531 break; 532 } 533 } 534 return error; 535 } 536 537 bool AviRecorder::Process() 538 { 539 switch(_timeEvent.Wait(500)) 540 { 541 case kEventSignaled: 542 if(_thread == NULL) 543 { 544 return false; 545 } 546 break; 547 case kEventError: 548 return false; 549 case kEventTimeout: 550 // No events triggered. No work to do. 551 return true; 552 } 553 CriticalSectionScoped lock( _critSec); 554 555 // Get the most recent frame to write to file (if any). Synchronize it with 556 // the audio stream (if any). Synchronization the video based on its render 557 // timestamp (i.e. VideoFrame::RenderTimeMS()) 558 I420VideoFrame* frameToProcess = _videoFramesQueue->FrameToRecord(); 559 if( frameToProcess == NULL) 560 { 561 return true; 562 } 563 int32_t error = 0; 564 if(!_videoOnly) 565 { 566 if(!_firstAudioFrameReceived) 567 { 568 // Video and audio can only be synchronized if both have been 569 // received. 570 return true; 571 } 572 error = ProcessAudio(); 573 574 while (_writtenAudioMS > _writtenVideoMS) 575 { 576 error = EncodeAndWriteVideoToFile( *frameToProcess); 577 if( error != 0) 578 { 579 LOG(LS_ERROR) << "AviRecorder::Process() error writing to " 580 << "file."; 581 break; 582 } else { 583 uint32_t frameLengthMS = 1000 / 584 _videoCodecInst.maxFramerate; 585 _writtenVideoFramesCounter++; 586 _writtenVideoMS += frameLengthMS; 587 // A full seconds worth of frames have been written. 588 if(_writtenVideoFramesCounter%_videoCodecInst.maxFramerate == 0) 589 { 590 // Frame rate is in frames per seconds. Frame length is 591 // calculated as an integer division which means it may 592 // be rounded down. Compensate for this every second. 593 uint32_t rest = 1000 % frameLengthMS; 594 _writtenVideoMS += rest; 595 } 596 } 597 } 598 } else { 599 // Frame rate is in frames per seconds. Frame length is calculated as an 600 // integer division which means it may be rounded down. This introduces 601 // drift. Once a full frame worth of drift has happened, skip writing 602 // one frame. Note that frame rate is in frames per second so the 603 // drift is completely compensated for. 604 uint32_t frameLengthMS = 1000/_videoCodecInst.maxFramerate; 605 uint32_t restMS = 1000 % frameLengthMS; 606 uint32_t frameSkip = (_videoCodecInst.maxFramerate * 607 frameLengthMS) / restMS; 608 609 _writtenVideoFramesCounter++; 610 if(_writtenVideoFramesCounter % frameSkip == 0) 611 { 612 _writtenVideoMS += frameLengthMS; 613 return true; 614 } 615 616 error = EncodeAndWriteVideoToFile( *frameToProcess); 617 if(error != 0) 618 { 619 LOG(LS_ERROR) << "AviRecorder::Process() error writing to file."; 620 } else { 621 _writtenVideoMS += frameLengthMS; 622 } 623 } 624 return error == 0; 625 } 626 627 int32_t AviRecorder::EncodeAndWriteVideoToFile(I420VideoFrame& videoFrame) 628 { 629 if (!IsRecording() || videoFrame.IsZeroSize()) 630 { 631 return -1; 632 } 633 634 if(_frameScaler->ResizeFrameIfNeeded(&videoFrame, _videoCodecInst.width, 635 _videoCodecInst.height) != 0) 636 { 637 return -1; 638 } 639 640 _videoEncodedData.payloadSize = 0; 641 642 if( STR_CASE_CMP(_videoCodecInst.plName, "I420") == 0) 643 { 644 int length = CalcBufferSize(kI420, videoFrame.width(), 645 videoFrame.height()); 646 _videoEncodedData.VerifyAndAllocate(length); 647 648 // I420 is raw data. No encoding needed (each sample is represented by 649 // 1 byte so there is no difference depending on endianness). 650 int ret_length = ExtractBuffer(videoFrame, length, 651 _videoEncodedData.payloadData); 652 if (ret_length < 0) 653 return -1; 654 655 _videoEncodedData.payloadSize = ret_length; 656 _videoEncodedData.frameType = kVideoFrameKey; 657 }else { 658 if( _videoEncoder->Encode(videoFrame, _videoEncodedData) != 0) 659 { 660 return -1; 661 } 662 } 663 664 if(_videoEncodedData.payloadSize > 0) 665 { 666 if(_moduleFile->IncomingAVIVideoData( 667 (int8_t*)(_videoEncodedData.payloadData), 668 _videoEncodedData.payloadSize)) 669 { 670 LOG(LS_ERROR) << "Error writing AVI file."; 671 return -1; 672 } 673 } else { 674 LOG(LS_ERROR) << "FileRecorder::RecordVideoToFile() frame dropped by " 675 << "encoder, bitrate likely too low."; 676 } 677 return 0; 678 } 679 680 // Store audio frame in the _audioFramesToWrite buffer. The writing to file 681 // happens in AviRecorder::Process(). 682 int32_t AviRecorder::WriteEncodedAudioData( 683 const int8_t* audioBuffer, 684 uint16_t bufferLength, 685 uint16_t millisecondsOfData, 686 const TickTime* playoutTS) 687 { 688 CriticalSectionScoped lock(_critSec); 689 690 if (!IsRecording()) 691 { 692 return -1; 693 } 694 if (bufferLength > MAX_AUDIO_BUFFER_IN_BYTES) 695 { 696 return -1; 697 } 698 if (_videoOnly) 699 { 700 return -1; 701 } 702 if (_audioFramesToWrite.size() > kMaxAudioBufferQueueLength) 703 { 704 StopRecording(); 705 return -1; 706 } 707 _firstAudioFrameReceived = true; 708 709 if(playoutTS) 710 { 711 _audioFramesToWrite.push_back(new AudioFrameFileInfo(audioBuffer, 712 bufferLength, 713 millisecondsOfData, 714 *playoutTS)); 715 } else { 716 _audioFramesToWrite.push_back(new AudioFrameFileInfo(audioBuffer, 717 bufferLength, 718 millisecondsOfData, 719 TickTime::Now())); 720 } 721 _timeEvent.Set(); 722 return 0; 723 } 724 725 #endif // WEBRTC_MODULE_UTILITY_VIDEO 726 } // namespace webrtc 727