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/voice_engine/output_mixer.h" 12 13 #include "webrtc/base/format_macros.h" 14 #include "webrtc/modules/audio_processing/include/audio_processing.h" 15 #include "webrtc/modules/utility/include/audio_frame_operations.h" 16 #include "webrtc/system_wrappers/include/critical_section_wrapper.h" 17 #include "webrtc/system_wrappers/include/file_wrapper.h" 18 #include "webrtc/system_wrappers/include/trace.h" 19 #include "webrtc/voice_engine/include/voe_external_media.h" 20 #include "webrtc/voice_engine/statistics.h" 21 #include "webrtc/voice_engine/utility.h" 22 23 namespace webrtc { 24 namespace voe { 25 26 void 27 OutputMixer::NewMixedAudio(int32_t id, 28 const AudioFrame& generalAudioFrame, 29 const AudioFrame** uniqueAudioFrames, 30 uint32_t size) 31 { 32 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 33 "OutputMixer::NewMixedAudio(id=%d, size=%u)", id, size); 34 35 _audioFrame.CopyFrom(generalAudioFrame); 36 _audioFrame.id_ = id; 37 } 38 39 void OutputMixer::PlayNotification(int32_t id, uint32_t durationMs) 40 { 41 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 42 "OutputMixer::PlayNotification(id=%d, durationMs=%d)", 43 id, durationMs); 44 // Not implement yet 45 } 46 47 void OutputMixer::RecordNotification(int32_t id, 48 uint32_t durationMs) 49 { 50 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 51 "OutputMixer::RecordNotification(id=%d, durationMs=%d)", 52 id, durationMs); 53 54 // Not implement yet 55 } 56 57 void OutputMixer::PlayFileEnded(int32_t id) 58 { 59 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 60 "OutputMixer::PlayFileEnded(id=%d)", id); 61 62 // not needed 63 } 64 65 void OutputMixer::RecordFileEnded(int32_t id) 66 { 67 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 68 "OutputMixer::RecordFileEnded(id=%d)", id); 69 assert(id == _instanceId); 70 71 CriticalSectionScoped cs(&_fileCritSect); 72 _outputFileRecording = false; 73 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), 74 "OutputMixer::RecordFileEnded() =>" 75 "output file recorder module is shutdown"); 76 } 77 78 int32_t 79 OutputMixer::Create(OutputMixer*& mixer, uint32_t instanceId) 80 { 81 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId, 82 "OutputMixer::Create(instanceId=%d)", instanceId); 83 mixer = new OutputMixer(instanceId); 84 if (mixer == NULL) 85 { 86 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId, 87 "OutputMixer::Create() unable to allocate memory for" 88 "mixer"); 89 return -1; 90 } 91 return 0; 92 } 93 94 OutputMixer::OutputMixer(uint32_t instanceId) : 95 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()), 96 _fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()), 97 _mixerModule(*AudioConferenceMixer::Create(instanceId)), 98 _audioLevel(), 99 _dtmfGenerator(instanceId), 100 _instanceId(instanceId), 101 _externalMediaCallbackPtr(NULL), 102 _externalMedia(false), 103 _panLeft(1.0f), 104 _panRight(1.0f), 105 _mixingFrequencyHz(8000), 106 _outputFileRecorderPtr(NULL), 107 _outputFileRecording(false) 108 { 109 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1), 110 "OutputMixer::OutputMixer() - ctor"); 111 112 if (_mixerModule.RegisterMixedStreamCallback(this) == -1) 113 { 114 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1), 115 "OutputMixer::OutputMixer() failed to register mixer" 116 "callbacks"); 117 } 118 119 _dtmfGenerator.Init(); 120 } 121 122 void 123 OutputMixer::Destroy(OutputMixer*& mixer) 124 { 125 if (mixer) 126 { 127 delete mixer; 128 mixer = NULL; 129 } 130 } 131 132 OutputMixer::~OutputMixer() 133 { 134 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1), 135 "OutputMixer::~OutputMixer() - dtor"); 136 if (_externalMedia) 137 { 138 DeRegisterExternalMediaProcessing(); 139 } 140 { 141 CriticalSectionScoped cs(&_fileCritSect); 142 if (_outputFileRecorderPtr) 143 { 144 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL); 145 _outputFileRecorderPtr->StopRecording(); 146 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr); 147 _outputFileRecorderPtr = NULL; 148 } 149 } 150 _mixerModule.UnRegisterMixedStreamCallback(); 151 delete &_mixerModule; 152 delete &_callbackCritSect; 153 delete &_fileCritSect; 154 } 155 156 int32_t 157 OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics) 158 { 159 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 160 "OutputMixer::SetEngineInformation()"); 161 _engineStatisticsPtr = &engineStatistics; 162 return 0; 163 } 164 165 int32_t 166 OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule) 167 { 168 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 169 "OutputMixer::SetAudioProcessingModule(" 170 "audioProcessingModule=0x%x)", audioProcessingModule); 171 _audioProcessingModulePtr = audioProcessingModule; 172 return 0; 173 } 174 175 int OutputMixer::RegisterExternalMediaProcessing( 176 VoEMediaProcess& proccess_object) 177 { 178 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 179 "OutputMixer::RegisterExternalMediaProcessing()"); 180 181 CriticalSectionScoped cs(&_callbackCritSect); 182 _externalMediaCallbackPtr = &proccess_object; 183 _externalMedia = true; 184 185 return 0; 186 } 187 188 int OutputMixer::DeRegisterExternalMediaProcessing() 189 { 190 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 191 "OutputMixer::DeRegisterExternalMediaProcessing()"); 192 193 CriticalSectionScoped cs(&_callbackCritSect); 194 _externalMedia = false; 195 _externalMediaCallbackPtr = NULL; 196 197 return 0; 198 } 199 200 int OutputMixer::PlayDtmfTone(uint8_t eventCode, int lengthMs, 201 int attenuationDb) 202 { 203 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), 204 "OutputMixer::PlayDtmfTone()"); 205 if (_dtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb) != 0) 206 { 207 _engineStatisticsPtr->SetLastError(VE_STILL_PLAYING_PREV_DTMF, 208 kTraceError, 209 "OutputMixer::PlayDtmfTone()"); 210 return -1; 211 } 212 return 0; 213 } 214 215 int32_t 216 OutputMixer::SetMixabilityStatus(MixerParticipant& participant, 217 bool mixable) 218 { 219 return _mixerModule.SetMixabilityStatus(&participant, mixable); 220 } 221 222 int32_t 223 OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant, 224 bool mixable) 225 { 226 return _mixerModule.SetAnonymousMixabilityStatus(&participant, mixable); 227 } 228 229 int32_t 230 OutputMixer::MixActiveChannels() 231 { 232 return _mixerModule.Process(); 233 } 234 235 int 236 OutputMixer::GetSpeechOutputLevel(uint32_t& level) 237 { 238 int8_t currentLevel = _audioLevel.Level(); 239 level = static_cast<uint32_t> (currentLevel); 240 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), 241 "GetSpeechOutputLevel() => level=%u", level); 242 return 0; 243 } 244 245 int 246 OutputMixer::GetSpeechOutputLevelFullRange(uint32_t& level) 247 { 248 int16_t currentLevel = _audioLevel.LevelFullRange(); 249 level = static_cast<uint32_t> (currentLevel); 250 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), 251 "GetSpeechOutputLevelFullRange() => level=%u", level); 252 return 0; 253 } 254 255 int 256 OutputMixer::SetOutputVolumePan(float left, float right) 257 { 258 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 259 "OutputMixer::SetOutputVolumePan()"); 260 _panLeft = left; 261 _panRight = right; 262 return 0; 263 } 264 265 int 266 OutputMixer::GetOutputVolumePan(float& left, float& right) 267 { 268 left = _panLeft; 269 right = _panRight; 270 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1), 271 "GetOutputVolumePan() => left=%2.1f, right=%2.1f", 272 left, right); 273 return 0; 274 } 275 276 int OutputMixer::StartRecordingPlayout(const char* fileName, 277 const CodecInst* codecInst) 278 { 279 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 280 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName); 281 282 if (_outputFileRecording) 283 { 284 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1), 285 "StartRecordingPlayout() is already recording"); 286 return 0; 287 } 288 289 FileFormats format; 290 const uint32_t notificationTime(0); 291 CodecInst dummyCodec={100,"L16",16000,320,1,320000}; 292 293 if ((codecInst != NULL) && 294 ((codecInst->channels < 1) || (codecInst->channels > 2))) 295 { 296 _engineStatisticsPtr->SetLastError( 297 VE_BAD_ARGUMENT, kTraceError, 298 "StartRecordingPlayout() invalid compression"); 299 return(-1); 300 } 301 if(codecInst == NULL) 302 { 303 format = kFileFormatPcm16kHzFile; 304 codecInst=&dummyCodec; 305 } 306 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) || 307 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) || 308 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0)) 309 { 310 format = kFileFormatWavFile; 311 } 312 else 313 { 314 format = kFileFormatCompressedFile; 315 } 316 317 CriticalSectionScoped cs(&_fileCritSect); 318 319 // Destroy the old instance 320 if (_outputFileRecorderPtr) 321 { 322 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL); 323 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr); 324 _outputFileRecorderPtr = NULL; 325 } 326 327 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder( 328 _instanceId, 329 (const FileFormats)format); 330 if (_outputFileRecorderPtr == NULL) 331 { 332 _engineStatisticsPtr->SetLastError( 333 VE_INVALID_ARGUMENT, kTraceError, 334 "StartRecordingPlayout() fileRecorder format isnot correct"); 335 return -1; 336 } 337 338 if (_outputFileRecorderPtr->StartRecordingAudioFile( 339 fileName, 340 (const CodecInst&)*codecInst, 341 notificationTime) != 0) 342 { 343 _engineStatisticsPtr->SetLastError( 344 VE_BAD_FILE, kTraceError, 345 "StartRecordingAudioFile() failed to start file recording"); 346 _outputFileRecorderPtr->StopRecording(); 347 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr); 348 _outputFileRecorderPtr = NULL; 349 return -1; 350 } 351 _outputFileRecorderPtr->RegisterModuleFileCallback(this); 352 _outputFileRecording = true; 353 354 return 0; 355 } 356 357 int OutputMixer::StartRecordingPlayout(OutStream* stream, 358 const CodecInst* codecInst) 359 { 360 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 361 "OutputMixer::StartRecordingPlayout()"); 362 363 if (_outputFileRecording) 364 { 365 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1), 366 "StartRecordingPlayout() is already recording"); 367 return 0; 368 } 369 370 FileFormats format; 371 const uint32_t notificationTime(0); 372 CodecInst dummyCodec={100,"L16",16000,320,1,320000}; 373 374 if (codecInst != NULL && codecInst->channels != 1) 375 { 376 _engineStatisticsPtr->SetLastError( 377 VE_BAD_ARGUMENT, kTraceError, 378 "StartRecordingPlayout() invalid compression"); 379 return(-1); 380 } 381 if(codecInst == NULL) 382 { 383 format = kFileFormatPcm16kHzFile; 384 codecInst=&dummyCodec; 385 } 386 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) || 387 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) || 388 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0)) 389 { 390 format = kFileFormatWavFile; 391 } 392 else 393 { 394 format = kFileFormatCompressedFile; 395 } 396 397 CriticalSectionScoped cs(&_fileCritSect); 398 399 // Destroy the old instance 400 if (_outputFileRecorderPtr) 401 { 402 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL); 403 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr); 404 _outputFileRecorderPtr = NULL; 405 } 406 407 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder( 408 _instanceId, 409 (const FileFormats)format); 410 if (_outputFileRecorderPtr == NULL) 411 { 412 _engineStatisticsPtr->SetLastError( 413 VE_INVALID_ARGUMENT, kTraceError, 414 "StartRecordingPlayout() fileRecorder format isnot correct"); 415 return -1; 416 } 417 418 if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream, 419 *codecInst, 420 notificationTime) != 0) 421 { 422 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError, 423 "StartRecordingAudioFile() failed to start file recording"); 424 _outputFileRecorderPtr->StopRecording(); 425 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr); 426 _outputFileRecorderPtr = NULL; 427 return -1; 428 } 429 430 _outputFileRecorderPtr->RegisterModuleFileCallback(this); 431 _outputFileRecording = true; 432 433 return 0; 434 } 435 436 int OutputMixer::StopRecordingPlayout() 437 { 438 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1), 439 "OutputMixer::StopRecordingPlayout()"); 440 441 if (!_outputFileRecording) 442 { 443 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1), 444 "StopRecordingPlayout() file isnot recording"); 445 return -1; 446 } 447 448 CriticalSectionScoped cs(&_fileCritSect); 449 450 if (_outputFileRecorderPtr->StopRecording() != 0) 451 { 452 _engineStatisticsPtr->SetLastError( 453 VE_STOP_RECORDING_FAILED, kTraceError, 454 "StopRecording(), could not stop recording"); 455 return -1; 456 } 457 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL); 458 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr); 459 _outputFileRecorderPtr = NULL; 460 _outputFileRecording = false; 461 462 return 0; 463 } 464 465 int OutputMixer::GetMixedAudio(int sample_rate_hz, 466 size_t num_channels, 467 AudioFrame* frame) { 468 WEBRTC_TRACE( 469 kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 470 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%" PRIuS ")", 471 sample_rate_hz, num_channels); 472 473 // --- Record playout if enabled 474 { 475 CriticalSectionScoped cs(&_fileCritSect); 476 if (_outputFileRecording && _outputFileRecorderPtr) 477 _outputFileRecorderPtr->RecordAudioToFile(_audioFrame); 478 } 479 480 frame->num_channels_ = num_channels; 481 frame->sample_rate_hz_ = sample_rate_hz; 482 // TODO(andrew): Ideally the downmixing would occur much earlier, in 483 // AudioCodingModule. 484 RemixAndResample(_audioFrame, &resampler_, frame); 485 return 0; 486 } 487 488 int32_t 489 OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm) 490 { 491 if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz) 492 { 493 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1), 494 "OutputMixer::DoOperationsOnCombinedSignal() => " 495 "mixing frequency = %d", _audioFrame.sample_rate_hz_); 496 _mixingFrequencyHz = _audioFrame.sample_rate_hz_; 497 } 498 499 // --- Insert inband Dtmf tone 500 if (_dtmfGenerator.IsAddingTone()) 501 { 502 InsertInbandDtmfTone(); 503 } 504 505 // Scale left and/or right channel(s) if balance is active 506 if (_panLeft != 1.0 || _panRight != 1.0) 507 { 508 if (_audioFrame.num_channels_ == 1) 509 { 510 AudioFrameOperations::MonoToStereo(&_audioFrame); 511 } 512 else 513 { 514 // Pure stereo mode (we are receiving a stereo signal). 515 } 516 517 assert(_audioFrame.num_channels_ == 2); 518 AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame); 519 } 520 521 // --- Far-end Voice Quality Enhancement (AudioProcessing Module) 522 if (feed_data_to_apm) { 523 // Convert from mixing to AudioProcessing sample rate, similarly to how it 524 // is done on the send side. Downmix to mono. 525 AudioFrame frame; 526 frame.num_channels_ = 1; 527 frame.sample_rate_hz_ = _audioProcessingModulePtr->input_sample_rate_hz(); 528 RemixAndResample(_audioFrame, &audioproc_resampler_, &frame); 529 530 if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) != 0) { 531 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), 532 "AudioProcessingModule::AnalyzeReverseStream() => error"); 533 RTC_DCHECK(false); 534 } 535 } 536 537 // --- External media processing 538 { 539 CriticalSectionScoped cs(&_callbackCritSect); 540 if (_externalMedia) 541 { 542 const bool is_stereo = (_audioFrame.num_channels_ == 2); 543 if (_externalMediaCallbackPtr) 544 { 545 _externalMediaCallbackPtr->Process( 546 -1, 547 kPlaybackAllChannelsMixed, 548 (int16_t*)_audioFrame.data_, 549 _audioFrame.samples_per_channel_, 550 _audioFrame.sample_rate_hz_, 551 is_stereo); 552 } 553 } 554 } 555 556 // --- Measure audio level (0-9) for the combined signal 557 _audioLevel.ComputeLevel(_audioFrame); 558 559 return 0; 560 } 561 562 // ---------------------------------------------------------------------------- 563 // Private methods 564 // ---------------------------------------------------------------------------- 565 566 int 567 OutputMixer::InsertInbandDtmfTone() 568 { 569 uint16_t sampleRate(0); 570 _dtmfGenerator.GetSampleRate(sampleRate); 571 if (sampleRate != _audioFrame.sample_rate_hz_) 572 { 573 // Update sample rate of Dtmf tone since the mixing frequency changed. 574 _dtmfGenerator.SetSampleRate( 575 (uint16_t)(_audioFrame.sample_rate_hz_)); 576 // Reset the tone to be added taking the new sample rate into account. 577 _dtmfGenerator.ResetTone(); 578 } 579 580 int16_t toneBuffer[320]; 581 uint16_t toneSamples(0); 582 if (_dtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1) 583 { 584 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), 585 "OutputMixer::InsertInbandDtmfTone() inserting Dtmf" 586 "tone failed"); 587 return -1; 588 } 589 590 // replace mixed audio with Dtmf tone 591 if (_audioFrame.num_channels_ == 1) 592 { 593 // mono 594 memcpy(_audioFrame.data_, toneBuffer, sizeof(int16_t) 595 * toneSamples); 596 } else 597 { 598 // stereo 599 for (size_t i = 0; i < _audioFrame.samples_per_channel_; i++) 600 { 601 _audioFrame.data_[2 * i] = toneBuffer[i]; 602 _audioFrame.data_[2 * i + 1] = 0; 603 } 604 } 605 assert(_audioFrame.samples_per_channel_ == toneSamples); 606 607 return 0; 608 } 609 610 } // namespace voe 611 } // namespace webrtc 612