Commit ba5b0d78 authored by xians's avatar xians Committed by Commit bot

Avoid reporting 0 as input level when AudioProcessing zero out the audio data.

When the microphone volume is low, the pre-processed audio data contains energy, while the post-processed audio data has only 0, we report 0 as energy before this patch, which is wrong since the audio flow contains energy.

This patch fix the problem by checking the energy of pre-processed data before reporting 0 as energy.

BUG=424149
TEST=1, use a USB headset.
2, set the microphone volume low, like below 50%.
3, join a hangout call.
4, open chrome://webrtc-internals, and look at the energy of input stream, constantly it is 0 there.

Review URL: https://codereview.chromium.org/661693003

Cr-Commit-Position: refs/heads/master@{#300274}
parent fbf0dae3
...@@ -38,9 +38,11 @@ MediaStreamAudioLevelCalculator::MediaStreamAudioLevelCalculator() ...@@ -38,9 +38,11 @@ MediaStreamAudioLevelCalculator::MediaStreamAudioLevelCalculator()
MediaStreamAudioLevelCalculator::~MediaStreamAudioLevelCalculator() { MediaStreamAudioLevelCalculator::~MediaStreamAudioLevelCalculator() {
} }
int MediaStreamAudioLevelCalculator::Calculate(const int16* audio_data, int MediaStreamAudioLevelCalculator::Calculate(
const int16* audio_data,
int number_of_channels, int number_of_channels,
int number_of_frames) { int number_of_frames,
bool force_report_nonzero_energy) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
// |level_| is updated every 10 callbacks. For the case where callback comes // |level_| is updated every 10 callbacks. For the case where callback comes
// every 10ms, |level_| will be updated approximately every 100ms. // every 10ms, |level_| will be updated approximately every 100ms.
...@@ -50,7 +52,8 @@ int MediaStreamAudioLevelCalculator::Calculate(const int16* audio_data, ...@@ -50,7 +52,8 @@ int MediaStreamAudioLevelCalculator::Calculate(const int16* audio_data,
max_amplitude_ = std::max(max_amplitude_, max); max_amplitude_ = std::max(max_amplitude_, max);
if (counter_++ == kUpdateFrequency) { if (counter_++ == kUpdateFrequency) {
level_ = max_amplitude_; level_ = (max_amplitude_ == 0 ?
force_report_nonzero_energy : max_amplitude_);
// Decay the absolute maximum amplitude by 1/4. // Decay the absolute maximum amplitude by 1/4.
max_amplitude_ >>= 2; max_amplitude_ >>= 2;
......
...@@ -21,8 +21,14 @@ class MediaStreamAudioLevelCalculator { ...@@ -21,8 +21,14 @@ class MediaStreamAudioLevelCalculator {
// Calculates the signal level of the audio data. // Calculates the signal level of the audio data.
// Returns the absolute value of the amplitude of the signal. // Returns the absolute value of the amplitude of the signal.
// |force_report_nonzero_energy| is a flag forcing the calculator to
// report nonzero energy even if the energy of the processed audio is zero.
// Since |audio_data| is post processed data, and the audio processing might
// zero all the audio data, when the caller detects the pre processed data
// contain energy, it could pass |force_report_nonzero_energy| as true to
// force calculator to report 1 as energy when |audio_data| is all zero.
int Calculate(const int16* audio_data, int number_of_channels, int Calculate(const int16* audio_data, int number_of_channels,
int number_of_frames); int number_of_frames, bool force_report_nonzero_energy);
private: private:
// Used to DCHECK that the constructor and Calculate() are always called on // Used to DCHECK that the constructor and Calculate() are always called on
......
...@@ -292,7 +292,7 @@ class SpeechRecognitionAudioSinkTest : public testing::Test { ...@@ -292,7 +292,7 @@ class SpeechRecognitionAudioSinkTest : public testing::Test {
for (uint32 i = 0; i < buffers; ++i) for (uint32 i = 0; i < buffers; ++i)
native_track()->Capture(source_data(), native_track()->Capture(source_data(),
base::TimeDelta::FromMilliseconds(0), 1, false, base::TimeDelta::FromMilliseconds(0), 1, false,
false); false, false);
} }
// Used to simulate a problem with sockets. // Used to simulate a problem with sockets.
......
...@@ -127,7 +127,7 @@ void WebAudioCapturerSource::consumeAudio( ...@@ -127,7 +127,7 @@ void WebAudioCapturerSource::consumeAudio(
params_.bits_per_sample() / 8, params_.bits_per_sample() / 8,
audio_data_.get()); audio_data_.get());
track_->Capture(audio_data_.get(), delay, volume, key_pressed, track_->Capture(audio_data_.get(), delay, volume, key_pressed,
need_audio_processing); need_audio_processing, false);
} }
} }
......
...@@ -74,14 +74,14 @@ TEST_F(WebRtcLocalAudioTrackAdapterTest, AddAndRemoveSink) { ...@@ -74,14 +74,14 @@ TEST_F(WebRtcLocalAudioTrackAdapterTest, AddAndRemoveSink) {
EXPECT_CALL(*sink, EXPECT_CALL(*sink,
OnData(_, 16, params_.sample_rate(), params_.channels(), OnData(_, 16, params_.sample_rate(), params_.channels(),
params_.frames_per_buffer())); params_.frames_per_buffer()));
track_->Capture(data.get(), base::TimeDelta(), 255, false, false); track_->Capture(data.get(), base::TimeDelta(), 255, false, false, false);
// Remove the sink from the webrtc track. // Remove the sink from the webrtc track.
webrtc_track->RemoveSink(sink.get()); webrtc_track->RemoveSink(sink.get());
sink.reset(); sink.reset();
// Verify that no more callback gets into the sink. // Verify that no more callback gets into the sink.
track_->Capture(data.get(), base::TimeDelta(), 255, false, false); track_->Capture(data.get(), base::TimeDelta(), 255, false, false, false);
} }
TEST_F(WebRtcLocalAudioTrackAdapterTest, GetSignalLevel) { TEST_F(WebRtcLocalAudioTrackAdapterTest, GetSignalLevel) {
......
...@@ -21,6 +21,24 @@ ...@@ -21,6 +21,24 @@
namespace content { namespace content {
namespace {
// Method to check if any of the data in |audio_source| has energy.
bool HasDataEnergy(const media::AudioBus& audio_source) {
for (int ch = 0; ch < audio_source.channels(); ++ch) {
const float* channel_ptr = audio_source.channel(ch);
for (int frame = 0; frame < audio_source.frames(); ++frame) {
if (channel_ptr[frame] != 0)
return true;
}
}
// All the data is zero.
return false;
}
} // namespace
// Reference counted container of WebRtcLocalAudioTrack delegate. // Reference counted container of WebRtcLocalAudioTrack delegate.
// TODO(xians): Switch to MediaStreamAudioSinkOwner. // TODO(xians): Switch to MediaStreamAudioSinkOwner.
class WebRtcAudioCapturer::TrackOwner class WebRtcAudioCapturer::TrackOwner
...@@ -33,14 +51,16 @@ class WebRtcAudioCapturer::TrackOwner ...@@ -33,14 +51,16 @@ class WebRtcAudioCapturer::TrackOwner
base::TimeDelta delay, base::TimeDelta delay,
double volume, double volume,
bool key_pressed, bool key_pressed,
bool need_audio_processing) { bool need_audio_processing,
bool force_report_nonzero_energy) {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
if (delegate_) { if (delegate_) {
delegate_->Capture(audio_data, delegate_->Capture(audio_data,
delay, delay,
volume, volume,
key_pressed, key_pressed,
need_audio_processing); need_audio_processing,
force_report_nonzero_energy);
} }
} }
...@@ -487,6 +507,12 @@ void WebRtcAudioCapturer::Capture(const media::AudioBus* audio_source, ...@@ -487,6 +507,12 @@ void WebRtcAudioCapturer::Capture(const media::AudioBus* audio_source,
(*it)->SetAudioProcessor(audio_processor_); (*it)->SetAudioProcessor(audio_processor_);
} }
// Figure out if the pre-processed data has any energy or not, the
// information will be passed to the track to force the calculator
// to report energy in case the post-processed data is zeroed by the audio
// processing.
const bool force_report_nonzero_energy = HasDataEnergy(*audio_source);
// Push the data to the processor for processing. // Push the data to the processor for processing.
audio_processor_->PushCaptureData(audio_source); audio_processor_->PushCaptureData(audio_source);
...@@ -500,7 +526,7 @@ void WebRtcAudioCapturer::Capture(const media::AudioBus* audio_source, ...@@ -500,7 +526,7 @@ void WebRtcAudioCapturer::Capture(const media::AudioBus* audio_source,
for (TrackList::ItemList::const_iterator it = tracks.begin(); for (TrackList::ItemList::const_iterator it = tracks.begin();
it != tracks.end(); ++it) { it != tracks.end(); ++it) {
(*it)->Capture(output, audio_delay, current_volume, key_pressed, (*it)->Capture(output, audio_delay, current_volume, key_pressed,
need_audio_processing); need_audio_processing, force_report_nonzero_energy);
} }
if (new_volume) { if (new_volume) {
......
...@@ -42,13 +42,14 @@ void WebRtcLocalAudioTrack::Capture(const int16* audio_data, ...@@ -42,13 +42,14 @@ void WebRtcLocalAudioTrack::Capture(const int16* audio_data,
base::TimeDelta delay, base::TimeDelta delay,
int volume, int volume,
bool key_pressed, bool key_pressed,
bool need_audio_processing) { bool need_audio_processing,
bool force_report_nonzero_energy) {
DCHECK(capture_thread_checker_.CalledOnValidThread()); DCHECK(capture_thread_checker_.CalledOnValidThread());
// Calculate the signal level regardless if the track is disabled or enabled. // Calculate the signal level regardless if the track is disabled or enabled.
int signal_level = level_calculator_->Calculate( int signal_level = level_calculator_->Calculate(
audio_data, audio_parameters_.channels(), audio_data, audio_parameters_.channels(),
audio_parameters_.frames_per_buffer()); audio_parameters_.frames_per_buffer(), force_report_nonzero_energy);
adapter_->SetSignalLevel(signal_level); adapter_->SetSignalLevel(signal_level);
scoped_refptr<WebRtcAudioCapturer> capturer; scoped_refptr<WebRtcAudioCapturer> capturer;
......
...@@ -70,7 +70,8 @@ class CONTENT_EXPORT WebRtcLocalAudioTrack ...@@ -70,7 +70,8 @@ class CONTENT_EXPORT WebRtcLocalAudioTrack
base::TimeDelta delay, base::TimeDelta delay,
int volume, int volume,
bool key_pressed, bool key_pressed,
bool need_audio_processing); bool need_audio_processing,
bool force_report_nonzero_energy);
// Method called by the capturer to set the audio parameters used by source // Method called by the capturer to set the audio parameters used by source
// of the capture data.. // of the capture data..
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment