• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "media/audio/audio_manager_base.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "build/build_config.h"
12 #include "media/audio/audio_output_dispatcher_impl.h"
13 #include "media/audio/audio_output_proxy.h"
14 #include "media/audio/audio_output_resampler.h"
15 #include "media/audio/fake_audio_input_stream.h"
16 #include "media/audio/fake_audio_output_stream.h"
17 #include "media/base/media_switches.h"
18 
19 namespace media {
20 
21 static const int kStreamCloseDelaySeconds = 5;
22 
23 // Default maximum number of output streams that can be open simultaneously
24 // for all platforms.
25 static const int kDefaultMaxOutputStreams = 16;
26 
27 // Default maximum number of input streams that can be open simultaneously
28 // for all platforms.
29 static const int kDefaultMaxInputStreams = 16;
30 
31 static const int kMaxInputChannels = 3;
32 
33 const char AudioManagerBase::kDefaultDeviceName[] = "Default";
34 const char AudioManagerBase::kDefaultDeviceId[] = "default";
35 const char AudioManagerBase::kLoopbackInputDeviceId[] = "loopback";
36 
37 struct AudioManagerBase::DispatcherParams {
DispatcherParamsmedia::AudioManagerBase::DispatcherParams38   DispatcherParams(const AudioParameters& input,
39                    const AudioParameters& output,
40                    const std::string& output_device_id)
41       : input_params(input),
42         output_params(output),
43         output_device_id(output_device_id) {}
~DispatcherParamsmedia::AudioManagerBase::DispatcherParams44   ~DispatcherParams() {}
45 
46   const AudioParameters input_params;
47   const AudioParameters output_params;
48   const std::string output_device_id;
49   scoped_refptr<AudioOutputDispatcher> dispatcher;
50 
51  private:
52   DISALLOW_COPY_AND_ASSIGN(DispatcherParams);
53 };
54 
55 class AudioManagerBase::CompareByParams {
56  public:
CompareByParams(const DispatcherParams * dispatcher)57   explicit CompareByParams(const DispatcherParams* dispatcher)
58       : dispatcher_(dispatcher) {}
operator ()(DispatcherParams * dispatcher_in) const59   bool operator()(DispatcherParams* dispatcher_in) const {
60     // We will reuse the existing dispatcher when:
61     // 1) Unified IO is not used, input_params and output_params of the
62     //    existing dispatcher are the same as the requested dispatcher.
63     // 2) Unified IO is used, input_params and output_params of the existing
64     //    dispatcher are the same as the request dispatcher.
65     return (dispatcher_->input_params.Equals(dispatcher_in->input_params) &&
66             dispatcher_->output_params.Equals(dispatcher_in->output_params) &&
67             dispatcher_->output_device_id == dispatcher_in->output_device_id);
68   }
69 
70  private:
71   const DispatcherParams* dispatcher_;
72 };
73 
AudioManagerBase(AudioLogFactory * audio_log_factory)74 AudioManagerBase::AudioManagerBase(AudioLogFactory* audio_log_factory)
75     : max_num_output_streams_(kDefaultMaxOutputStreams),
76       max_num_input_streams_(kDefaultMaxInputStreams),
77       num_output_streams_(0),
78       num_input_streams_(0),
79       // TODO(dalecurtis): Switch this to an ObserverListThreadSafe, so we don't
80       // block the UI thread when swapping devices.
81       output_listeners_(
82           ObserverList<AudioDeviceListener>::NOTIFY_EXISTING_ONLY),
83       audio_thread_("AudioThread"),
84       audio_log_factory_(audio_log_factory) {
85 #if defined(OS_WIN)
86   audio_thread_.init_com_with_mta(true);
87 #elif defined(OS_MACOSX)
88   // CoreAudio calls must occur on the main thread of the process, which in our
89   // case is sadly the browser UI thread.  Failure to execute calls on the right
90   // thread leads to crashes and odd behavior.  See http://crbug.com/158170.
91   // TODO(dalecurtis): We should require the message loop to be passed in.
92   if (base::MessageLoopForUI::IsCurrent()) {
93     task_runner_ = base::MessageLoopProxy::current();
94     return;
95   }
96 #endif
97 
98   CHECK(audio_thread_.Start());
99   task_runner_ = audio_thread_.message_loop_proxy();
100 }
101 
~AudioManagerBase()102 AudioManagerBase::~AudioManagerBase() {
103   // The platform specific AudioManager implementation must have already
104   // stopped the audio thread. Otherwise, we may destroy audio streams before
105   // stopping the thread, resulting an unexpected behavior.
106   // This way we make sure activities of the audio streams are all stopped
107   // before we destroy them.
108   CHECK(!audio_thread_.IsRunning());
109   // All the output streams should have been deleted.
110   DCHECK_EQ(0, num_output_streams_);
111   // All the input streams should have been deleted.
112   DCHECK_EQ(0, num_input_streams_);
113 }
114 
GetAudioInputDeviceModel()115 base::string16 AudioManagerBase::GetAudioInputDeviceModel() {
116   return base::string16();
117 }
118 
GetTaskRunner()119 scoped_refptr<base::SingleThreadTaskRunner> AudioManagerBase::GetTaskRunner() {
120   return task_runner_;
121 }
122 
123 scoped_refptr<base::SingleThreadTaskRunner>
GetWorkerTaskRunner()124 AudioManagerBase::GetWorkerTaskRunner() {
125   // Lazily start the worker thread.
126   if (!audio_thread_.IsRunning())
127     CHECK(audio_thread_.Start());
128 
129   return audio_thread_.message_loop_proxy();
130 }
131 
MakeAudioOutputStream(const AudioParameters & params,const std::string & device_id)132 AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
133     const AudioParameters& params,
134     const std::string& device_id) {
135   // TODO(miu): Fix ~50 call points across several unit test modules to call
136   // this method on the audio thread, then uncomment the following:
137   // DCHECK(task_runner_->BelongsToCurrentThread());
138 
139   if (!params.IsValid()) {
140     DLOG(ERROR) << "Audio parameters are invalid";
141     return NULL;
142   }
143 
144   // Limit the number of audio streams opened. This is to prevent using
145   // excessive resources for a large number of audio streams. More
146   // importantly it prevents instability on certain systems.
147   // See bug: http://crbug.com/30242.
148   if (num_output_streams_ >= max_num_output_streams_) {
149     DLOG(ERROR) << "Number of opened output audio streams "
150                 << num_output_streams_
151                 << " exceed the max allowed number "
152                 << max_num_output_streams_;
153     return NULL;
154   }
155 
156   AudioOutputStream* stream;
157   switch (params.format()) {
158     case AudioParameters::AUDIO_PCM_LINEAR:
159       DCHECK(device_id.empty())
160           << "AUDIO_PCM_LINEAR supports only the default device.";
161       stream = MakeLinearOutputStream(params);
162       break;
163     case AudioParameters::AUDIO_PCM_LOW_LATENCY:
164       stream = MakeLowLatencyOutputStream(params, device_id);
165       break;
166     case AudioParameters::AUDIO_FAKE:
167       stream = FakeAudioOutputStream::MakeFakeStream(this, params);
168       break;
169     default:
170       stream = NULL;
171       break;
172   }
173 
174   if (stream) {
175     ++num_output_streams_;
176   }
177 
178   return stream;
179 }
180 
MakeAudioInputStream(const AudioParameters & params,const std::string & device_id)181 AudioInputStream* AudioManagerBase::MakeAudioInputStream(
182     const AudioParameters& params,
183     const std::string& device_id) {
184   // TODO(miu): Fix ~20 call points across several unit test modules to call
185   // this method on the audio thread, then uncomment the following:
186   // DCHECK(task_runner_->BelongsToCurrentThread());
187 
188   if (!params.IsValid() || (params.channels() > kMaxInputChannels) ||
189       device_id.empty()) {
190     DLOG(ERROR) << "Audio parameters are invalid for device " << device_id;
191     return NULL;
192   }
193 
194   if (num_input_streams_ >= max_num_input_streams_) {
195     DLOG(ERROR) << "Number of opened input audio streams "
196                 << num_input_streams_
197                 << " exceed the max allowed number " << max_num_input_streams_;
198     return NULL;
199   }
200 
201   AudioInputStream* stream;
202   switch (params.format()) {
203     case AudioParameters::AUDIO_PCM_LINEAR:
204       stream = MakeLinearInputStream(params, device_id);
205       break;
206     case AudioParameters::AUDIO_PCM_LOW_LATENCY:
207       stream = MakeLowLatencyInputStream(params, device_id);
208       break;
209     case AudioParameters::AUDIO_FAKE:
210       stream = FakeAudioInputStream::MakeFakeStream(this, params);
211       break;
212     default:
213       stream = NULL;
214       break;
215   }
216 
217   if (stream) {
218     ++num_input_streams_;
219   }
220 
221   return stream;
222 }
223 
MakeAudioOutputStreamProxy(const AudioParameters & params,const std::string & device_id)224 AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy(
225     const AudioParameters& params,
226     const std::string& device_id) {
227   DCHECK(task_runner_->BelongsToCurrentThread());
228 
229   // If the caller supplied an empty device id to select the default device,
230   // we fetch the actual device id of the default device so that the lookup
231   // will find the correct device regardless of whether it was opened as
232   // "default" or via the specific id.
233   // NOTE: Implementations that don't yet support opening non-default output
234   // devices may return an empty string from GetDefaultOutputDeviceID().
235   std::string output_device_id = device_id.empty() ?
236       GetDefaultOutputDeviceID() : device_id;
237 
238   // If we're not using AudioOutputResampler our output parameters are the same
239   // as our input parameters.
240   AudioParameters output_params = params;
241   if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
242     output_params =
243         GetPreferredOutputStreamParameters(output_device_id, params);
244 
245     // Ensure we only pass on valid output parameters.
246     if (!output_params.IsValid()) {
247       // We've received invalid audio output parameters, so switch to a mock
248       // output device based on the input parameters.  This may happen if the OS
249       // provided us junk values for the hardware configuration.
250       LOG(ERROR) << "Invalid audio output parameters received; using fake "
251                  << "audio path. Channels: " << output_params.channels() << ", "
252                  << "Sample Rate: " << output_params.sample_rate() << ", "
253                  << "Bits Per Sample: " << output_params.bits_per_sample()
254                  << ", Frames Per Buffer: "
255                  << output_params.frames_per_buffer();
256 
257       // Tell the AudioManager to create a fake output device.
258       output_params = AudioParameters(
259           AudioParameters::AUDIO_FAKE, params.channel_layout(),
260           params.sample_rate(), params.bits_per_sample(),
261           params.frames_per_buffer());
262     }
263   }
264 
265   DispatcherParams* dispatcher_params =
266       new DispatcherParams(params, output_params, output_device_id);
267 
268   AudioOutputDispatchers::iterator it =
269       std::find_if(output_dispatchers_.begin(), output_dispatchers_.end(),
270                    CompareByParams(dispatcher_params));
271   if (it != output_dispatchers_.end()) {
272     delete dispatcher_params;
273     return new AudioOutputProxy((*it)->dispatcher.get());
274   }
275 
276   const base::TimeDelta kCloseDelay =
277       base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds);
278   scoped_refptr<AudioOutputDispatcher> dispatcher;
279   if (output_params.format() != AudioParameters::AUDIO_FAKE) {
280     dispatcher = new AudioOutputResampler(this, params, output_params,
281                                           output_device_id,
282                                           kCloseDelay);
283   } else {
284     dispatcher = new AudioOutputDispatcherImpl(this, output_params,
285                                                output_device_id,
286                                                kCloseDelay);
287   }
288 
289   dispatcher_params->dispatcher = dispatcher;
290   output_dispatchers_.push_back(dispatcher_params);
291   return new AudioOutputProxy(dispatcher.get());
292 }
293 
ShowAudioInputSettings()294 void AudioManagerBase::ShowAudioInputSettings() {
295 }
296 
GetAudioInputDeviceNames(AudioDeviceNames * device_names)297 void AudioManagerBase::GetAudioInputDeviceNames(
298     AudioDeviceNames* device_names) {
299 }
300 
GetAudioOutputDeviceNames(AudioDeviceNames * device_names)301 void AudioManagerBase::GetAudioOutputDeviceNames(
302     AudioDeviceNames* device_names) {
303 }
304 
ReleaseOutputStream(AudioOutputStream * stream)305 void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
306   DCHECK(stream);
307   // TODO(xians) : Have a clearer destruction path for the AudioOutputStream.
308   // For example, pass the ownership to AudioManager so it can delete the
309   // streams.
310   --num_output_streams_;
311   delete stream;
312 }
313 
ReleaseInputStream(AudioInputStream * stream)314 void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) {
315   DCHECK(stream);
316   // TODO(xians) : Have a clearer destruction path for the AudioInputStream.
317   --num_input_streams_;
318   delete stream;
319 }
320 
Shutdown()321 void AudioManagerBase::Shutdown() {
322   // Only true when we're sharing the UI message loop with the browser.  The UI
323   // loop is no longer running at this time and browser destruction is imminent.
324   if (task_runner_->BelongsToCurrentThread()) {
325     ShutdownOnAudioThread();
326   } else {
327     task_runner_->PostTask(FROM_HERE, base::Bind(
328         &AudioManagerBase::ShutdownOnAudioThread, base::Unretained(this)));
329   }
330 
331   // Stop() will wait for any posted messages to be processed first.
332   audio_thread_.Stop();
333 }
334 
ShutdownOnAudioThread()335 void AudioManagerBase::ShutdownOnAudioThread() {
336   DCHECK(task_runner_->BelongsToCurrentThread());
337   while (!output_dispatchers_.empty()) {
338     output_dispatchers_.back()->dispatcher->Shutdown();
339     output_dispatchers_.pop_back();
340   }
341 }
342 
AddOutputDeviceChangeListener(AudioDeviceListener * listener)343 void AudioManagerBase::AddOutputDeviceChangeListener(
344     AudioDeviceListener* listener) {
345   DCHECK(task_runner_->BelongsToCurrentThread());
346   output_listeners_.AddObserver(listener);
347 }
348 
RemoveOutputDeviceChangeListener(AudioDeviceListener * listener)349 void AudioManagerBase::RemoveOutputDeviceChangeListener(
350     AudioDeviceListener* listener) {
351   DCHECK(task_runner_->BelongsToCurrentThread());
352   output_listeners_.RemoveObserver(listener);
353 }
354 
NotifyAllOutputDeviceChangeListeners()355 void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
356   DCHECK(task_runner_->BelongsToCurrentThread());
357   DVLOG(1) << "Firing OnDeviceChange() notifications.";
358   FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange());
359 }
360 
GetDefaultOutputStreamParameters()361 AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() {
362   return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(),
363       AudioParameters());
364 }
365 
GetOutputStreamParameters(const std::string & device_id)366 AudioParameters AudioManagerBase::GetOutputStreamParameters(
367     const std::string& device_id) {
368   return GetPreferredOutputStreamParameters(device_id,
369       AudioParameters());
370 }
371 
GetInputStreamParameters(const std::string & device_id)372 AudioParameters AudioManagerBase::GetInputStreamParameters(
373     const std::string& device_id) {
374   NOTREACHED();
375   return AudioParameters();
376 }
377 
GetAssociatedOutputDeviceID(const std::string & input_device_id)378 std::string AudioManagerBase::GetAssociatedOutputDeviceID(
379     const std::string& input_device_id) {
380   return "";
381 }
382 
GetDefaultOutputDeviceID()383 std::string AudioManagerBase::GetDefaultOutputDeviceID() {
384   return "";
385 }
386 
GetUserBufferSize()387 int AudioManagerBase::GetUserBufferSize() {
388   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
389   int buffer_size = 0;
390   std::string buffer_size_str(cmd_line->GetSwitchValueASCII(
391       switches::kAudioBufferSize));
392   if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0)
393     return buffer_size;
394 
395   return 0;
396 }
397 
CreateAudioLog(AudioLogFactory::AudioComponent component)398 scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog(
399     AudioLogFactory::AudioComponent component) {
400   return audio_log_factory_->CreateAudioLog(component);
401 }
402 
SetHasKeyboardMic()403 void AudioManagerBase::SetHasKeyboardMic() {
404   NOTREACHED();
405 }
406 
407 }  // namespace media
408