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 "media/audio/audio_manager_base.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "build/build_config.h"
14 #include "media/audio/audio_output_dispatcher_impl.h"
15 #include "media/audio/audio_output_proxy.h"
16 #include "media/audio/audio_output_resampler.h"
17 #include "media/audio/fake_audio_input_stream.h"
18 #include "media/audio/fake_audio_output_stream.h"
19 #include "media/base/media_switches.h"
23 static const int kStreamCloseDelaySeconds
= 5;
25 // Default maximum number of output streams that can be open simultaneously
27 static const int kDefaultMaxOutputStreams
= 16;
29 // Default maximum number of input streams that can be open simultaneously
31 static const int kDefaultMaxInputStreams
= 16;
33 static const int kMaxInputChannels
= 3;
35 const char AudioManagerBase::kDefaultDeviceName
[] = "Default";
36 const char AudioManagerBase::kDefaultDeviceId
[] = "default";
37 const char AudioManagerBase::kCommunicationsDeviceId
[] = "communications";
38 const char AudioManagerBase::kCommunicationsDeviceName
[] = "Communications";
39 const char AudioManagerBase::kLoopbackInputDeviceId
[] = "loopback";
41 struct AudioManagerBase::DispatcherParams
{
42 DispatcherParams(const AudioParameters
& input
,
43 const AudioParameters
& output
,
44 const std::string
& output_device_id
)
45 : input_params(input
),
46 output_params(output
),
47 output_device_id(output_device_id
) {}
48 ~DispatcherParams() {}
50 const AudioParameters input_params
;
51 const AudioParameters output_params
;
52 const std::string output_device_id
;
53 scoped_refptr
<AudioOutputDispatcher
> dispatcher
;
56 DISALLOW_COPY_AND_ASSIGN(DispatcherParams
);
59 class AudioManagerBase::CompareByParams
{
61 explicit CompareByParams(const DispatcherParams
* dispatcher
)
62 : dispatcher_(dispatcher
) {}
63 bool operator()(DispatcherParams
* dispatcher_in
) const {
64 // We will reuse the existing dispatcher when:
65 // 1) Unified IO is not used, input_params and output_params of the
66 // existing dispatcher are the same as the requested dispatcher.
67 // 2) Unified IO is used, input_params and output_params of the existing
68 // dispatcher are the same as the request dispatcher.
69 return (dispatcher_
->input_params
.Equals(dispatcher_in
->input_params
) &&
70 dispatcher_
->output_params
.Equals(dispatcher_in
->output_params
) &&
71 dispatcher_
->output_device_id
== dispatcher_in
->output_device_id
);
75 const DispatcherParams
* dispatcher_
;
78 AudioManagerBase::AudioManagerBase(AudioLogFactory
* audio_log_factory
)
79 : max_num_output_streams_(kDefaultMaxOutputStreams
),
80 max_num_input_streams_(kDefaultMaxInputStreams
),
81 num_output_streams_(0),
82 num_input_streams_(0),
83 // TODO(dalecurtis): Switch this to an base::ObserverListThreadSafe, so we
85 // block the UI thread when swapping devices.
87 base::ObserverList
<AudioDeviceListener
>::NOTIFY_EXISTING_ONLY
),
88 audio_thread_("AudioThread"),
89 audio_log_factory_(audio_log_factory
) {
91 audio_thread_
.init_com_with_mta(true);
92 #elif defined(OS_MACOSX)
93 // CoreAudio calls must occur on the main thread of the process, which in our
94 // case is sadly the browser UI thread. Failure to execute calls on the right
95 // thread leads to crashes and odd behavior. See http://crbug.com/158170.
96 // TODO(dalecurtis): We should require the message loop to be passed in.
97 if (base::MessageLoopForUI::IsCurrent()) {
98 task_runner_
= base::ThreadTaskRunnerHandle::Get();
103 CHECK(audio_thread_
.Start());
104 task_runner_
= audio_thread_
.task_runner();
107 AudioManagerBase::~AudioManagerBase() {
108 // The platform specific AudioManager implementation must have already
109 // stopped the audio thread. Otherwise, we may destroy audio streams before
110 // stopping the thread, resulting an unexpected behavior.
111 // This way we make sure activities of the audio streams are all stopped
112 // before we destroy them.
113 CHECK(!audio_thread_
.IsRunning());
114 // All the output streams should have been deleted.
115 DCHECK_EQ(0, num_output_streams_
);
116 // All the input streams should have been deleted.
117 DCHECK_EQ(0, num_input_streams_
);
120 base::string16
AudioManagerBase::GetAudioInputDeviceModel() {
121 return base::string16();
124 scoped_refptr
<base::SingleThreadTaskRunner
> AudioManagerBase::GetTaskRunner() {
128 scoped_refptr
<base::SingleThreadTaskRunner
>
129 AudioManagerBase::GetWorkerTaskRunner() {
130 // Lazily start the worker thread.
131 if (!audio_thread_
.IsRunning())
132 CHECK(audio_thread_
.Start());
134 return audio_thread_
.task_runner();
137 AudioOutputStream
* AudioManagerBase::MakeAudioOutputStream(
138 const AudioParameters
& params
,
139 const std::string
& device_id
) {
140 // TODO(miu): Fix ~50 call points across several unit test modules to call
141 // this method on the audio thread, then uncomment the following:
142 // DCHECK(task_runner_->BelongsToCurrentThread());
144 if (!params
.IsValid()) {
145 DLOG(ERROR
) << "Audio parameters are invalid";
149 // Limit the number of audio streams opened. This is to prevent using
150 // excessive resources for a large number of audio streams. More
151 // importantly it prevents instability on certain systems.
152 // See bug: http://crbug.com/30242.
153 if (num_output_streams_
>= max_num_output_streams_
) {
154 DLOG(ERROR
) << "Number of opened output audio streams "
155 << num_output_streams_
156 << " exceed the max allowed number "
157 << max_num_output_streams_
;
161 AudioOutputStream
* stream
;
162 switch (params
.format()) {
163 case AudioParameters::AUDIO_PCM_LINEAR
:
164 DCHECK(device_id
.empty())
165 << "AUDIO_PCM_LINEAR supports only the default device.";
166 stream
= MakeLinearOutputStream(params
);
168 case AudioParameters::AUDIO_PCM_LOW_LATENCY
:
169 stream
= MakeLowLatencyOutputStream(params
, device_id
);
171 case AudioParameters::AUDIO_FAKE
:
172 stream
= FakeAudioOutputStream::MakeFakeStream(this, params
);
180 ++num_output_streams_
;
186 AudioInputStream
* AudioManagerBase::MakeAudioInputStream(
187 const AudioParameters
& params
,
188 const std::string
& device_id
) {
189 // TODO(miu): Fix ~20 call points across several unit test modules to call
190 // this method on the audio thread, then uncomment the following:
191 // DCHECK(task_runner_->BelongsToCurrentThread());
193 if (!params
.IsValid() || (params
.channels() > kMaxInputChannels
) ||
195 DLOG(ERROR
) << "Audio parameters are invalid for device " << device_id
;
199 if (num_input_streams_
>= max_num_input_streams_
) {
200 DLOG(ERROR
) << "Number of opened input audio streams "
201 << num_input_streams_
202 << " exceed the max allowed number " << max_num_input_streams_
;
206 DVLOG(2) << "Creating a new AudioInputStream with buffer size = "
207 << params
.frames_per_buffer();
209 AudioInputStream
* stream
;
210 switch (params
.format()) {
211 case AudioParameters::AUDIO_PCM_LINEAR
:
212 stream
= MakeLinearInputStream(params
, device_id
);
214 case AudioParameters::AUDIO_PCM_LOW_LATENCY
:
215 stream
= MakeLowLatencyInputStream(params
, device_id
);
217 case AudioParameters::AUDIO_FAKE
:
218 stream
= FakeAudioInputStream::MakeFakeStream(this, params
);
226 ++num_input_streams_
;
232 AudioOutputStream
* AudioManagerBase::MakeAudioOutputStreamProxy(
233 const AudioParameters
& params
,
234 const std::string
& device_id
) {
235 DCHECK(task_runner_
->BelongsToCurrentThread());
237 // If the caller supplied an empty device id to select the default device,
238 // we fetch the actual device id of the default device so that the lookup
239 // will find the correct device regardless of whether it was opened as
240 // "default" or via the specific id.
241 // NOTE: Implementations that don't yet support opening non-default output
242 // devices may return an empty string from GetDefaultOutputDeviceID().
243 std::string output_device_id
= device_id
.empty() ?
244 GetDefaultOutputDeviceID() : device_id
;
246 // If we're not using AudioOutputResampler our output parameters are the same
247 // as our input parameters.
248 AudioParameters output_params
= params
;
249 if (params
.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY
) {
251 GetPreferredOutputStreamParameters(output_device_id
, params
);
253 // Ensure we only pass on valid output parameters.
254 if (!output_params
.IsValid()) {
255 // We've received invalid audio output parameters, so switch to a mock
256 // output device based on the input parameters. This may happen if the OS
257 // provided us junk values for the hardware configuration.
258 LOG(ERROR
) << "Invalid audio output parameters received; using fake "
259 << "audio path. Channels: " << output_params
.channels() << ", "
260 << "Sample Rate: " << output_params
.sample_rate() << ", "
261 << "Bits Per Sample: " << output_params
.bits_per_sample()
262 << ", Frames Per Buffer: "
263 << output_params
.frames_per_buffer();
265 // Tell the AudioManager to create a fake output device.
266 output_params
= params
;
267 output_params
.set_format(AudioParameters::AUDIO_FAKE
);
268 } else if (params
.effects() != output_params
.effects()) {
269 // Turn off effects that weren't requested.
270 output_params
.set_effects(params
.effects() & output_params
.effects());
274 DispatcherParams
* dispatcher_params
=
275 new DispatcherParams(params
, output_params
, output_device_id
);
277 AudioOutputDispatchers::iterator it
=
278 std::find_if(output_dispatchers_
.begin(), output_dispatchers_
.end(),
279 CompareByParams(dispatcher_params
));
280 if (it
!= output_dispatchers_
.end()) {
281 delete dispatcher_params
;
282 return new AudioOutputProxy((*it
)->dispatcher
.get());
285 const base::TimeDelta kCloseDelay
=
286 base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds
);
287 scoped_refptr
<AudioOutputDispatcher
> dispatcher
;
288 if (output_params
.format() != AudioParameters::AUDIO_FAKE
) {
289 dispatcher
= new AudioOutputResampler(this, params
, output_params
,
293 dispatcher
= new AudioOutputDispatcherImpl(this, output_params
,
298 dispatcher_params
->dispatcher
= dispatcher
;
299 output_dispatchers_
.push_back(dispatcher_params
);
300 return new AudioOutputProxy(dispatcher
.get());
303 void AudioManagerBase::ShowAudioInputSettings() {
306 void AudioManagerBase::GetAudioInputDeviceNames(
307 AudioDeviceNames
* device_names
) {
310 void AudioManagerBase::GetAudioOutputDeviceNames(
311 AudioDeviceNames
* device_names
) {
314 void AudioManagerBase::ReleaseOutputStream(AudioOutputStream
* stream
) {
316 // TODO(xians) : Have a clearer destruction path for the AudioOutputStream.
317 // For example, pass the ownership to AudioManager so it can delete the
319 --num_output_streams_
;
323 void AudioManagerBase::ReleaseInputStream(AudioInputStream
* stream
) {
325 // TODO(xians) : Have a clearer destruction path for the AudioInputStream.
326 --num_input_streams_
;
330 void AudioManagerBase::Shutdown() {
331 // Only true when we're sharing the UI message loop with the browser. The UI
332 // loop is no longer running at this time and browser destruction is imminent.
333 if (task_runner_
->BelongsToCurrentThread()) {
334 ShutdownOnAudioThread();
336 task_runner_
->PostTask(FROM_HERE
, base::Bind(
337 &AudioManagerBase::ShutdownOnAudioThread
, base::Unretained(this)));
340 // Stop() will wait for any posted messages to be processed first.
341 audio_thread_
.Stop();
344 void AudioManagerBase::ShutdownOnAudioThread() {
345 DCHECK(task_runner_
->BelongsToCurrentThread());
346 while (!output_dispatchers_
.empty()) {
347 output_dispatchers_
.back()->dispatcher
->Shutdown();
348 output_dispatchers_
.pop_back();
352 void AudioManagerBase::AddOutputDeviceChangeListener(
353 AudioDeviceListener
* listener
) {
354 DCHECK(task_runner_
->BelongsToCurrentThread());
355 output_listeners_
.AddObserver(listener
);
358 void AudioManagerBase::RemoveOutputDeviceChangeListener(
359 AudioDeviceListener
* listener
) {
360 DCHECK(task_runner_
->BelongsToCurrentThread());
361 output_listeners_
.RemoveObserver(listener
);
364 void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
365 DCHECK(task_runner_
->BelongsToCurrentThread());
366 DVLOG(1) << "Firing OnDeviceChange() notifications.";
367 FOR_EACH_OBSERVER(AudioDeviceListener
, output_listeners_
, OnDeviceChange());
370 AudioParameters
AudioManagerBase::GetDefaultOutputStreamParameters() {
371 return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(),
375 AudioParameters
AudioManagerBase::GetOutputStreamParameters(
376 const std::string
& device_id
) {
377 return GetPreferredOutputStreamParameters(device_id
,
381 AudioParameters
AudioManagerBase::GetInputStreamParameters(
382 const std::string
& device_id
) {
384 return AudioParameters();
387 std::string
AudioManagerBase::GetAssociatedOutputDeviceID(
388 const std::string
& input_device_id
) {
392 std::string
AudioManagerBase::GetDefaultOutputDeviceID() {
396 int AudioManagerBase::GetUserBufferSize() {
397 const base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
399 std::string
buffer_size_str(cmd_line
->GetSwitchValueASCII(
400 switches::kAudioBufferSize
));
401 if (base::StringToInt(buffer_size_str
, &buffer_size
) && buffer_size
> 0)
407 scoped_ptr
<AudioLog
> AudioManagerBase::CreateAudioLog(
408 AudioLogFactory::AudioComponent component
) {
409 return audio_log_factory_
->CreateAudioLog(component
);
412 void AudioManagerBase::SetHasKeyboardMic() {