diff options
author | henrika <henrika@chromium.org> | 2014-10-16 07:44:12 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-16 14:44:37 +0000 |
commit | c569d98674f56fccf39c53f7ec7a63127755390f (patch) | |
tree | 12bc496f1bf71c5c9421f492c63bd8f600682a88 | |
parent | 15a8709fe24ebb55e15ed88b306d76ad72412931 (diff) | |
download | chromium_src-c569d98674f56fccf39c53f7ec7a63127755390f.zip chromium_src-c569d98674f56fccf39c53f7ec7a63127755390f.tar.gz chromium_src-c569d98674f56fccf39c53f7ec7a63127755390f.tar.bz2 |
Add support for audio input mute detection on all platforms.
TBR=rkc@chromium.org
BUG=422275
TEST=Manual tests using DVLOGs to confirm valid microphone mute states.
Review URL: https://codereview.chromium.org/645923002
Cr-Commit-Position: refs/heads/master@{#299891}
27 files changed, 162 insertions, 33 deletions
diff --git a/components/copresence/mediums/audio/audio_recorder_unittest.cc b/components/copresence/mediums/audio/audio_recorder_unittest.cc index df3f3e5..2c81213 100644 --- a/components/copresence/mediums/audio/audio_recorder_unittest.cc +++ b/components/copresence/mediums/audio/audio_recorder_unittest.cc @@ -47,6 +47,7 @@ class TestAudioInputStream : public media::AudioInputStream { virtual double GetVolume() override { return 1.0; } virtual void SetAutomaticGainControl(bool enabled) override {} virtual bool GetAutomaticGainControl() override { return true; } + virtual bool IsMuted() override { return false; } private: void SimulateRecording() { diff --git a/content/browser/media/capture/web_contents_audio_input_stream.cc b/content/browser/media/capture/web_contents_audio_input_stream.cc index ad4fd2f..cdf7eb9 100644 --- a/content/browser/media/capture/web_contents_audio_input_stream.cc +++ b/content/browser/media/capture/web_contents_audio_input_stream.cc @@ -391,4 +391,8 @@ bool WebContentsAudioInputStream::GetAutomaticGainControl() { return impl_->mixer_stream()->GetAutomaticGainControl(); } +bool WebContentsAudioInputStream::IsMuted() { + return false; +} + } // namespace content diff --git a/content/browser/media/capture/web_contents_audio_input_stream.h b/content/browser/media/capture/web_contents_audio_input_stream.h index 2f34eed..a0468ff 100644 --- a/content/browser/media/capture/web_contents_audio_input_stream.h +++ b/content/browser/media/capture/web_contents_audio_input_stream.h @@ -46,6 +46,7 @@ class CONTENT_EXPORT WebContentsAudioInputStream virtual double GetVolume() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; + virtual bool IsMuted() override; // Create a new audio mirroring session, or return NULL on error. |device_id| // should be in the format accepted by diff --git a/media/audio/alsa/alsa_input.cc b/media/audio/alsa/alsa_input.cc index 0bc9f31..3c555a0 100644 --- a/media/audio/alsa/alsa_input.cc +++ b/media/audio/alsa/alsa_input.cc @@ -336,6 +336,10 @@ double AlsaPcmInputStream::GetVolume() { return static_cast<double>(current_volume); } +bool AlsaPcmInputStream::IsMuted() { + return false; +} + void AlsaPcmInputStream::HandleError(const char* method, int error) { LOG(WARNING) << method << ": " << wrapper_->StrError(error); callback_->OnError(this); diff --git a/media/audio/alsa/alsa_input.h b/media/audio/alsa/alsa_input.h index adc2619..ef7cb2c 100644 --- a/media/audio/alsa/alsa_input.h +++ b/media/audio/alsa/alsa_input.h @@ -49,6 +49,7 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> { virtual double GetMaxVolume() override; virtual void SetVolume(double volume) override; virtual double GetVolume() override; + virtual bool IsMuted() override; private: // Logs the error and invokes any registered callbacks. diff --git a/media/audio/android/audio_record_input.cc b/media/audio/android/audio_record_input.cc index 3f19588..1dd6118 100644 --- a/media/audio/android/audio_record_input.cc +++ b/media/audio/android/audio_record_input.cc @@ -134,4 +134,9 @@ bool AudioRecordInputStream::GetAutomaticGainControl() { return false; } +bool AudioRecordInputStream::IsMuted() { + NOTIMPLEMENTED(); + return false; +} + } // namespace media diff --git a/media/audio/android/audio_record_input.h b/media/audio/android/audio_record_input.h index 198fd59..d4c4368 100644 --- a/media/audio/android/audio_record_input.h +++ b/media/audio/android/audio_record_input.h @@ -40,6 +40,7 @@ class MEDIA_EXPORT AudioRecordInputStream : public AudioInputStream { virtual double GetVolume() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; + virtual bool IsMuted() override; static bool RegisterAudioRecordInput(JNIEnv* env); diff --git a/media/audio/android/opensles_input.cc b/media/audio/android/opensles_input.cc index 1ef3aac..d8e5f63 100644 --- a/media/audio/android/opensles_input.cc +++ b/media/audio/android/opensles_input.cc @@ -185,6 +185,11 @@ bool OpenSLESInputStream::GetAutomaticGainControl() { return false; } +bool OpenSLESInputStream::IsMuted() { + NOTIMPLEMENTED(); + return false; +} + bool OpenSLESInputStream::CreateRecorder() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!engine_object_.Get()); diff --git a/media/audio/android/opensles_input.h b/media/audio/android/opensles_input.h index 2998e53..ae62639 100644 --- a/media/audio/android/opensles_input.h +++ b/media/audio/android/opensles_input.h @@ -43,6 +43,7 @@ class OpenSLESInputStream : public AudioInputStream { virtual double GetVolume() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; + virtual bool IsMuted() override; private: bool CreateRecorder(); diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index 72239f0..3bd14f1 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -583,11 +583,24 @@ void AudioInputController::DoLogAudioLevels(float level_dbfs, if (!handler_) return; + // Detect if the user has enabled hardware mute by pressing the mute + // button in audio settings for the selected microphone. + const bool microphone_is_muted = stream_->IsMuted(); + if (microphone_is_muted) { + LogMicrophoneMuteResult(MICROPHONE_IS_MUTED); + handler_->OnLog(this, "AIC::OnData: microphone is muted!"); + // Return early if microphone is muted. No need to adding logs and UMA stats + // of audio levels if we know that the micropone is muted. + return; + } + + LogMicrophoneMuteResult(MICROPHONE_IS_NOT_MUTED); + std::string log_string = base::StringPrintf( "AIC::OnData: average audio level=%.2f dBFS", level_dbfs); static const float kSilenceThresholdDBFS = -72.24719896f; if (level_dbfs < kSilenceThresholdDBFS) - log_string += " <=> no audio input!"; + log_string += " <=> low audio input level!"; handler_->OnLog(this, log_string); UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS); @@ -598,20 +611,6 @@ void AudioInputController::DoLogAudioLevels(float level_dbfs, if (microphone_volume_percent < kLowLevelMicrophoneLevelPercent) log_string += " <=> low microphone level!"; handler_->OnLog(this, log_string); - - // Try to detect if the user has enabled hardware mute by pressing the mute - // button in audio settings for the selected microphone. The idea here is to - // detect when all input samples are zeros but the actual volume slider is - // larger than zero. It should correspond to a hardware mute state. - if (level_dbfs == -std::numeric_limits<float>::infinity() && - microphone_volume_percent > 0) { - LogMicrophoneMuteResult(MICROPHONE_IS_MUTED); - log_string = base::StringPrintf( - "AIC::OnData: microphone is muted!"); - handler_->OnLog(this, log_string); - } else { - LogMicrophoneMuteResult(MICROPHONE_IS_NOT_MUTED); - } #endif } diff --git a/media/audio/audio_io.h b/media/audio/audio_io.h index 105e32e..3add053 100644 --- a/media/audio/audio_io.h +++ b/media/audio/audio_io.h @@ -164,6 +164,9 @@ class MEDIA_EXPORT AudioInputStream { // Returns the Automatic Gain Control (AGC) state. virtual bool GetAutomaticGainControl() = 0; + + // Returns the current muting state for the microphone. + virtual bool IsMuted() = 0; }; } // namespace media diff --git a/media/audio/cras/cras_input.cc b/media/audio/cras/cras_input.cc index 0b86446..3fae8df 100644 --- a/media/audio/cras/cras_input.cc +++ b/media/audio/cras/cras_input.cc @@ -301,6 +301,10 @@ double CrasInputStream::GetVolume() { return GetVolumeRatioFromDecibels(dB); } +bool CrasInputStream::IsMuted() { + return false; +} + double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const { return pow(10, dB / 20.0); } diff --git a/media/audio/cras/cras_input.h b/media/audio/cras/cras_input.h index 4f5614d..f290f1e 100644 --- a/media/audio/cras/cras_input.h +++ b/media/audio/cras/cras_input.h @@ -40,6 +40,7 @@ class CrasInputStream : public AgcAudioStream<AudioInputStream> { virtual double GetMaxVolume() override; virtual void SetVolume(double volume) override; virtual double GetVolume() override; + virtual bool IsMuted() override; private: // Handles requests to get samples from the provided buffer. This will be diff --git a/media/audio/fake_audio_input_stream.cc b/media/audio/fake_audio_input_stream.cc index 74ac579..157d397 100644 --- a/media/audio/fake_audio_input_stream.cc +++ b/media/audio/fake_audio_input_stream.cc @@ -203,6 +203,11 @@ double FakeAudioInputStream::GetVolume() { return 1.0; } +bool FakeAudioInputStream::IsMuted() { + DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); + return false; +} + void FakeAudioInputStream::SetAutomaticGainControl(bool enabled) {} bool FakeAudioInputStream::GetAutomaticGainControl() { diff --git a/media/audio/fake_audio_input_stream.h b/media/audio/fake_audio_input_stream.h index db00c2e..dba6aaa 100644 --- a/media/audio/fake_audio_input_stream.h +++ b/media/audio/fake_audio_input_stream.h @@ -34,6 +34,7 @@ class MEDIA_EXPORT FakeAudioInputStream virtual double GetMaxVolume() override; virtual void SetVolume(double volume) override; virtual double GetVolume() override; + virtual bool IsMuted() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; diff --git a/media/audio/mac/audio_input_mac.cc b/media/audio/mac/audio_input_mac.cc index b7f6e17..af5d9bc 100644 --- a/media/audio/mac/audio_input_mac.cc +++ b/media/audio/mac/audio_input_mac.cc @@ -136,6 +136,11 @@ double PCMQueueInAudioInputStream::GetVolume() { return 0.0; } +bool PCMQueueInAudioInputStream::IsMuted() { + NOTREACHED() << "Only supported for low-latency mode."; + return false; +} + void PCMQueueInAudioInputStream::SetAutomaticGainControl(bool enabled) { NOTREACHED() << "Only supported for low-latency mode."; } diff --git a/media/audio/mac/audio_input_mac.h b/media/audio/mac/audio_input_mac.h index 37afcee..6c1b4a14f 100644 --- a/media/audio/mac/audio_input_mac.h +++ b/media/audio/mac/audio_input_mac.h @@ -38,6 +38,7 @@ class PCMQueueInAudioInputStream : public AudioInputStream { virtual double GetVolume() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; + virtual bool IsMuted() override; private: // Issue the OnError to |callback_|; diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index 1baa09f..2b2ce00 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -451,6 +451,29 @@ double AUAudioInputStream::GetVolume() { return 0.0; } +bool AUAudioInputStream::IsMuted() { + // Verify that we have a valid device. + DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown"; + + AudioObjectPropertyAddress property_address = { + kAudioDevicePropertyMute, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster + }; + + if (!AudioObjectHasProperty(input_device_id_, &property_address)) { + DLOG(ERROR) << "Device does not support checking master mute state"; + return false; + } + + UInt32 muted = 0; + UInt32 size = sizeof(muted); + OSStatus result = AudioObjectGetPropertyData( + input_device_id_, &property_address, 0, NULL, &size, &muted); + DLOG_IF(WARNING, result != noErr) << "Failed to get mute state"; + return result == noErr && muted != 0; +} + // AUHAL AudioDeviceOutput unit callback OSStatus AUAudioInputStream::InputProc(void* user_data, AudioUnitRenderActionFlags* flags, diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h index 623cf1d..b3950ffd 100644 --- a/media/audio/mac/audio_low_latency_input_mac.h +++ b/media/audio/mac/audio_low_latency_input_mac.h @@ -72,6 +72,7 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { virtual double GetMaxVolume() override; virtual void SetVolume(double volume) override; virtual double GetVolume() override; + virtual bool IsMuted() override; // Returns the current hardware sample rate for the default input device. MEDIA_EXPORT static int HardwareSampleRate(); diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc index 14ef242..7e2ca22 100644 --- a/media/audio/pulse/pulse_input.cc +++ b/media/audio/pulse/pulse_input.cc @@ -4,8 +4,6 @@ #include "media/audio/pulse/pulse_input.h" -#include <pulse/pulseaudio.h> - #include "base/logging.h" #include "media/audio/pulse/audio_manager_pulse.h" #include "media/audio/pulse/pulse_util.h" @@ -30,6 +28,7 @@ PulseAudioInputStream::PulseAudioInputStream(AudioManagerPulse* audio_manager, channels_(0), volume_(0.0), stream_started_(false), + muted_(false), fifo_(params.channels(), params.frames_per_buffer(), kNumberOfBlocksBufferInFifo), @@ -185,20 +184,17 @@ double PulseAudioInputStream::GetVolume() { // Return zero and the callback will asynchronously update the |volume_|. return 0.0; } else { - // Called by other thread, put an AutoPulseLock and wait for the operation. - AutoPulseLock auto_lock(pa_mainloop_); - if (!handle_) - return 0.0; - - size_t index = pa_stream_get_device_index(handle_); - pa_operation* operation = pa_context_get_source_info_by_index( - pa_context_, index, &VolumeCallback, this); - WaitForOperationCompletion(pa_mainloop_, operation); - + GetSourceInformation(&VolumeCallback); return volume_; } } +bool PulseAudioInputStream::IsMuted() { + DCHECK(thread_checker_.CalledOnValidThread()); + GetSourceInformation(&MuteCallback); + return muted_; +} + // static, used by pa_stream_set_read_callback. void PulseAudioInputStream::ReadCallback(pa_stream* handle, size_t length, @@ -236,11 +232,32 @@ void PulseAudioInputStream::VolumeCallback(pa_context* context, stream->volume_ = static_cast<double>(volume); } +// static, used by pa_context_get_source_info_by_index. +void PulseAudioInputStream::MuteCallback(pa_context* context, + const pa_source_info* info, + int error, + void* user_data) { + // Runs on PulseAudio callback thread. It might be possible to make this + // method more thread safe by passing a struct (or pair) of a local copy of + // |pa_mainloop_| and |muted_| instead. + PulseAudioInputStream* stream = + reinterpret_cast<PulseAudioInputStream*>(user_data); + + // Avoid infinite wait loop in case of error. + if (error) { + pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); + return; + } + + stream->muted_ = info->mute != 0; +} + // static, used by pa_stream_set_state_callback. void PulseAudioInputStream::StreamNotifyCallback(pa_stream* s, void* user_data) { PulseAudioInputStream* stream = reinterpret_cast<PulseAudioInputStream*>(user_data); + if (s && stream->callback_ && pa_stream_get_state(s) == PA_STREAM_FAILED) { stream->callback_->OnError(stream); @@ -301,4 +318,16 @@ void PulseAudioInputStream::ReadData() { pa_threaded_mainloop_signal(pa_mainloop_, 0); } +bool PulseAudioInputStream::GetSourceInformation(pa_source_info_cb_t callback) { + AutoPulseLock auto_lock(pa_mainloop_); + if (!handle_) + return false; + + size_t index = pa_stream_get_device_index(handle_); + pa_operation* operation = + pa_context_get_source_info_by_index(pa_context_, index, callback, this); + WaitForOperationCompletion(pa_mainloop_, operation); + return true; +} + } // namespace media diff --git a/media/audio/pulse/pulse_input.h b/media/audio/pulse/pulse_input.h index 60acf2f..dd42b31 100644 --- a/media/audio/pulse/pulse_input.h +++ b/media/audio/pulse/pulse_input.h @@ -5,6 +5,7 @@ #ifndef MEDIA_AUDIO_PULSE_PULSE_INPUT_H_ #define MEDIA_AUDIO_PULSE_PULSE_INPUT_H_ +#include <pulse/pulseaudio.h> #include <string> #include "base/threading/thread_checker.h" @@ -14,11 +15,6 @@ #include "media/audio/audio_parameters.h" #include "media/base/audio_block_fifo.h" -struct pa_context; -struct pa_source_info; -struct pa_stream; -struct pa_threaded_mainloop; - namespace media { class AudioManagerPulse; @@ -41,6 +37,7 @@ class PulseAudioInputStream : public AgcAudioStream<AudioInputStream> { virtual double GetMaxVolume() override; virtual void SetVolume(double volume) override; virtual double GetVolume() override; + virtual bool IsMuted() override; private: // PulseAudio Callbacks. @@ -48,10 +45,17 @@ class PulseAudioInputStream : public AgcAudioStream<AudioInputStream> { static void StreamNotifyCallback(pa_stream* stream, void* user_data); static void VolumeCallback(pa_context* context, const pa_source_info* info, int error, void* user_data); + static void MuteCallback(pa_context* context, + const pa_source_info* info, + int error, + void* user_data); // Helper for the ReadCallback. void ReadData(); + // Utility method used by GetVolume() and IsMuted(). + bool GetSourceInformation(pa_source_info_cb_t callback); + AudioManagerPulse* audio_manager_; AudioInputCallback* callback_; std::string device_name_; @@ -60,6 +64,10 @@ class PulseAudioInputStream : public AgcAudioStream<AudioInputStream> { double volume_; bool stream_started_; + // Set to true in IsMuted() if user has muted the selected microphone in the + // sound settings UI. + bool muted_; + // Holds the data from the OS. AudioBlockFifo fifo_; diff --git a/media/audio/virtual_audio_input_stream.cc b/media/audio/virtual_audio_input_stream.cc index c832a82..6d1189e 100644 --- a/media/audio/virtual_audio_input_stream.cc +++ b/media/audio/virtual_audio_input_stream.cc @@ -173,4 +173,8 @@ bool VirtualAudioInputStream::GetAutomaticGainControl() { return false; } +bool VirtualAudioInputStream::IsMuted() { + return false; +} + } // namespace media diff --git a/media/audio/virtual_audio_input_stream.h b/media/audio/virtual_audio_input_stream.h index 88c92ea..b33dadd 100644 --- a/media/audio/virtual_audio_input_stream.h +++ b/media/audio/virtual_audio_input_stream.h @@ -56,6 +56,7 @@ class MEDIA_EXPORT VirtualAudioInputStream : public AudioInputStream { virtual double GetVolume() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; + virtual bool IsMuted() override; // Attaches a VirtualAudioOutputStream to be used as input. This // VirtualAudioInputStream must outlive all attached streams, so any attached diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc index 9fe061b..8df8620 100644 --- a/media/audio/win/audio_low_latency_input_win.cc +++ b/media/audio/win/audio_low_latency_input_win.cc @@ -253,7 +253,7 @@ void WASAPIAudioInputStream::SetVolume(double volume) { } double WASAPIAudioInputStream::GetVolume() { - DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; + DCHECK(opened_) << "Open() has not been called successfully"; if (!opened_) return 0.0; @@ -265,6 +265,20 @@ double WASAPIAudioInputStream::GetVolume() { return static_cast<double>(level); } +bool WASAPIAudioInputStream::IsMuted() { + DCHECK(opened_) << "Open() has not been called successfully"; + DCHECK(CalledOnValidThread()); + if (!opened_) + return false; + + // Retrieves the current muting state for the audio session. + BOOL is_muted = FALSE; + HRESULT hr = simple_audio_volume_->GetMute(&is_muted); + DLOG_IF(WARNING, FAILED(hr)) << "Failed to get input master volume."; + + return is_muted != FALSE; +} + // static AudioParameters WASAPIAudioInputStream::GetInputStreamParameters( const std::string& device_id) { diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h index 51feac7..e933a44 100644 --- a/media/audio/win/audio_low_latency_input_win.h +++ b/media/audio/win/audio_low_latency_input_win.h @@ -102,6 +102,7 @@ class MEDIA_EXPORT WASAPIAudioInputStream virtual double GetMaxVolume() override; virtual void SetVolume(double volume) override; virtual double GetVolume() override; + virtual bool IsMuted() override; bool started() const { return started_; } diff --git a/media/audio/win/wavein_input_win.cc b/media/audio/win/wavein_input_win.cc index e96a877..72c5841 100644 --- a/media/audio/win/wavein_input_win.cc +++ b/media/audio/win/wavein_input_win.cc @@ -226,6 +226,11 @@ bool PCMWaveInAudioInputStream::GetAutomaticGainControl() { return false; } +bool PCMWaveInAudioInputStream::IsMuted() { + NOTIMPLEMENTED(); + return false; +} + void PCMWaveInAudioInputStream::HandleError(MMRESULT error) { DLOG(WARNING) << "PCMWaveInAudio error " << error; if (callback_) diff --git a/media/audio/win/wavein_input_win.h b/media/audio/win/wavein_input_win.h index c587ed6..8c22c9b 100644 --- a/media/audio/win/wavein_input_win.h +++ b/media/audio/win/wavein_input_win.h @@ -45,6 +45,7 @@ class PCMWaveInAudioInputStream : public AudioInputStream { virtual double GetVolume() override; virtual void SetAutomaticGainControl(bool enabled) override; virtual bool GetAutomaticGainControl() override; + virtual bool IsMuted() override; private: enum State { |