audio_manager_pulse.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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.
4
5#include "media/audio/pulse/audio_manager_pulse.h"
6
7#include "base/command_line.h"
8#include "base/environment.h"
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/nix/xdg_util.h"
12#include "base/stl_util.h"
13#if defined(USE_ALSA)
14#include "media/audio/alsa/audio_manager_alsa.h"
15#endif
16#include "media/audio/audio_parameters.h"
17#include "media/audio/pulse/pulse_input.h"
18#include "media/audio/pulse/pulse_output.h"
19#include "media/audio/pulse/pulse_util.h"
20#include "media/base/channel_layout.h"
21
22#if defined(DLOPEN_PULSEAUDIO)
23#include "media/audio/pulse/pulse_stubs.h"
24
25using media_audio_pulse::kModulePulse;
26using media_audio_pulse::InitializeStubs;
27using media_audio_pulse::StubPathMap;
28#endif  // defined(DLOPEN_PULSEAUDIO)
29
30namespace media {
31
32using pulse::AutoPulseLock;
33using pulse::WaitForOperationCompletion;
34
35// Maximum number of output streams that can be open simultaneously.
36static const int kMaxOutputStreams = 50;
37
38// Define bounds for the output buffer size.
39static const int kMinimumOutputBufferSize = 512;
40static const int kMaximumOutputBufferSize = 8192;
41
42// Default input buffer size.
43static const int kDefaultInputBufferSize = 1024;
44
45#if defined(DLOPEN_PULSEAUDIO)
46static const base::FilePath::CharType kPulseLib[] =
47    FILE_PATH_LITERAL("libpulse.so.0");
48#endif
49
50// static
51AudioManager* AudioManagerPulse::Create(AudioLogFactory* audio_log_factory) {
52  scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse(audio_log_factory));
53  if (ret->Init())
54    return ret.release();
55
56  DVLOG(1) << "PulseAudio is not available on the OS";
57  return NULL;
58}
59
60AudioManagerPulse::AudioManagerPulse(AudioLogFactory* audio_log_factory)
61    : AudioManagerBase(audio_log_factory),
62      input_mainloop_(NULL),
63      input_context_(NULL),
64      devices_(NULL),
65      native_input_sample_rate_(0) {
66  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
67}
68
69AudioManagerPulse::~AudioManagerPulse() {
70  Shutdown();
71
72  // The Pulse objects are the last things to be destroyed since Shutdown()
73  // needs them.
74  DestroyPulse();
75}
76
77// Implementation of AudioManager.
78bool AudioManagerPulse::HasAudioOutputDevices() {
79  AudioDeviceNames devices;
80  GetAudioOutputDeviceNames(&devices);
81  return !devices.empty();
82}
83
84bool AudioManagerPulse::HasAudioInputDevices() {
85  AudioDeviceNames devices;
86  GetAudioInputDeviceNames(&devices);
87  return !devices.empty();
88}
89
90void AudioManagerPulse::ShowAudioInputSettings() {
91#if defined(USE_ALSA)
92  AudioManagerAlsa::ShowLinuxAudioInputSettings();
93#endif
94}
95
96void AudioManagerPulse::GetAudioDeviceNames(
97    bool input, media::AudioDeviceNames* device_names) {
98  DCHECK(device_names->empty());
99  DCHECK(input_mainloop_);
100  DCHECK(input_context_);
101  AutoPulseLock auto_lock(input_mainloop_);
102  devices_ = device_names;
103  pa_operation* operation = NULL;
104  if (input) {
105    operation = pa_context_get_source_info_list(
106      input_context_, InputDevicesInfoCallback, this);
107  } else {
108    operation = pa_context_get_sink_info_list(
109        input_context_, OutputDevicesInfoCallback, this);
110  }
111  WaitForOperationCompletion(input_mainloop_, operation);
112
113  // Prepend the default device if the list is not empty.
114  if (!device_names->empty()) {
115    device_names->push_front(
116        AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
117                        AudioManagerBase::kDefaultDeviceId));
118  }
119}
120
121void AudioManagerPulse::GetAudioInputDeviceNames(
122    AudioDeviceNames* device_names) {
123  GetAudioDeviceNames(true, device_names);
124}
125
126void AudioManagerPulse::GetAudioOutputDeviceNames(
127    AudioDeviceNames* device_names) {
128  GetAudioDeviceNames(false, device_names);
129}
130
131AudioParameters AudioManagerPulse::GetInputStreamParameters(
132    const std::string& device_id) {
133  int user_buffer_size = GetUserBufferSize();
134  int buffer_size = user_buffer_size ?
135      user_buffer_size : kDefaultInputBufferSize;
136
137  // TODO(xians): add support for querying native channel layout for pulse.
138  return AudioParameters(
139      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
140      GetNativeSampleRate(), 16, buffer_size);
141}
142
143AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
144    const AudioParameters& params) {
145  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
146  return MakeOutputStream(params, AudioManagerBase::kDefaultDeviceId);
147}
148
149AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
150    const AudioParameters& params,
151    const std::string& device_id) {
152  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
153  return MakeOutputStream(
154      params,
155      device_id.empty() ? AudioManagerBase::kDefaultDeviceId : device_id);
156}
157
158AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
159    const AudioParameters& params, const std::string& device_id) {
160  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
161  return MakeInputStream(params, device_id);
162}
163
164AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
165    const AudioParameters& params, const std::string& device_id) {
166  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
167  return MakeInputStream(params, device_id);
168}
169
170AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
171    const std::string& output_device_id,
172    const AudioParameters& input_params) {
173  // TODO(tommi): Support |output_device_id|.
174  VLOG_IF(0, !output_device_id.empty()) << "Not implemented!";
175
176  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
177  int buffer_size = kMinimumOutputBufferSize;
178  int bits_per_sample = 16;
179  int input_channels = 0;
180  int sample_rate = GetNativeSampleRate();
181  if (input_params.IsValid()) {
182    bits_per_sample = input_params.bits_per_sample();
183    channel_layout = input_params.channel_layout();
184    input_channels = input_params.input_channels();
185    buffer_size =
186        std::min(kMaximumOutputBufferSize,
187                 std::max(buffer_size, input_params.frames_per_buffer()));
188  }
189
190  int user_buffer_size = GetUserBufferSize();
191  if (user_buffer_size)
192    buffer_size = user_buffer_size;
193
194  return AudioParameters(
195      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
196      sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
197}
198
199AudioOutputStream* AudioManagerPulse::MakeOutputStream(
200    const AudioParameters& params,
201    const std::string& device_id) {
202  DCHECK(!device_id.empty());
203  return new PulseAudioOutputStream(params, device_id, this);
204}
205
206AudioInputStream* AudioManagerPulse::MakeInputStream(
207    const AudioParameters& params, const std::string& device_id) {
208  return new PulseAudioInputStream(this, device_id, params,
209                                   input_mainloop_, input_context_);
210}
211
212int AudioManagerPulse::GetNativeSampleRate() {
213  DCHECK(input_mainloop_);
214  DCHECK(input_context_);
215  AutoPulseLock auto_lock(input_mainloop_);
216  pa_operation* operation = pa_context_get_server_info(
217      input_context_, SampleRateInfoCallback, this);
218  WaitForOperationCompletion(input_mainloop_, operation);
219
220  return native_input_sample_rate_;
221}
222
223bool AudioManagerPulse::Init() {
224  DCHECK(!input_mainloop_);
225
226#if defined(DLOPEN_PULSEAUDIO)
227  StubPathMap paths;
228
229  // Check if the pulse library is avialbale.
230  paths[kModulePulse].push_back(kPulseLib);
231  if (!InitializeStubs(paths)) {
232    VLOG(1) << "Failed on loading the Pulse library and symbols";
233    return false;
234  }
235#endif  // defined(DLOPEN_PULSEAUDIO)
236
237  // Create a mainloop API and connect to the default server.
238  // The mainloop is the internal asynchronous API event loop.
239  input_mainloop_ = pa_threaded_mainloop_new();
240  if (!input_mainloop_)
241    return false;
242
243  // Start the threaded mainloop.
244  if (pa_threaded_mainloop_start(input_mainloop_))
245    return false;
246
247  // Lock the event loop object, effectively blocking the event loop thread
248  // from processing events. This is necessary.
249  AutoPulseLock auto_lock(input_mainloop_);
250
251  pa_mainloop_api* pa_mainloop_api =
252      pa_threaded_mainloop_get_api(input_mainloop_);
253  input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
254  if (!input_context_)
255    return false;
256
257  pa_context_set_state_callback(input_context_, &pulse::ContextStateCallback,
258                                input_mainloop_);
259  if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
260    VLOG(0) << "Failed to connect to the context.  Error: "
261            << pa_strerror(pa_context_errno(input_context_));
262    return false;
263  }
264
265  // Wait until |input_context_| is ready.  pa_threaded_mainloop_wait() must be
266  // called after pa_context_get_state() in case the context is already ready,
267  // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
268  while (true) {
269    pa_context_state_t context_state = pa_context_get_state(input_context_);
270    if (!PA_CONTEXT_IS_GOOD(context_state))
271      return false;
272    if (context_state == PA_CONTEXT_READY)
273      break;
274    pa_threaded_mainloop_wait(input_mainloop_);
275  }
276
277  return true;
278}
279
280void AudioManagerPulse::DestroyPulse() {
281  if (!input_mainloop_) {
282    DCHECK(!input_context_);
283    return;
284  }
285
286  {
287    AutoPulseLock auto_lock(input_mainloop_);
288    if (input_context_) {
289      // Clear our state callback.
290      pa_context_set_state_callback(input_context_, NULL, NULL);
291      pa_context_disconnect(input_context_);
292      pa_context_unref(input_context_);
293      input_context_ = NULL;
294    }
295  }
296
297  pa_threaded_mainloop_stop(input_mainloop_);
298  pa_threaded_mainloop_free(input_mainloop_);
299  input_mainloop_ = NULL;
300}
301
302void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context,
303                                                 const pa_source_info* info,
304                                                 int error, void *user_data) {
305  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
306
307  if (error) {
308    // Signal the pulse object that it is done.
309    pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
310    return;
311  }
312
313  // Exclude the output devices.
314  if (info->monitor_of_sink == PA_INVALID_INDEX) {
315    manager->devices_->push_back(AudioDeviceName(info->description,
316                                                 info->name));
317  }
318}
319
320void AudioManagerPulse::OutputDevicesInfoCallback(pa_context* context,
321                                                  const pa_sink_info* info,
322                                                  int error, void *user_data) {
323  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
324
325  if (error) {
326    // Signal the pulse object that it is done.
327    pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
328    return;
329  }
330
331  manager->devices_->push_back(AudioDeviceName(info->description,
332                                               info->name));
333}
334
335void AudioManagerPulse::SampleRateInfoCallback(pa_context* context,
336                                               const pa_server_info* info,
337                                               void* user_data) {
338  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
339
340  manager->native_input_sample_rate_ = info->sample_spec.rate;
341  pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
342}
343
344}  // namespace media
345