зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1397793 - Move to APM - Part 1 - UpdateSingleSource. r=pehrsons
This part is about setting on/off audio processing feature. It's long, but it's mostly mechanichal changes, from the old API to the new one. This also covers reseting the processing in case of device changes (with macros). MozReview-Commit-ID: EI2TxHRicEr --HG-- extra : rebase_source : 5c389e00019633d371d74cdd2d881dab4d353848 extra : source : 2c7a56648de9125ae1893d54ec011b6cbb181d86
This commit is contained in:
Родитель
17c1d145ae
Коммит
49545441d4
|
@ -513,9 +513,10 @@ private:
|
|||
// Note: shared across all microphone sources
|
||||
static int sChannelsOpen;
|
||||
|
||||
const UniquePtr<webrtc::AudioProcessing> mAudioProcessing;
|
||||
|
||||
// accessed from the GraphDriver thread except for deletion
|
||||
nsAutoPtr<AudioPacketizer<AudioDataValue, int16_t>> mPacketizer;
|
||||
nsAutoPtr<AudioPacketizer<AudioDataValue, AudioDataValue>> mPacketizer;
|
||||
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERenderListener;
|
||||
|
||||
// mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and
|
||||
|
@ -539,8 +540,6 @@ private:
|
|||
uint64_t mTotalFrames;
|
||||
uint64_t mLastLogFrames;
|
||||
|
||||
NullTransport *mNullTransport;
|
||||
|
||||
// mSkipProcessing is true if none of the processing passes are enabled,
|
||||
// because of prefs or constraints. This allows simply copying the audio into
|
||||
// the MSG, skipping resampling and the whole webrtc.org code.
|
||||
|
@ -549,7 +548,8 @@ private:
|
|||
// To only update microphone when needed, we keep track of previous settings.
|
||||
MediaEnginePrefs mLastPrefs;
|
||||
|
||||
AlignedShortBuffer mInputDownmixBuffer;
|
||||
AlignedFloatBuffer mInputBuffer;
|
||||
AlignedFloatBuffer mInputDownmixBuffer;
|
||||
};
|
||||
|
||||
class MediaEngineWebRTC : public MediaEngine
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
#undef FF
|
||||
#endif
|
||||
#include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
|
||||
#include "webrtc/voice_engine/voice_engine_defines.h"
|
||||
#include "webrtc/modules/audio_processing/include/audio_processing.h"
|
||||
#include "webrtc/common_audio/include/audio_util.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
#define CHANNELS 1
|
||||
#define ENCODING "L16"
|
||||
|
@ -63,7 +68,7 @@ AudioOutputObserver::AudioOutputObserver()
|
|||
, mDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100)
|
||||
{
|
||||
// Buffers of 10ms chunks
|
||||
mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
|
||||
mPlayoutFifo = new SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
|
||||
}
|
||||
|
||||
AudioOutputObserver::~AudioOutputObserver()
|
||||
|
@ -191,6 +196,7 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
|||
bool aExtendedFilter)
|
||||
: MediaEngineAudioSource(kReleased)
|
||||
, mAudioInput(aAudioInput)
|
||||
, mAudioProcessing(AudioProcessing::Create())
|
||||
, mMonitor("WebRTCMic.Monitor")
|
||||
, mCapIndex(aIndex)
|
||||
, mDelayAgnostic(aDelayAgnostic)
|
||||
|
@ -266,6 +272,140 @@ bool operator == (const MediaEnginePrefs& a, const MediaEnginePrefs& b)
|
|||
return !memcmp(&a, &b, sizeof(MediaEnginePrefs));
|
||||
};
|
||||
|
||||
// This does an early return in case of error.
|
||||
#define HANDLE_APM_ERROR(fn) \
|
||||
do { \
|
||||
int rv = fn; \
|
||||
if (rv != AudioProcessing::kNoError) { \
|
||||
MOZ_ASSERT_UNREACHABLE("APM error in " #fn); \
|
||||
return; \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
void MediaEngineWebRTCMicrophoneSource::UpdateAECSettingsIfNeeded(bool aEnable, EcModes aMode)
|
||||
{
|
||||
using webrtc::EcModes;
|
||||
|
||||
EchoCancellation::SuppressionLevel level;
|
||||
|
||||
switch(aMode) {
|
||||
case EcModes::kEcUnchanged:
|
||||
level = mAudioProcessing->echo_cancellation()->suppression_level();
|
||||
break;
|
||||
case EcModes::kEcConference:
|
||||
level = EchoCancellation::kHighSuppression;
|
||||
break;
|
||||
case EcModes::kEcDefault:
|
||||
level = EchoCancellation::kModerateSuppression;
|
||||
break;
|
||||
case EcModes::kEcAec:
|
||||
level = EchoCancellation::kModerateSuppression;
|
||||
break;
|
||||
case EcModes::kEcAecm:
|
||||
// No suppression level to set for the mobile echo canceller
|
||||
break;
|
||||
default:
|
||||
MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Bad EcMode value"));
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the echo cancelation mode.");
|
||||
// fall back to something sensible in release
|
||||
level = EchoCancellation::kModerateSuppression;
|
||||
break;
|
||||
}
|
||||
|
||||
// AECm and AEC are mutually exclusive.
|
||||
if (aMode == EcModes::kEcAecm) {
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(false));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(aEnable));
|
||||
} else {
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(false));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(aEnable));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->set_suppression_level(level));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateAGCSettingsIfNeeded(bool aEnable, AgcModes aMode)
|
||||
{
|
||||
#if defined(WEBRTC_IOS) || defined(ATA) || defined(WEBRTC_ANDROID)
|
||||
if (aMode == kAgcAdaptiveAnalog) {
|
||||
MOZ_LOG(GetMediaManagerLog(),
|
||||
LogLevel::Error,
|
||||
("Invalid AGC mode kAgcAdaptiveAnalog on mobile"));
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the auto gain, on mobile.");
|
||||
aMode = kAgcDefault;
|
||||
}
|
||||
#endif
|
||||
GainControl::Mode mode = kDefaultAgcMode;
|
||||
|
||||
switch (aMode) {
|
||||
case AgcModes::kAgcDefault:
|
||||
mode = kDefaultAgcMode;
|
||||
break;
|
||||
case AgcModes::kAgcUnchanged:
|
||||
mode = mAudioProcessing->gain_control()->mode();
|
||||
break;
|
||||
case AgcModes::kAgcFixedDigital:
|
||||
mode = GainControl::Mode::kFixedDigital;
|
||||
break;
|
||||
case AgcModes::kAgcAdaptiveAnalog:
|
||||
mode = GainControl::Mode::kAdaptiveAnalog;
|
||||
break;
|
||||
case AgcModes::kAgcAdaptiveDigital:
|
||||
mode = GainControl::Mode::kAdaptiveDigital;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the auto gain.");
|
||||
// This is a good fallback, it works regardless of the platform.
|
||||
mode = GainControl::Mode::kAdaptiveDigital;
|
||||
break;
|
||||
}
|
||||
|
||||
HANDLE_APM_ERROR(mAudioProcessing->gain_control()->set_mode(mode));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->gain_control()->Enable(aEnable));
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateNSSettingsIfNeeded(bool aEnable, NsModes aMode)
|
||||
{
|
||||
NoiseSuppression::Level nsLevel;
|
||||
|
||||
switch (aMode) {
|
||||
case NsModes::kNsDefault:
|
||||
nsLevel = kDefaultNsMode;
|
||||
break;
|
||||
case NsModes::kNsUnchanged:
|
||||
nsLevel = mAudioProcessing->noise_suppression()->level();
|
||||
break;
|
||||
case NsModes::kNsConference:
|
||||
nsLevel = NoiseSuppression::kHigh;
|
||||
break;
|
||||
case NsModes::kNsLowSuppression:
|
||||
nsLevel = NoiseSuppression::kLow;
|
||||
break;
|
||||
case NsModes::kNsModerateSuppression:
|
||||
nsLevel = NoiseSuppression::kModerate;
|
||||
break;
|
||||
case NsModes::kNsHighSuppression:
|
||||
nsLevel = NoiseSuppression::kHigh;
|
||||
break;
|
||||
case NsModes::kNsVeryHighSuppression:
|
||||
nsLevel = NoiseSuppression::kVeryHigh;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
|
||||
" for the noise suppression.");
|
||||
// Pick something sensible as a faillback in release.
|
||||
nsLevel = NoiseSuppression::kModerate;
|
||||
}
|
||||
HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->set_level(nsLevel));
|
||||
HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->Enable(aEnable));
|
||||
}
|
||||
|
||||
#undef HANDLE_APM_ERROR
|
||||
|
||||
nsresult
|
||||
MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
||||
const AllocationHandle* aHandle,
|
||||
|
@ -314,24 +454,12 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
|||
// (Bug 1238038) fail allocation for a second device
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (mAudioInput->SetRecordingDevice(mCapIndex)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mAudioInput->SetUserChannelCount(prefs.mChannels);
|
||||
if (!AllocChannel()) {
|
||||
FreeChannel();
|
||||
LOG(("Audio device is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG(("Audio device %d allocated", mCapIndex));
|
||||
{
|
||||
// Update with the actual applied channelCount in order
|
||||
// to store it in settings.
|
||||
uint32_t channelCount = 0;
|
||||
mAudioInput->GetChannelCount(channelCount);
|
||||
MOZ_ASSERT(channelCount > 0);
|
||||
prefs.mChannels = channelCount;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStarted:
|
||||
|
@ -341,19 +469,19 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
|||
|
||||
if (prefs.mChannels != mLastPrefs.mChannels) {
|
||||
MOZ_ASSERT(mSources.Length() > 0);
|
||||
// If the channel count changed, tell the MSG to open a new driver with
|
||||
// the correct channel count.
|
||||
auto& source = mSources.LastElement();
|
||||
mAudioInput->SetUserChannelCount(prefs.mChannels);
|
||||
// Get validated number of channel
|
||||
uint32_t channelCount = 0;
|
||||
mAudioInput->GetChannelCount(channelCount);
|
||||
MOZ_ASSERT(channelCount > 0 && mLastPrefs.mChannels > 0);
|
||||
// Check if new validated channels is the same as previous
|
||||
if (static_cast<uint32_t>(mLastPrefs.mChannels) != channelCount &&
|
||||
if (mLastPrefs.mChannels != prefs.mChannels &&
|
||||
!source->OpenNewAudioCallbackDriver(mListener)) {
|
||||
MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Could not open a new AudioCallbackDriver for input"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Update settings
|
||||
prefs.mChannels = channelCount;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
|
||||
|
@ -372,46 +500,22 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
|||
}
|
||||
|
||||
if (sChannelsOpen > 0) {
|
||||
int error;
|
||||
UpdateAGCSettingsIfNeeded(prefs.mAgcOn, static_cast<AgcModes>(prefs.mAgc));
|
||||
UpdateNSSettingsIfNeeded(prefs.mNoiseOn, static_cast<NsModes>(prefs.mNoise));
|
||||
UpdateAECSettingsIfNeeded(prefs.mAecOn, static_cast<EcModes>(prefs.mAec));
|
||||
|
||||
error = mVoEProcessing->SetEcStatus(prefs.mAecOn, (webrtc::EcModes)prefs.mAec);
|
||||
if (error) {
|
||||
LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error));
|
||||
// Overhead of capturing all the time is very low (<0.1% of an audio only call)
|
||||
if (prefs.mAecOn) {
|
||||
error = mVoEProcessing->SetEcMetricsStatus(true);
|
||||
if (error) {
|
||||
LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
error = mVoEProcessing->SetAgcStatus(prefs.mAgcOn, (webrtc::AgcModes)prefs.mAgc);
|
||||
if (error) {
|
||||
LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error));
|
||||
}
|
||||
error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise);
|
||||
if (error) {
|
||||
LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error));
|
||||
}
|
||||
webrtc::Config config;
|
||||
config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter));
|
||||
config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic));
|
||||
mAudioProcessing->SetExtraOptions(config);
|
||||
}
|
||||
|
||||
// we don't allow switching from non-fast-path to fast-path on the fly yet
|
||||
if (mState != kStarted) {
|
||||
mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn);
|
||||
if (mSkipProcessing) {
|
||||
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
|
||||
} else {
|
||||
// make sure we route a copy of the mixed audio output of this MSG to the
|
||||
// AEC
|
||||
if (!mAudioOutputObserver) {
|
||||
mAudioOutputObserver = new AudioOutputObserver();
|
||||
}
|
||||
}
|
||||
}
|
||||
SetLastPrefs(prefs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#undef HANDLE_APM_ERROR
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
|
||||
const MediaEnginePrefs& aPrefs)
|
||||
|
@ -579,13 +683,25 @@ MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph,
|
|||
TrackRate aRate,
|
||||
uint32_t aChannels)
|
||||
{
|
||||
// This will call Process() with data coming out of the AEC/NS/AGC/etc chain
|
||||
MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode.");
|
||||
size_t offset = 0;
|
||||
|
||||
if (!mPacketizer ||
|
||||
mPacketizer->PacketSize() != aRate/100u ||
|
||||
mPacketizer->Channels() != aChannels) {
|
||||
// It's ok to drop the audio still in the packetizer here.
|
||||
mPacketizer =
|
||||
new AudioPacketizer<AudioDataValue, int16_t>(aRate/100, aChannels);
|
||||
new AudioPacketizer<AudioDataValue, AudioDataValue>(aRate/100, aChannels);
|
||||
}
|
||||
|
||||
// On initial capture, throw away all far-end data except the most recent sample
|
||||
// since it's already irrelevant and we want to keep avoid confusing the AEC far-end
|
||||
// input code with "old" audio.
|
||||
if (!mStarted) {
|
||||
mStarted = true;
|
||||
while (mAudioOutputObserver->Size() > 1) {
|
||||
free(mAudioOutputObserver->Pop()); // only call if size() > 0
|
||||
}
|
||||
}
|
||||
|
||||
mPacketizer->Input(aBuffer, static_cast<uint32_t>(aFrames));
|
||||
|
@ -704,38 +820,31 @@ MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph,
|
|||
|
||||
#define ResetProcessingIfNeeded(_processing) \
|
||||
do { \
|
||||
webrtc::_processing##Modes mode; \
|
||||
int rv = mVoEProcessing->Get##_processing##Status(enabled, mode); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not get the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
bool enabled = mAudioProcessing->_processing()->is_enabled(); \
|
||||
\
|
||||
if (enabled) { \
|
||||
rv = mVoEProcessing->Set##_processing##Status(!enabled); \
|
||||
int rv = mAudioProcessing->_processing()->Enable(!enabled); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not reset the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
rv = mAudioProcessing->_processing()->Enable(enabled); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not reset the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
rv = mVoEProcessing->Set##_processing##Status(enabled); \
|
||||
if (rv) { \
|
||||
NS_WARNING("Could not reset the status of the " \
|
||||
#_processing " on device change."); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void
|
||||
MediaEngineWebRTCMicrophoneSource::DeviceChanged() {
|
||||
// Reset some processing
|
||||
bool enabled;
|
||||
ResetProcessingIfNeeded(Agc);
|
||||
ResetProcessingIfNeeded(Ec);
|
||||
ResetProcessingIfNeeded(Ns);
|
||||
ResetProcessingIfNeeded(gain_control);
|
||||
ResetProcessingIfNeeded(echo_cancellation);
|
||||
ResetProcessingIfNeeded(noise_suppression);
|
||||
}
|
||||
|
||||
// mState records if a channel is allocated (slightly redundantly to mChannel)
|
||||
|
@ -796,8 +905,6 @@ MediaEngineWebRTCMicrophoneSource::Shutdown()
|
|||
Deallocate(mRegisteredHandles[0].get());
|
||||
}
|
||||
MOZ_ASSERT(mState == kReleased);
|
||||
|
||||
mAudioInput = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -496,7 +496,7 @@ pref("media.peerconnection.ice.no_host", false);
|
|||
pref("media.peerconnection.ice.default_address_only", false);
|
||||
pref("media.peerconnection.ice.proxy_only", false);
|
||||
|
||||
// These values (aec, agc, and noice) are from media/webrtc/trunk/webrtc/common_types.h
|
||||
// These values (aec, agc, and noise) are from media/webrtc/trunk/webrtc/common_types.h
|
||||
// kXxxUnchanged = 0, kXxxDefault = 1, and higher values are specific to each
|
||||
// setting (for Xxx = Ec, Agc, or Ns). Defaults are all set to kXxxDefault here.
|
||||
pref("media.peerconnection.turn.disable", false);
|
||||
|
@ -510,7 +510,7 @@ pref("media.getusermedia.noise_enabled", true);
|
|||
pref("media.getusermedia.aec_extended_filter", true);
|
||||
pref("media.getusermedia.noise", 1);
|
||||
pref("media.getusermedia.agc_enabled", false);
|
||||
pref("media.getusermedia.agc", 1);
|
||||
pref("media.getusermedia.agc", 3); // kAgcAdaptiveDigital
|
||||
// capture_delay: Adjustments for OS-specific input delay (lower bound)
|
||||
// playout_delay: Adjustments for OS-specific AudioStream+cubeb+output delay (lower bound)
|
||||
// full_duplex: enable cubeb full-duplex capture/playback
|
||||
|
|
Загрузка…
Ссылка в новой задаче