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