1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/stream/processed_local_audio_source.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/renderer/media/audio_device_factory.h"
14 #include "content/renderer/media/stream/media_stream_audio_processor_options.h"
15 #include "content/renderer/media/stream/media_stream_constraints_util.h"
16 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
17 #include "content/renderer/media/webrtc/webrtc_audio_device_impl.h"
18 #include "content/renderer/media/webrtc_logging.h"
19 #include "content/renderer/render_frame_impl.h"
20 #include "media/base/channel_layout.h"
21 #include "media/base/sample_rates.h"
22 #include "third_party/webrtc/api/mediaconstraintsinterface.h"
23 #include "third_party/webrtc/media/base/mediachannel.h"
28 // Used as an identifier for ProcessedLocalAudioSource::From().
29 void* const kProcessedLocalAudioSourceIdentifier =
30 const_cast<void**>(&kProcessedLocalAudioSourceIdentifier);
33 ProcessedLocalAudioSource::ProcessedLocalAudioSource(
34 int consumer_render_frame_id,
35 const MediaStreamDevice& device,
37 bool disable_local_echo,
38 const AudioProcessingProperties& audio_processing_properties,
39 const ConstraintsCallback& started_callback,
40 PeerConnectionDependencyFactory* factory)
41 : MediaStreamAudioSource(true /* is_local_source */,
44 consumer_render_frame_id_(consumer_render_frame_id),
46 audio_processing_properties_(audio_processing_properties),
47 started_callback_(started_callback),
49 allow_invalid_render_frame_id_for_testing_(false) {
51 DVLOG(1) << "ProcessedLocalAudioSource::ProcessedLocalAudioSource()";
55 ProcessedLocalAudioSource::~ProcessedLocalAudioSource() {
56 DVLOG(1) << "ProcessedLocalAudioSource::~ProcessedLocalAudioSource()";
57 EnsureSourceIsStopped();
61 ProcessedLocalAudioSource* ProcessedLocalAudioSource::From(
62 MediaStreamAudioSource* source) {
64 source->GetClassIdentifier() == kProcessedLocalAudioSourceIdentifier)
65 return static_cast<ProcessedLocalAudioSource*>(source);
69 void* ProcessedLocalAudioSource::GetClassIdentifier() const {
70 return kProcessedLocalAudioSourceIdentifier;
73 bool ProcessedLocalAudioSource::EnsureSourceIsStarted() {
74 DCHECK(thread_checker_.CalledOnValidThread());
77 base::AutoLock auto_lock(source_lock_);
82 // Sanity-check that the consuming RenderFrame still exists. This is required
83 // to initialize the audio source.
84 if (!allow_invalid_render_frame_id_for_testing_ &&
85 !RenderFrameImpl::FromRoutingID(consumer_render_frame_id_)) {
86 WebRtcLogMessage("ProcessedLocalAudioSource::EnsureSourceIsStarted() fails "
87 " because the render frame does not exist.");
91 WebRtcLogMessage(base::StringPrintf(
92 "ProcessedLocalAudioSource::EnsureSourceIsStarted. render_frame_id=%d"
93 ", channel_layout=%d, sample_rate=%d, buffer_size=%d"
94 ", session_id=%d, effects=%d. ",
95 consumer_render_frame_id_, device().input.channel_layout(),
96 device().input.sample_rate(), device().input.frames_per_buffer(),
97 device().session_id, device().input.effects()));
99 MediaStreamDevice modified_device(device());
100 bool device_is_modified = false;
102 // Disable HW echo cancellation if constraints explicitly specified no
103 // echo cancellation.
104 if (audio_processing_properties_.disable_hw_echo_cancellation &&
105 (device().input.effects() & media::AudioParameters::ECHO_CANCELLER)) {
106 modified_device.input.set_effects(modified_device.input.effects() &
107 ~media::AudioParameters::ECHO_CANCELLER);
108 device_is_modified = true;
109 } else if (audio_processing_properties_
110 .enable_experimental_hw_echo_cancellation &&
111 (device().input.effects() &
112 media::AudioParameters::EXPERIMENTAL_ECHO_CANCELLER)) {
113 // Set the ECHO_CANCELLER effect, since that is what controls what's
114 // actually being used. The EXPERIMENTAL_ flag only indicates availability.
115 modified_device.input.set_effects(modified_device.input.effects() |
116 media::AudioParameters::ECHO_CANCELLER);
117 device_is_modified = true;
120 // Disable noise suppression on the device if the properties explicitly
122 if (audio_processing_properties_.disable_hw_noise_suppression &&
123 (device().input.effects() & media::AudioParameters::NOISE_SUPPRESSION)) {
124 modified_device.input.set_effects(
125 modified_device.input.effects() &
126 ~media::AudioParameters::NOISE_SUPPRESSION);
127 device_is_modified = true;
130 if (device_is_modified)
131 SetDevice(modified_device);
133 // Create the MediaStreamAudioProcessor, bound to the WebRTC audio device
135 WebRtcAudioDeviceImpl* const rtc_audio_device =
136 pc_factory_->GetWebRtcAudioDevice();
137 if (!rtc_audio_device) {
138 WebRtcLogMessage("ProcessedLocalAudioSource::EnsureSourceIsStarted() fails "
139 " because there is no WebRtcAudioDeviceImpl instance.");
142 audio_processor_ = new rtc::RefCountedObject<MediaStreamAudioProcessor>(
143 audio_processing_properties_, rtc_audio_device);
145 // If KEYBOARD_MIC effect is set, change the layout to the corresponding
146 // layout that includes the keyboard mic.
147 media::ChannelLayout channel_layout = device().input.channel_layout();
148 if ((device().input.effects() & media::AudioParameters::KEYBOARD_MIC) &&
149 audio_processing_properties_.goog_experimental_noise_suppression) {
150 if (channel_layout == media::CHANNEL_LAYOUT_STEREO) {
151 channel_layout = media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
152 DVLOG(1) << "Changed stereo layout to stereo + keyboard mic layout due "
153 << "to KEYBOARD_MIC effect.";
155 DVLOG(1) << "KEYBOARD_MIC effect ignored, not compatible with layout "
160 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout;
161 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout",
162 channel_layout, media::CHANNEL_LAYOUT_MAX + 1);
164 // Verify that the reported input channel configuration is supported.
165 if (channel_layout != media::CHANNEL_LAYOUT_MONO &&
166 channel_layout != media::CHANNEL_LAYOUT_STEREO &&
167 channel_layout != media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
168 WebRtcLogMessage(base::StringPrintf(
169 "ProcessedLocalAudioSource::EnsureSourceIsStarted() fails "
170 " because the input channel layout (%d) is not supported.",
171 static_cast<int>(channel_layout)));
175 DVLOG(1) << "Audio input hardware sample rate: "
176 << device().input.sample_rate();
177 media::AudioSampleRate asr;
178 if (media::ToAudioSampleRate(device().input.sample_rate(), &asr)) {
179 UMA_HISTOGRAM_ENUMERATION(
180 "WebRTC.AudioInputSampleRate", asr, media::kAudioSampleRateMax + 1);
182 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected",
183 device().input.sample_rate());
186 // Determine the audio format required of the AudioCapturerSource. Then, pass
187 // that to the |audio_processor_| and set the output format of this
188 // ProcessedLocalAudioSource to the processor's output format.
189 media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
190 channel_layout, device().input.sample_rate(),
192 GetBufferSize(device().input.sample_rate()));
193 params.set_effects(device().input.effects());
194 DCHECK(params.IsValid());
195 audio_processor_->OnCaptureFormatChanged(params);
196 SetFormat(audio_processor_->OutputFormat());
199 VLOG(1) << "Starting WebRTC audio source for consumption by render frame "
200 << consumer_render_frame_id_ << " with input parameters={"
201 << params.AsHumanReadableString() << "} and output parameters={"
202 << GetAudioParameters().AsHumanReadableString() << '}';
203 scoped_refptr<media::AudioCapturerSource> new_source =
204 AudioDeviceFactory::NewAudioCapturerSource(consumer_render_frame_id_);
205 new_source->Initialize(params, this, device().session_id);
206 // We need to set the AGC control before starting the stream.
207 new_source->SetAutomaticGainControl(true);
209 base::AutoLock auto_lock(source_lock_);
210 source_ = std::move(new_source);
214 // Register this source with the WebRtcAudioDeviceImpl.
215 rtc_audio_device->AddAudioCapturer(this);
220 void ProcessedLocalAudioSource::EnsureSourceIsStopped() {
221 DCHECK(thread_checker_.CalledOnValidThread());
223 scoped_refptr<media::AudioCapturerSource> source_to_stop;
225 base::AutoLock auto_lock(source_lock_);
228 source_to_stop = std::move(source_);
231 if (WebRtcAudioDeviceImpl* rtc_audio_device =
232 pc_factory_->GetWebRtcAudioDevice()) {
233 rtc_audio_device->RemoveAudioCapturer(this);
236 source_to_stop->Stop();
238 // Stop the audio processor to avoid feeding render data into the processor.
239 audio_processor_->Stop();
241 VLOG(1) << "Stopped WebRTC audio pipeline for consumption by render frame "
242 << consumer_render_frame_id_ << '.';
245 void ProcessedLocalAudioSource::SetVolume(int volume) {
246 DVLOG(1) << "ProcessedLocalAudioSource::SetVolume()";
247 DCHECK_LE(volume, MaxVolume());
249 const double normalized_volume = static_cast<double>(volume) / MaxVolume();
251 // Hold a strong reference to |source_| while its SetVolume() method is
252 // called. This will prevent the object from being destroyed on another thread
253 // in the meantime. It's possible the |source_| will be stopped on another
254 // thread while calling SetVolume() here; but this is safe: The operation will
255 // simply be ignored.
256 scoped_refptr<media::AudioCapturerSource> maybe_source;
258 base::AutoLock auto_lock(source_lock_);
259 maybe_source = source_;
262 maybe_source->SetVolume(normalized_volume);
265 int ProcessedLocalAudioSource::Volume() const {
266 // Note: Using NoBarrier_Load() because the timing of visibility of the
267 // updated volume information on other threads can be relaxed.
268 return base::subtle::NoBarrier_Load(&volume_);
271 int ProcessedLocalAudioSource::MaxVolume() const {
272 return WebRtcAudioDeviceImpl::kMaxVolumeLevel;
275 void ProcessedLocalAudioSource::OnCaptureStarted() {
276 started_callback_.Run(this, MEDIA_DEVICE_OK, "");
279 void ProcessedLocalAudioSource::Capture(const media::AudioBus* audio_bus,
280 int audio_delay_milliseconds,
283 #if defined(OS_WIN) || defined(OS_MACOSX)
284 DCHECK_LE(volume, 1.0);
285 #elif (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
286 // We have a special situation on Linux where the microphone volume can be
287 // "higher than maximum". The input volume slider in the sound preference
288 // allows the user to set a scaling that is higher than 100%. It means that
289 // even if the reported maximum levels is N, the actual microphone level can
290 // go up to 1.5x*N and that corresponds to a normalized |volume| of 1.5x.
291 DCHECK_LE(volume, 1.6);
294 // TODO(miu): Plumbing is needed to determine the actual capture timestamp
295 // of the audio, instead of just snapshotting TimeTicks::Now(), for proper
296 // audio/video sync. http://crbug.com/335335
297 const base::TimeTicks reference_clock_snapshot = base::TimeTicks::Now();
298 TRACE_EVENT2("audio", "ProcessedLocalAudioSource::Capture", "now (ms)",
299 (reference_clock_snapshot - base::TimeTicks()).InMillisecondsF(),
300 "delay (ms)", audio_delay_milliseconds);
302 // Map internal volume range of [0.0, 1.0] into [0, 255] used by AGC.
303 // The volume can be higher than 255 on Linux, and it will be cropped to
304 // 255 since AGC does not allow values out of range.
305 int current_volume = static_cast<int>((volume * MaxVolume()) + 0.5);
306 // Note: Using NoBarrier_Store() because the timing of visibility of the
307 // updated volume information on other threads can be relaxed.
308 base::subtle::NoBarrier_Store(&volume_, current_volume);
309 current_volume = std::min(current_volume, MaxVolume());
311 // Sanity-check the input audio format in debug builds. Then, notify the
312 // tracks if the format has changed.
314 // Locking is not needed here to read the audio input/output parameters
315 // because the audio processor format changes only occur while audio capture
317 DCHECK(audio_processor_->InputFormat().IsValid());
318 DCHECK_EQ(audio_bus->channels(), audio_processor_->InputFormat().channels());
319 DCHECK_EQ(audio_bus->frames(),
320 audio_processor_->InputFormat().frames_per_buffer());
322 // Figure out if the pre-processed data has any energy or not. This
323 // information will be passed to the level calculator to force it to report
324 // energy in case the post-processed data is zeroed by the audio processing.
325 const bool force_report_nonzero_energy = !audio_bus->AreFramesZero();
327 // Push the data to the processor for processing.
328 audio_processor_->PushCaptureData(
330 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds));
332 // Process and consume the data in the processor until there is not enough
333 // data in the processor.
334 media::AudioBus* processed_data = nullptr;
335 base::TimeDelta processed_data_audio_delay;
337 while (audio_processor_->ProcessAndConsumeData(
338 current_volume, key_pressed,
339 &processed_data, &processed_data_audio_delay, &new_volume)) {
340 DCHECK(processed_data);
342 level_calculator_.Calculate(*processed_data, force_report_nonzero_energy);
344 DeliverDataToTracks(*processed_data,
345 reference_clock_snapshot - processed_data_audio_delay);
348 SetVolume(new_volume);
350 // Update the |current_volume| to avoid passing the old volume to AGC.
351 current_volume = new_volume;
356 void ProcessedLocalAudioSource::OnCaptureError(const std::string& message) {
357 WebRtcLogMessage("ProcessedLocalAudioSource::OnCaptureError: " + message);
358 StopSourceOnError(message);
361 void ProcessedLocalAudioSource::OnCaptureMuted(bool is_muted) {
362 SetMutedState(is_muted);
365 media::AudioParameters ProcessedLocalAudioSource::GetInputFormat() const {
366 return audio_processor_ ? audio_processor_->InputFormat()
367 : media::AudioParameters();
370 int ProcessedLocalAudioSource::GetBufferSize(int sample_rate) const {
371 DCHECK(thread_checker_.CalledOnValidThread());
372 #if defined(OS_ANDROID)
373 // TODO(henrika): Re-evaluate whether to use same logic as other platforms.
374 // http://crbug.com/638081
375 return (2 * sample_rate / 100);
378 // If audio processing is turned on, require 10ms buffers.
379 if (audio_processor_->has_audio_processing())
380 return (sample_rate / 100);
382 // If audio processing is off and the native hardware buffer size was
383 // provided, use it. It can be harmful, in terms of CPU/power consumption, to
384 // use smaller buffer sizes than the native size (http://crbug.com/362261).
385 if (int hardware_buffer_size = device().input.frames_per_buffer())
386 return hardware_buffer_size;
388 // If the buffer size is missing from the MediaStreamDevice, provide 10ms as a
391 // TODO(miu): Identify where/why the buffer size might be missing, fix the
392 // code, and then require it here. http://crbug.com/638081
393 return (sample_rate / 100);
396 } // namespace content