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