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
25 using media_audio_pulse::kModulePulse;
26 using media_audio_pulse::InitializeStubs;
27 using media_audio_pulse::StubPathMap;
28 #endif // defined(DLOPEN_PULSEAUDIO)
29
30 namespace media {
31
32 using pulse::AutoPulseLock;
33 using pulse::WaitForOperationCompletion;
34
35 // Maximum number of output streams that can be open simultaneously.
36 static const int kMaxOutputStreams = 50;
37
38 // Define bounds for the output buffer size.
39 static const int kMinimumOutputBufferSize = 512;
40 static const int kMaximumOutputBufferSize = 8192;
41
42 // Default input buffer size.
43 static const int kDefaultInputBufferSize = 1024;
44
45 #if defined(DLOPEN_PULSEAUDIO)
46 static const base::FilePath::CharType kPulseLib[] =
47 FILE_PATH_LITERAL("libpulse.so.0");
48 #endif
49
50 // static
Create(AudioLogFactory * audio_log_factory)51 AudioManager* 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
AudioManagerPulse(AudioLogFactory * audio_log_factory)60 AudioManagerPulse::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
~AudioManagerPulse()69 AudioManagerPulse::~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.
HasAudioOutputDevices()78 bool AudioManagerPulse::HasAudioOutputDevices() {
79 AudioDeviceNames devices;
80 GetAudioOutputDeviceNames(&devices);
81 return !devices.empty();
82 }
83
HasAudioInputDevices()84 bool AudioManagerPulse::HasAudioInputDevices() {
85 AudioDeviceNames devices;
86 GetAudioInputDeviceNames(&devices);
87 return !devices.empty();
88 }
89
ShowAudioInputSettings()90 void AudioManagerPulse::ShowAudioInputSettings() {
91 #if defined(USE_ALSA)
92 AudioManagerAlsa::ShowLinuxAudioInputSettings();
93 #endif
94 }
95
GetAudioDeviceNames(bool input,media::AudioDeviceNames * device_names)96 void 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
GetAudioInputDeviceNames(AudioDeviceNames * device_names)121 void AudioManagerPulse::GetAudioInputDeviceNames(
122 AudioDeviceNames* device_names) {
123 GetAudioDeviceNames(true, device_names);
124 }
125
GetAudioOutputDeviceNames(AudioDeviceNames * device_names)126 void AudioManagerPulse::GetAudioOutputDeviceNames(
127 AudioDeviceNames* device_names) {
128 GetAudioDeviceNames(false, device_names);
129 }
130
GetInputStreamParameters(const std::string & device_id)131 AudioParameters 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
MakeLinearOutputStream(const AudioParameters & params)143 AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
144 const AudioParameters& params) {
145 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
146 return MakeOutputStream(params, AudioManagerBase::kDefaultDeviceId);
147 }
148
MakeLowLatencyOutputStream(const AudioParameters & params,const std::string & device_id)149 AudioOutputStream* 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
MakeLinearInputStream(const AudioParameters & params,const std::string & device_id)158 AudioInputStream* 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
MakeLowLatencyInputStream(const AudioParameters & params,const std::string & device_id)164 AudioInputStream* 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
GetPreferredOutputStreamParameters(const std::string & output_device_id,const AudioParameters & input_params)170 AudioParameters 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 sample_rate = GetNativeSampleRate();
180 if (input_params.IsValid()) {
181 bits_per_sample = input_params.bits_per_sample();
182 channel_layout = input_params.channel_layout();
183 buffer_size =
184 std::min(kMaximumOutputBufferSize,
185 std::max(buffer_size, input_params.frames_per_buffer()));
186 }
187
188 int user_buffer_size = GetUserBufferSize();
189 if (user_buffer_size)
190 buffer_size = user_buffer_size;
191
192 return AudioParameters(
193 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
194 sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
195 }
196
MakeOutputStream(const AudioParameters & params,const std::string & device_id)197 AudioOutputStream* AudioManagerPulse::MakeOutputStream(
198 const AudioParameters& params,
199 const std::string& device_id) {
200 DCHECK(!device_id.empty());
201 return new PulseAudioOutputStream(params, device_id, this);
202 }
203
MakeInputStream(const AudioParameters & params,const std::string & device_id)204 AudioInputStream* AudioManagerPulse::MakeInputStream(
205 const AudioParameters& params, const std::string& device_id) {
206 return new PulseAudioInputStream(this, device_id, params,
207 input_mainloop_, input_context_);
208 }
209
GetNativeSampleRate()210 int AudioManagerPulse::GetNativeSampleRate() {
211 DCHECK(input_mainloop_);
212 DCHECK(input_context_);
213 AutoPulseLock auto_lock(input_mainloop_);
214 pa_operation* operation = pa_context_get_server_info(
215 input_context_, SampleRateInfoCallback, this);
216 WaitForOperationCompletion(input_mainloop_, operation);
217
218 return native_input_sample_rate_;
219 }
220
Init()221 bool AudioManagerPulse::Init() {
222 DCHECK(!input_mainloop_);
223
224 #if defined(DLOPEN_PULSEAUDIO)
225 StubPathMap paths;
226
227 // Check if the pulse library is avialbale.
228 paths[kModulePulse].push_back(kPulseLib);
229 if (!InitializeStubs(paths)) {
230 VLOG(1) << "Failed on loading the Pulse library and symbols";
231 return false;
232 }
233 #endif // defined(DLOPEN_PULSEAUDIO)
234
235 // Create a mainloop API and connect to the default server.
236 // The mainloop is the internal asynchronous API event loop.
237 input_mainloop_ = pa_threaded_mainloop_new();
238 if (!input_mainloop_)
239 return false;
240
241 // Start the threaded mainloop.
242 if (pa_threaded_mainloop_start(input_mainloop_))
243 return false;
244
245 // Lock the event loop object, effectively blocking the event loop thread
246 // from processing events. This is necessary.
247 AutoPulseLock auto_lock(input_mainloop_);
248
249 pa_mainloop_api* pa_mainloop_api =
250 pa_threaded_mainloop_get_api(input_mainloop_);
251 input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
252 if (!input_context_)
253 return false;
254
255 pa_context_set_state_callback(input_context_, &pulse::ContextStateCallback,
256 input_mainloop_);
257 if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
258 VLOG(0) << "Failed to connect to the context. Error: "
259 << pa_strerror(pa_context_errno(input_context_));
260 return false;
261 }
262
263 // Wait until |input_context_| is ready. pa_threaded_mainloop_wait() must be
264 // called after pa_context_get_state() in case the context is already ready,
265 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
266 while (true) {
267 pa_context_state_t context_state = pa_context_get_state(input_context_);
268 if (!PA_CONTEXT_IS_GOOD(context_state))
269 return false;
270 if (context_state == PA_CONTEXT_READY)
271 break;
272 pa_threaded_mainloop_wait(input_mainloop_);
273 }
274
275 return true;
276 }
277
DestroyPulse()278 void AudioManagerPulse::DestroyPulse() {
279 if (!input_mainloop_) {
280 DCHECK(!input_context_);
281 return;
282 }
283
284 {
285 AutoPulseLock auto_lock(input_mainloop_);
286 if (input_context_) {
287 // Clear our state callback.
288 pa_context_set_state_callback(input_context_, NULL, NULL);
289 pa_context_disconnect(input_context_);
290 pa_context_unref(input_context_);
291 input_context_ = NULL;
292 }
293 }
294
295 pa_threaded_mainloop_stop(input_mainloop_);
296 pa_threaded_mainloop_free(input_mainloop_);
297 input_mainloop_ = NULL;
298 }
299
InputDevicesInfoCallback(pa_context * context,const pa_source_info * info,int error,void * user_data)300 void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context,
301 const pa_source_info* info,
302 int error, void *user_data) {
303 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
304
305 if (error) {
306 // Signal the pulse object that it is done.
307 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
308 return;
309 }
310
311 // Exclude the output devices.
312 if (info->monitor_of_sink == PA_INVALID_INDEX) {
313 manager->devices_->push_back(AudioDeviceName(info->description,
314 info->name));
315 }
316 }
317
OutputDevicesInfoCallback(pa_context * context,const pa_sink_info * info,int error,void * user_data)318 void AudioManagerPulse::OutputDevicesInfoCallback(pa_context* context,
319 const pa_sink_info* info,
320 int error, void *user_data) {
321 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
322
323 if (error) {
324 // Signal the pulse object that it is done.
325 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
326 return;
327 }
328
329 manager->devices_->push_back(AudioDeviceName(info->description,
330 info->name));
331 }
332
SampleRateInfoCallback(pa_context * context,const pa_server_info * info,void * user_data)333 void AudioManagerPulse::SampleRateInfoCallback(pa_context* context,
334 const pa_server_info* info,
335 void* user_data) {
336 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
337
338 manager->native_input_sample_rate_ = info->sample_spec.rate;
339 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
340 }
341
342 } // namespace media
343