audio_manager_pulse.cc revision a3f7b4e666c476898878fa745f637129375cd889
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  // TODO(xians): implement this function.
70  return true;
71}
72
73bool AudioManagerPulse::HasAudioInputDevices() {
74  // TODO(xians): implement this function.
75  return true;
76}
77
78void AudioManagerPulse::ShowAudioInputSettings() {
79  AudioManagerLinux::ShowLinuxAudioInputSettings();
80}
81
82void AudioManagerPulse::GetAudioInputDeviceNames(
83    media::AudioDeviceNames* device_names) {
84  DCHECK(device_names->empty());
85  DCHECK(input_mainloop_);
86  DCHECK(input_context_);
87  devices_ = device_names;
88  AutoPulseLock auto_lock(input_mainloop_);
89  pa_operation* operation = pa_context_get_source_info_list(
90      input_context_, DevicesInfoCallback, this);
91  WaitForOperationCompletion(input_mainloop_, operation);
92
93  // Append the default device on the top of the list if the list is not empty.
94  if (!device_names->empty()) {
95    device_names->push_front(
96        AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
97                        AudioManagerBase::kDefaultDeviceId));
98  }
99}
100
101AudioParameters AudioManagerPulse::GetInputStreamParameters(
102    const std::string& device_id) {
103  static const int kDefaultInputBufferSize = 1024;
104
105  // TODO(xians): add support for querying native channel layout for pulse.
106  return AudioParameters(
107      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
108      GetNativeSampleRate(), 16, kDefaultInputBufferSize);
109}
110
111AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
112    const AudioParameters& params) {
113  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
114  return MakeOutputStream(params, std::string());
115}
116
117AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
118    const AudioParameters& params, const std::string& input_device_id) {
119  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
120  return MakeOutputStream(params, input_device_id);
121}
122
123AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
124    const AudioParameters& params, const std::string& device_id) {
125  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
126  return MakeInputStream(params, device_id);
127}
128
129AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
130    const AudioParameters& params, const std::string& device_id) {
131  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
132  return MakeInputStream(params, device_id);
133}
134
135AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
136    const AudioParameters& input_params) {
137  static const int kDefaultOutputBufferSize = 512;
138
139  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
140  int buffer_size = kDefaultOutputBufferSize;
141  int bits_per_sample = 16;
142  int input_channels = 0;
143  int sample_rate;
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    sample_rate = input_params.sample_rate();
150  } else {
151    sample_rate = GetNativeSampleRate();
152  }
153
154  int user_buffer_size = GetUserBufferSize();
155  if (user_buffer_size)
156    buffer_size = user_buffer_size;
157
158  return AudioParameters(
159      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
160      sample_rate, bits_per_sample, buffer_size);
161}
162
163AudioOutputStream* AudioManagerPulse::MakeOutputStream(
164    const AudioParameters& params, const std::string& input_device_id) {
165  if (params.input_channels()) {
166    return new PulseAudioUnifiedStream(params, input_device_id, this);
167  }
168
169  return new PulseAudioOutputStream(params, this);
170}
171
172AudioInputStream* AudioManagerPulse::MakeInputStream(
173    const AudioParameters& params, const std::string& device_id) {
174  return new PulseAudioInputStream(this, device_id, params,
175                                   input_mainloop_, input_context_);
176}
177
178int AudioManagerPulse::GetNativeSampleRate() {
179  DCHECK(input_mainloop_);
180  DCHECK(input_context_);
181  AutoPulseLock auto_lock(input_mainloop_);
182  pa_operation* operation = pa_context_get_server_info(
183      input_context_, SampleRateInfoCallback, this);
184  WaitForOperationCompletion(input_mainloop_, operation);
185
186  return native_input_sample_rate_;
187}
188
189bool AudioManagerPulse::Init() {
190  DCHECK(!input_mainloop_);
191
192#if defined(DLOPEN_PULSEAUDIO)
193  StubPathMap paths;
194
195  // Check if the pulse library is avialbale.
196  paths[kModulePulse].push_back(kPulseLib);
197  if (!InitializeStubs(paths)) {
198    DLOG(WARNING) << "Failed on loading the Pulse library and symbols";
199    return false;
200  }
201#endif  // defined(DLOPEN_PULSEAUDIO)
202
203  // Create a mainloop API and connect to the default server.
204  // The mainloop is the internal asynchronous API event loop.
205  input_mainloop_ = pa_threaded_mainloop_new();
206  if (!input_mainloop_)
207    return false;
208
209  // Start the threaded mainloop.
210  if (pa_threaded_mainloop_start(input_mainloop_))
211    return false;
212
213  // Lock the event loop object, effectively blocking the event loop thread
214  // from processing events. This is necessary.
215  AutoPulseLock auto_lock(input_mainloop_);
216
217  pa_mainloop_api* pa_mainloop_api =
218      pa_threaded_mainloop_get_api(input_mainloop_);
219  input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
220  if (!input_context_)
221    return false;
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.  Error: "
227                << pa_strerror(pa_context_errno(input_context_));
228    return false;
229  }
230
231  // Wait until |input_context_| is ready.  pa_threaded_mainloop_wait() must be
232  // called after pa_context_get_state() in case the context is already ready,
233  // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
234  while (true) {
235    pa_context_state_t context_state = pa_context_get_state(input_context_);
236    if (!PA_CONTEXT_IS_GOOD(context_state))
237      return false;
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