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