| Index: webrtc/modules/audio_mixer/audio_mixer.cc
|
| diff --git a/webrtc/modules/audio_mixer/audio_mixer.cc b/webrtc/modules/audio_mixer/audio_mixer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..cef29bfcdc04df91314287cc93d0731a4f84bdb5
|
| --- /dev/null
|
| +++ b/webrtc/modules/audio_mixer/audio_mixer.cc
|
| @@ -0,0 +1,450 @@
|
| +/*
|
| + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license
|
| + * that can be found in the LICENSE file in the root of the source
|
| + * tree. An additional intellectual property rights grant can be found
|
| + * in the file PATENTS. All contributing project authors may
|
| + * be found in the AUTHORS file in the root of the source tree.
|
| + */
|
| +
|
| +#include "webrtc/modules/audio_mixer/audio_mixer.h"
|
| +
|
| +#include "webrtc/base/format_macros.h"
|
| +#include "webrtc/modules/audio_processing/include/audio_processing.h"
|
| +#include "webrtc/modules/utility/include/audio_frame_operations.h"
|
| +#include "webrtc/system_wrappers/include/file_wrapper.h"
|
| +#include "webrtc/system_wrappers/include/trace.h"
|
| +#include "webrtc/voice_engine/include/voe_external_media.h"
|
| +#include "webrtc/voice_engine/statistics.h"
|
| +#include "webrtc/voice_engine/utility.h"
|
| +
|
| +namespace webrtc {
|
| +namespace voe {
|
| +
|
| +void AudioMixer::NewMixedAudio(int32_t id,
|
| + const AudioFrame& generalAudioFrame,
|
| + const AudioFrame** uniqueAudioFrames,
|
| + uint32_t size) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::NewMixedAudio(id=%d, size=%u)", id, size);
|
| +
|
| + _audioFrame.CopyFrom(generalAudioFrame);
|
| + _audioFrame.id_ = id;
|
| +}
|
| +
|
| +void AudioMixer::PlayNotification(int32_t id, uint32_t durationMs) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::PlayNotification(id=%d, durationMs=%d)", id,
|
| + durationMs);
|
| + // Not implement yet
|
| +}
|
| +
|
| +void AudioMixer::RecordNotification(int32_t id, uint32_t durationMs) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::RecordNotification(id=%d, durationMs=%d)", id,
|
| + durationMs);
|
| +
|
| + // Not implement yet
|
| +}
|
| +
|
| +void AudioMixer::PlayFileEnded(int32_t id) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::PlayFileEnded(id=%d)", id);
|
| +
|
| + // not needed
|
| +}
|
| +
|
| +void AudioMixer::RecordFileEnded(int32_t id) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::RecordFileEnded(id=%d)", id);
|
| + assert(id == _instanceId);
|
| +
|
| + rtc::CritScope cs(&_fileCritSect);
|
| + _outputFileRecording = false;
|
| + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::RecordFileEnded() =>"
|
| + "output file recorder module is shutdown");
|
| +}
|
| +
|
| +int32_t AudioMixer::Create(AudioMixer*& mixer, uint32_t instanceId) {
|
| + WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
|
| + "AudioMixer::Create(instanceId=%d)", instanceId);
|
| + mixer = new AudioMixer(instanceId);
|
| + if (mixer == NULL) {
|
| + WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
|
| + "AudioMixer::Create() unable to allocate memory for"
|
| + "mixer");
|
| + return -1;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +AudioMixer::AudioMixer(uint32_t instanceId)
|
| + : _mixerModule(*NewAudioConferenceMixer::Create(instanceId)),
|
| + _audioLevel(),
|
| + _instanceId(instanceId),
|
| + _externalMediaCallbackPtr(NULL),
|
| + _externalMedia(false),
|
| + _panLeft(1.0f),
|
| + _panRight(1.0f),
|
| + _mixingFrequencyHz(8000),
|
| + _outputFileRecorderPtr(NULL),
|
| + _outputFileRecording(false) {
|
| + WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::AudioMixer() - ctor");
|
| +
|
| + if (_mixerModule.RegisterMixedStreamCallback(this) == -1) {
|
| + WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::AudioMixer() failed to register mixer"
|
| + "callbacks");
|
| + }
|
| +}
|
| +
|
| +void AudioMixer::Destroy(AudioMixer*& mixer) {
|
| + if (mixer) {
|
| + delete mixer;
|
| + mixer = NULL;
|
| + }
|
| +}
|
| +
|
| +AudioMixer::~AudioMixer() {
|
| + WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::~AudioMixer() - dtor");
|
| + if (_externalMedia) {
|
| + DeRegisterExternalMediaProcessing();
|
| + }
|
| + {
|
| + rtc::CritScope cs(&_fileCritSect);
|
| + if (_outputFileRecorderPtr) {
|
| + _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
| + _outputFileRecorderPtr->StopRecording();
|
| + FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
| + _outputFileRecorderPtr = NULL;
|
| + }
|
| + }
|
| + _mixerModule.UnRegisterMixedStreamCallback();
|
| + delete &_mixerModule;
|
| +}
|
| +
|
| +int32_t AudioMixer::SetEngineInformation(voe::Statistics& engineStatistics) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::SetEngineInformation()");
|
| + _engineStatisticsPtr = &engineStatistics;
|
| + return 0;
|
| +}
|
| +
|
| +int32_t AudioMixer::SetAudioProcessingModule(
|
| + AudioProcessing* audioProcessingModule) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::SetAudioProcessingModule("
|
| + "audioProcessingModule=0x%x)",
|
| + audioProcessingModule);
|
| + _audioProcessingModulePtr = audioProcessingModule;
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::RegisterExternalMediaProcessing(
|
| + VoEMediaProcess& proccess_object) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::RegisterExternalMediaProcessing()");
|
| +
|
| + rtc::CritScope cs(&_callbackCritSect);
|
| + _externalMediaCallbackPtr = &proccess_object;
|
| + _externalMedia = true;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::DeRegisterExternalMediaProcessing() {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::DeRegisterExternalMediaProcessing()");
|
| +
|
| + rtc::CritScope cs(&_callbackCritSect);
|
| + _externalMedia = false;
|
| + _externalMediaCallbackPtr = NULL;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int32_t AudioMixer::SetMixabilityStatus(MixerAudioSource& participant,
|
| + bool mixable) {
|
| + return _mixerModule.SetMixabilityStatus(&participant, mixable);
|
| +}
|
| +
|
| +int32_t AudioMixer::SetAnonymousMixabilityStatus(MixerAudioSource& participant,
|
| + bool mixable) {
|
| + return _mixerModule.SetAnonymousMixabilityStatus(&participant, mixable);
|
| +}
|
| +
|
| +int32_t AudioMixer::MixActiveChannels() {
|
| + _mixerModule.Process();
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::GetSpeechOutputLevel(uint32_t& level) {
|
| + int8_t currentLevel = _audioLevel.Level();
|
| + level = static_cast<uint32_t>(currentLevel);
|
| + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "GetSpeechOutputLevel() => level=%u", level);
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::GetSpeechOutputLevelFullRange(uint32_t& level) {
|
| + int16_t currentLevel = _audioLevel.LevelFullRange();
|
| + level = static_cast<uint32_t>(currentLevel);
|
| + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "GetSpeechOutputLevelFullRange() => level=%u", level);
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::SetOutputVolumePan(float left, float right) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::SetOutputVolumePan()");
|
| + _panLeft = left;
|
| + _panRight = right;
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::GetOutputVolumePan(float& left, float& right) {
|
| + left = _panLeft;
|
| + right = _panRight;
|
| + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "GetOutputVolumePan() => left=%2.1f, right=%2.1f", left, right);
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::StartRecordingPlayout(const char* fileName,
|
| + const CodecInst* codecInst) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::StartRecordingPlayout(fileName=%s)", fileName);
|
| +
|
| + if (_outputFileRecording) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
|
| + "StartRecordingPlayout() is already recording");
|
| + return 0;
|
| + }
|
| +
|
| + FileFormats format;
|
| + const uint32_t notificationTime(0);
|
| + CodecInst dummyCodec = {100, "L16", 16000, 320, 1, 320000};
|
| +
|
| + if ((codecInst != NULL) &&
|
| + ((codecInst->channels < 1) || (codecInst->channels > 2))) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_BAD_ARGUMENT, kTraceError,
|
| + "StartRecordingPlayout() invalid compression");
|
| + return (-1);
|
| + }
|
| + if (codecInst == NULL) {
|
| + format = kFileFormatPcm16kHzFile;
|
| + codecInst = &dummyCodec;
|
| + } else if ((STR_CASE_CMP(codecInst->plname, "L16") == 0) ||
|
| + (STR_CASE_CMP(codecInst->plname, "PCMU") == 0) ||
|
| + (STR_CASE_CMP(codecInst->plname, "PCMA") == 0)) {
|
| + format = kFileFormatWavFile;
|
| + } else {
|
| + format = kFileFormatCompressedFile;
|
| + }
|
| +
|
| + rtc::CritScope cs(&_fileCritSect);
|
| +
|
| + // Destroy the old instance
|
| + if (_outputFileRecorderPtr) {
|
| + _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
| + FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
| + _outputFileRecorderPtr = NULL;
|
| + }
|
| +
|
| + _outputFileRecorderPtr =
|
| + FileRecorder::CreateFileRecorder(_instanceId, (const FileFormats)format);
|
| + if (_outputFileRecorderPtr == NULL) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_INVALID_ARGUMENT, kTraceError,
|
| + "StartRecordingPlayout() fileRecorder format isnot correct");
|
| + return -1;
|
| + }
|
| +
|
| + if (_outputFileRecorderPtr->StartRecordingAudioFile(
|
| + fileName, (const CodecInst&)*codecInst, notificationTime) != 0) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_BAD_FILE, kTraceError,
|
| + "StartRecordingAudioFile() failed to start file recording");
|
| + _outputFileRecorderPtr->StopRecording();
|
| + FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
| + _outputFileRecorderPtr = NULL;
|
| + return -1;
|
| + }
|
| + _outputFileRecorderPtr->RegisterModuleFileCallback(this);
|
| + _outputFileRecording = true;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::StartRecordingPlayout(OutStream* stream,
|
| + const CodecInst* codecInst) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::StartRecordingPlayout()");
|
| +
|
| + if (_outputFileRecording) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
|
| + "StartRecordingPlayout() is already recording");
|
| + return 0;
|
| + }
|
| +
|
| + FileFormats format;
|
| + const uint32_t notificationTime(0);
|
| + CodecInst dummyCodec = {100, "L16", 16000, 320, 1, 320000};
|
| +
|
| + if (codecInst != NULL && codecInst->channels != 1) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_BAD_ARGUMENT, kTraceError,
|
| + "StartRecordingPlayout() invalid compression");
|
| + return (-1);
|
| + }
|
| + if (codecInst == NULL) {
|
| + format = kFileFormatPcm16kHzFile;
|
| + codecInst = &dummyCodec;
|
| + } else if ((STR_CASE_CMP(codecInst->plname, "L16") == 0) ||
|
| + (STR_CASE_CMP(codecInst->plname, "PCMU") == 0) ||
|
| + (STR_CASE_CMP(codecInst->plname, "PCMA") == 0)) {
|
| + format = kFileFormatWavFile;
|
| + } else {
|
| + format = kFileFormatCompressedFile;
|
| + }
|
| +
|
| + rtc::CritScope cs(&_fileCritSect);
|
| +
|
| + // Destroy the old instance
|
| + if (_outputFileRecorderPtr) {
|
| + _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
| + FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
| + _outputFileRecorderPtr = NULL;
|
| + }
|
| +
|
| + _outputFileRecorderPtr =
|
| + FileRecorder::CreateFileRecorder(_instanceId, (const FileFormats)format);
|
| + if (_outputFileRecorderPtr == NULL) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_INVALID_ARGUMENT, kTraceError,
|
| + "StartRecordingPlayout() fileRecorder format isnot correct");
|
| + return -1;
|
| + }
|
| +
|
| + if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream, *codecInst,
|
| + notificationTime) != 0) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_BAD_FILE, kTraceError,
|
| + "StartRecordingAudioFile() failed to start file recording");
|
| + _outputFileRecorderPtr->StopRecording();
|
| + FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
| + _outputFileRecorderPtr = NULL;
|
| + return -1;
|
| + }
|
| +
|
| + _outputFileRecorderPtr->RegisterModuleFileCallback(this);
|
| + _outputFileRecording = true;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::StopRecordingPlayout() {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::StopRecordingPlayout()");
|
| +
|
| + if (!_outputFileRecording) {
|
| + WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
|
| + "StopRecordingPlayout() file isnot recording");
|
| + return -1;
|
| + }
|
| +
|
| + rtc::CritScope cs(&_fileCritSect);
|
| +
|
| + if (_outputFileRecorderPtr->StopRecording() != 0) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_STOP_RECORDING_FAILED, kTraceError,
|
| + "StopRecording(), could not stop recording");
|
| + return -1;
|
| + }
|
| + _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
| + FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
| + _outputFileRecorderPtr = NULL;
|
| + _outputFileRecording = false;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int AudioMixer::GetMixedAudio(int sample_rate_hz,
|
| + size_t num_channels,
|
| + AudioFrame* frame) {
|
| + WEBRTC_TRACE(
|
| + kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%" PRIuS ")",
|
| + sample_rate_hz, num_channels);
|
| +
|
| + // --- Record playout if enabled
|
| + {
|
| + rtc::CritScope cs(&_fileCritSect);
|
| + if (_outputFileRecording && _outputFileRecorderPtr)
|
| + _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
|
| + }
|
| +
|
| + frame->num_channels_ = num_channels;
|
| + frame->sample_rate_hz_ = sample_rate_hz;
|
| + // TODO(andrew): Ideally the downmixing would occur much earlier, in
|
| + // AudioCodingModule.
|
| + RemixAndResample(_audioFrame, &resampler_, frame);
|
| + return 0;
|
| +}
|
| +
|
| +int32_t AudioMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm) {
|
| + if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz) {
|
| + WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioMixer::DoOperationsOnCombinedSignal() => "
|
| + "mixing frequency = %d",
|
| + _audioFrame.sample_rate_hz_);
|
| + _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
|
| + }
|
| +
|
| + // Scale left and/or right channel(s) if balance is active
|
| + if (_panLeft != 1.0 || _panRight != 1.0) {
|
| + if (_audioFrame.num_channels_ == 1) {
|
| + AudioFrameOperations::MonoToStereo(&_audioFrame);
|
| + } else {
|
| + // Pure stereo mode (we are receiving a stereo signal).
|
| + }
|
| +
|
| + assert(_audioFrame.num_channels_ == 2);
|
| + AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame);
|
| + }
|
| +
|
| + // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
|
| + if (feed_data_to_apm) {
|
| + if (_audioProcessingModulePtr->ProcessReverseStream(&_audioFrame) != 0) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
|
| + "AudioProcessingModule::ProcessReverseStream() => error");
|
| + RTC_DCHECK(false);
|
| + }
|
| + }
|
| +
|
| + // --- External media processing
|
| + {
|
| + rtc::CritScope cs(&_callbackCritSect);
|
| + if (_externalMedia) {
|
| + const bool is_stereo = (_audioFrame.num_channels_ == 2);
|
| + if (_externalMediaCallbackPtr) {
|
| + _externalMediaCallbackPtr->Process(
|
| + -1, kPlaybackAllChannelsMixed, (int16_t*)_audioFrame.data_,
|
| + _audioFrame.samples_per_channel_, _audioFrame.sample_rate_hz_,
|
| + is_stereo);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // --- Measure audio level (0-9) for the combined signal
|
| + _audioLevel.ComputeLevel(_audioFrame);
|
| +
|
| + return 0;
|
| +}
|
| +} // namespace voe
|
| +} // namespace webrtc
|
|
|