1 // Copyright 2013 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/webrtc_audio_device_impl.h"
8 #include "base/metrics/histogram.h"
9 #include "base/string_util.h"
10 #include "base/win/windows_version.h"
11 #include "content/renderer/media/webrtc_audio_capturer.h"
12 #include "content/renderer/media/webrtc_audio_renderer.h"
13 #include "content/renderer/render_thread_impl.h"
14 #include "media/audio/audio_parameters.h"
15 #include "media/audio/audio_util.h"
16 #include "media/audio/sample_rates.h"
18 using media::AudioParameters
;
19 using media::ChannelLayout
;
25 const double kMaxVolumeLevel
= 255.0;
29 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()
31 audio_transport_callback_(NULL
),
37 agc_is_enabled_(false),
38 microphone_volume_(0) {
39 DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()";
42 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() {
43 DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()";
44 DCHECK(thread_checker_
.CalledOnValidThread());
48 int32_t WebRtcAudioDeviceImpl::AddRef() {
49 DCHECK(thread_checker_
.CalledOnValidThread());
50 return base::subtle::Barrier_AtomicIncrement(&ref_count_
, 1);
53 int32_t WebRtcAudioDeviceImpl::Release() {
54 DCHECK(thread_checker_
.CalledOnValidThread());
55 int ret
= base::subtle::Barrier_AtomicIncrement(&ref_count_
, -1);
62 void WebRtcAudioDeviceImpl::CaptureData(const int16
* audio_data
,
63 int number_of_channels
,
65 int audio_delay_milliseconds
,
67 DCHECK_LE(number_of_frames
, input_buffer_size());
68 #if defined(OS_WIN) || defined(OS_MACOSX)
69 DCHECK_LE(volume
, 1.0);
70 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
71 // We have a special situation on Linux where the microphone volume can be
72 // "higher than maximum". The input volume slider in the sound preference
73 // allows the user to set a scaling that is higher than 100%. It means that
74 // even if the reported maximum levels is N, the actual microphone level can
75 // go up to 1.5x*N and that corresponds to a normalized |volume| of 1.5x.
76 DCHECK_LE(volume
, 1.6);
79 media::AudioParameters input_audio_parameters
;
80 int output_delay_ms
= 0;
82 base::AutoLock
auto_lock(lock_
);
86 // Take a copy of the input parameters while we are under a lock.
87 input_audio_parameters
= input_audio_parameters_
;
89 // Store the reported audio delay locally.
90 input_delay_ms_
= audio_delay_milliseconds
;
91 output_delay_ms
= output_delay_ms_
;
93 // Map internal volume range of [0.0, 1.0] into [0, 255] used by the
94 // webrtc::VoiceEngine.
95 microphone_volume_
= static_cast<uint32_t>(volume
* kMaxVolumeLevel
);
98 const int channels
= number_of_channels
;
99 DCHECK_LE(channels
, input_channels());
100 uint32_t new_mic_level
= 0;
102 int samples_per_sec
= input_sample_rate();
103 if (samples_per_sec
== 44100) {
104 // Even if the hardware runs at 44.1kHz, we use 44.0 internally.
105 samples_per_sec
= 44000;
107 const int samples_per_10_msec
= (samples_per_sec
/ 100);
108 int bytes_per_sample
= input_audio_parameters
.bits_per_sample() / 8;
109 const int bytes_per_10_msec
=
110 channels
* samples_per_10_msec
* bytes_per_sample
;
111 int accumulated_audio_samples
= 0;
113 const uint8
* audio_byte_buffer
= reinterpret_cast<const uint8
*>(audio_data
);
115 // Write audio samples in blocks of 10 milliseconds to the registered
116 // webrtc::AudioTransport sink. Keep writing until our internal byte
118 while (accumulated_audio_samples
< number_of_frames
) {
119 // Deliver 10ms of recorded 16-bit linear PCM audio.
120 audio_transport_callback_
->RecordedDataIsAvailable(
126 input_delay_ms_
+ output_delay_ms
,
127 0, // TODO(henrika): |clock_drift| parameter is not utilized today.
131 accumulated_audio_samples
+= samples_per_10_msec
;
132 audio_byte_buffer
+= bytes_per_10_msec
;
135 // The AGC returns a non-zero microphone level if it has been decided
136 // that a new level should be set.
137 if (new_mic_level
!= 0) {
138 // Use IPC and set the new level. Note that, it will take some time
139 // before the new level is effective due to the IPC scheme.
140 // During this time, |current_mic_level| will contain "non-valid" values
141 // and it might reduce the AGC performance. Measurements on Windows 7 have
142 // shown that we might receive old volume levels for one or two callbacks.
143 SetMicrophoneVolume(new_mic_level
);
147 void WebRtcAudioDeviceImpl::SetCaptureFormat(
148 const media::AudioParameters
& params
) {
149 DVLOG(1) << "WebRtcAudioDeviceImpl::SetCaptureFormat()";
150 DCHECK(thread_checker_
.CalledOnValidThread());
151 base::AutoLock
auto_lock(lock_
);
152 input_audio_parameters_
= params
;
155 void WebRtcAudioDeviceImpl::RenderData(uint8
* audio_data
,
156 int number_of_channels
,
157 int number_of_frames
,
158 int audio_delay_milliseconds
) {
159 DCHECK_LE(number_of_frames
, output_buffer_size());
161 base::AutoLock
auto_lock(lock_
);
162 // Store the reported audio delay locally.
163 output_delay_ms_
= audio_delay_milliseconds
;
166 const int channels
= number_of_channels
;
167 DCHECK_LE(channels
, output_channels());
169 int samples_per_sec
= output_sample_rate();
170 if (samples_per_sec
== 44100) {
171 // Even if the hardware runs at 44.1kHz, we use 44.0 internally.
172 samples_per_sec
= 44000;
174 int samples_per_10_msec
= (samples_per_sec
/ 100);
175 int bytes_per_sample
= output_audio_parameters_
.bits_per_sample() / 8;
176 const int bytes_per_10_msec
=
177 channels
* samples_per_10_msec
* bytes_per_sample
;
179 uint32_t num_audio_samples
= 0;
180 int accumulated_audio_samples
= 0;
182 // Get audio samples in blocks of 10 milliseconds from the registered
183 // webrtc::AudioTransport source. Keep reading until our internal buffer
185 while (accumulated_audio_samples
< number_of_frames
) {
186 // Get 10ms and append output to temporary byte buffer.
187 audio_transport_callback_
->NeedMorePlayData(samples_per_10_msec
,
193 accumulated_audio_samples
+= num_audio_samples
;
194 audio_data
+= bytes_per_10_msec
;
198 void WebRtcAudioDeviceImpl::SetRenderFormat(const AudioParameters
& params
) {
199 DCHECK(thread_checker_
.CalledOnValidThread());
200 output_audio_parameters_
= params
;
203 void WebRtcAudioDeviceImpl::RemoveAudioRenderer(WebRtcAudioRenderer
* renderer
) {
204 DCHECK(thread_checker_
.CalledOnValidThread());
205 DCHECK_EQ(renderer
, renderer_
);
206 base::AutoLock
auto_lock(lock_
);
211 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback(
212 webrtc::AudioTransport
* audio_callback
) {
213 DVLOG(1) << "WebRtcAudioDeviceImpl::RegisterAudioCallback()";
214 DCHECK(thread_checker_
.CalledOnValidThread());
215 DCHECK_EQ(audio_transport_callback_
== NULL
, audio_callback
!= NULL
);
216 audio_transport_callback_
= audio_callback
;
220 int32_t WebRtcAudioDeviceImpl::Init() {
221 DVLOG(1) << "WebRtcAudioDeviceImpl::Init()";
222 DCHECK(thread_checker_
.CalledOnValidThread());
228 capturer_
= WebRtcAudioCapturer::CreateCapturer();
230 capturer_
->AddCapturerSink(this);
232 // We need to return a success to continue the initialization of WebRtc VoE
233 // because failure on the capturer_ initialization should not prevent WebRTC
234 // from working. See issue http://crbug.com/144421 for details.
240 int32_t WebRtcAudioDeviceImpl::Terminate() {
241 DVLOG(1) << "WebRtcAudioDeviceImpl::Terminate()";
242 DCHECK(thread_checker_
.CalledOnValidThread());
244 // Calling Terminate() multiple times in a row is OK.
251 // It is necessary to stop the |renderer_| before going away.
258 // |capturer_| is stopped by the media stream, so do not need to
260 capturer_
->RemoveCapturerSink(this);
264 initialized_
= false;
268 bool WebRtcAudioDeviceImpl::Initialized() const {
272 int32_t WebRtcAudioDeviceImpl::PlayoutIsAvailable(bool* available
) {
273 *available
= initialized_
;
277 bool WebRtcAudioDeviceImpl::PlayoutIsInitialized() const {
281 int32_t WebRtcAudioDeviceImpl::RecordingIsAvailable(bool* available
) {
282 *available
= (capturer_
!= NULL
);
286 bool WebRtcAudioDeviceImpl::RecordingIsInitialized() const {
287 DVLOG(1) << "WebRtcAudioDeviceImpl::RecordingIsInitialized()";
288 DCHECK(thread_checker_
.CalledOnValidThread());
289 return (capturer_
!= NULL
);
292 int32_t WebRtcAudioDeviceImpl::StartPlayout() {
293 DVLOG(1) << "WebRtcAudioDeviceImpl::StartPlayout()";
294 LOG_IF(ERROR
, !audio_transport_callback_
) << "Audio transport is missing";
296 base::AutoLock
auto_lock(lock_
);
297 if (!audio_transport_callback_
)
302 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and
303 // that the call is ignored the second time.
308 start_render_time_
= base::Time::Now();
312 int32_t WebRtcAudioDeviceImpl::StopPlayout() {
313 DVLOG(1) << "WebRtcAudioDeviceImpl::StopPlayout()";
315 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case.
319 // Add histogram data to be uploaded as part of an UMA logging event.
320 // This histogram keeps track of total playout times.
321 if (!start_render_time_
.is_null()) {
322 base::TimeDelta render_time
= base::Time::Now() - start_render_time_
;
323 UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioRenderTime", render_time
);
330 bool WebRtcAudioDeviceImpl::Playing() const {
334 int32_t WebRtcAudioDeviceImpl::StartRecording() {
335 DVLOG(1) << "WebRtcAudioDeviceImpl::StartRecording()";
336 DCHECK(initialized_
);
337 LOG_IF(ERROR
, !audio_transport_callback_
) << "Audio transport is missing";
338 if (!audio_transport_callback_
) {
342 start_capture_time_
= base::Time::Now();
344 base::AutoLock
auto_lock(lock_
);
350 int32_t WebRtcAudioDeviceImpl::StopRecording() {
351 DVLOG(1) << "WebRtcAudioDeviceImpl::StopRecording()";
356 // Add histogram data to be uploaded as part of an UMA logging event.
357 // This histogram keeps track of total recording times.
358 if (!start_capture_time_
.is_null()) {
359 base::TimeDelta capture_time
= base::Time::Now() - start_capture_time_
;
360 UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioCaptureTime", capture_time
);
363 base::AutoLock
auto_lock(lock_
);
369 bool WebRtcAudioDeviceImpl::Recording() const {
370 base::AutoLock
auto_lock(lock_
);
374 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable
) {
375 DVLOG(1) << "WebRtcAudioDeviceImpl::SetAGC(enable=" << enable
<< ")";
376 DCHECK(initialized_
);
378 // Return early if we are not changing the AGC state.
379 if (enable
== agc_is_enabled_
)
382 // The current implementation does not support changing the AGC state while
383 // recording. Using this approach simplifies the design and it is also
384 // inline with the latest WebRTC standard.
385 if (!capturer_
|| capturer_
->is_recording())
388 capturer_
->SetAutomaticGainControl(enable
);
389 agc_is_enabled_
= enable
;
393 bool WebRtcAudioDeviceImpl::AGC() const {
394 DVLOG(1) << "WebRtcAudioDeviceImpl::AGC()";
395 DCHECK(thread_checker_
.CalledOnValidThread());
396 // To reduce the usage of IPC messages, an internal AGC state is used.
397 // TODO(henrika): investigate if there is a need for a "deeper" getter.
398 return agc_is_enabled_
;
401 int32_t WebRtcAudioDeviceImpl::SetMicrophoneVolume(uint32_t volume
) {
402 DVLOG(1) << "WebRtcAudioDeviceImpl::SetMicrophoneVolume(" << volume
<< ")";
403 DCHECK(initialized_
);
407 if (volume
> kMaxVolumeLevel
)
410 // WebRTC uses a range of [0, 255] to represent the level of the microphone
411 // volume. The IPC channel between the renderer and browser process works
412 // with doubles in the [0.0, 1.0] range and we have to compensate for that.
413 double normalized_volume
= static_cast<double>(volume
) / kMaxVolumeLevel
;
414 capturer_
->SetVolume(normalized_volume
);
418 // TODO(henrika): sort out calling thread once we start using this API.
419 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume
) const {
420 DVLOG(1) << "WebRtcAudioDeviceImpl::MicrophoneVolume()";
421 // The microphone level is fed to this class using the Capture() callback
422 // and cached in the same method, i.e. we don't ask the native audio layer
423 // for the actual micropone level here.
424 DCHECK(initialized_
);
427 base::AutoLock
auto_lock(lock_
);
428 *volume
= microphone_volume_
;
432 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume(uint32_t* max_volume
) const {
433 *max_volume
= kMaxVolumeLevel
;
437 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(uint32_t* min_volume
) const {
442 int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available
) const {
443 DCHECK(initialized_
);
444 *available
= (output_channels() == 2);
448 int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable(
449 bool* available
) const {
450 DCHECK(initialized_
);
453 *available
= (input_channels() == 2);
457 int32_t WebRtcAudioDeviceImpl::PlayoutDelay(uint16_t* delay_ms
) const {
458 base::AutoLock
auto_lock(lock_
);
459 *delay_ms
= static_cast<uint16_t>(output_delay_ms_
);
463 int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms
) const {
464 base::AutoLock
auto_lock(lock_
);
465 *delay_ms
= static_cast<uint16_t>(input_delay_ms_
);
469 int32_t WebRtcAudioDeviceImpl::RecordingSampleRate(
470 uint32_t* samples_per_sec
) const {
471 *samples_per_sec
= static_cast<uint32_t>(input_sample_rate());
475 int32_t WebRtcAudioDeviceImpl::PlayoutSampleRate(
476 uint32_t* samples_per_sec
) const {
477 *samples_per_sec
= static_cast<uint32_t>(output_sample_rate());
481 bool WebRtcAudioDeviceImpl::SetAudioRenderer(WebRtcAudioRenderer
* renderer
) {
482 DCHECK(thread_checker_
.CalledOnValidThread());
485 base::AutoLock
auto_lock(lock_
);
489 if (!renderer
->Initialize(this))
492 renderer_
= renderer
;
496 } // namespace content