Upstream version 6.34.113.0
[platform/framework/web/crosswalk.git] / src / media / audio / pulse / pulse_util.cc
1 // Copyright (c) 2012 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/pulse_util.h"
6
7 #include "base/logging.h"
8 #include "base/time/time.h"
9 #include "media/audio/audio_manager_base.h"
10 #include "media/audio/audio_parameters.h"
11
12 namespace media {
13
14 namespace pulse {
15
16 namespace {
17
18 pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
19   switch (channel) {
20     // PulseAudio does not differentiate between left/right and
21     // stereo-left/stereo-right, both translate to front-left/front-right.
22     case LEFT:
23       return PA_CHANNEL_POSITION_FRONT_LEFT;
24     case RIGHT:
25       return PA_CHANNEL_POSITION_FRONT_RIGHT;
26     case CENTER:
27       return PA_CHANNEL_POSITION_FRONT_CENTER;
28     case LFE:
29       return PA_CHANNEL_POSITION_LFE;
30     case BACK_LEFT:
31       return PA_CHANNEL_POSITION_REAR_LEFT;
32     case BACK_RIGHT:
33       return PA_CHANNEL_POSITION_REAR_RIGHT;
34     case LEFT_OF_CENTER:
35       return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
36     case RIGHT_OF_CENTER:
37       return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
38     case BACK_CENTER:
39       return PA_CHANNEL_POSITION_REAR_CENTER;
40     case SIDE_LEFT:
41       return PA_CHANNEL_POSITION_SIDE_LEFT;
42     case SIDE_RIGHT:
43       return PA_CHANNEL_POSITION_SIDE_RIGHT;
44     case CHANNELS_MAX:
45       return PA_CHANNEL_POSITION_INVALID;
46     default:
47       NOTREACHED() << "Invalid channel: " << channel;
48       return PA_CHANNEL_POSITION_INVALID;
49   }
50 }
51
52 }  // namespace
53
54 // static, pa_stream_success_cb_t
55 void StreamSuccessCallback(pa_stream* s, int error, void* mainloop) {
56   pa_threaded_mainloop* pa_mainloop =
57       static_cast<pa_threaded_mainloop*>(mainloop);
58   pa_threaded_mainloop_signal(pa_mainloop, 0);
59 }
60
61 // |pa_context| and |pa_stream| state changed cb.
62 void ContextStateCallback(pa_context* context, void* mainloop) {
63   pa_threaded_mainloop* pa_mainloop =
64       static_cast<pa_threaded_mainloop*>(mainloop);
65   pa_threaded_mainloop_signal(pa_mainloop, 0);
66 }
67
68 pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
69   switch (bits_per_sample) {
70     case 8:
71       return PA_SAMPLE_U8;
72     case 16:
73       return PA_SAMPLE_S16LE;
74     case 24:
75       return PA_SAMPLE_S24LE;
76     case 32:
77       return PA_SAMPLE_S32LE;
78     default:
79       NOTREACHED() << "Invalid bits per sample: " << bits_per_sample;
80       return PA_SAMPLE_INVALID;
81   }
82 }
83
84 pa_channel_map ChannelLayoutToPAChannelMap(ChannelLayout channel_layout) {
85   pa_channel_map channel_map;
86   pa_channel_map_init(&channel_map);
87
88   channel_map.channels = ChannelLayoutToChannelCount(channel_layout);
89   for (Channels ch = LEFT; ch < CHANNELS_MAX;
90        ch = static_cast<Channels>(ch + 1)) {
91     int channel_index = ChannelOrder(channel_layout, ch);
92     if (channel_index < 0)
93       continue;
94
95     channel_map.map[channel_index] = ChromiumToPAChannelPosition(ch);
96   }
97
98   return channel_map;
99 }
100
101 void WaitForOperationCompletion(pa_threaded_mainloop* pa_mainloop,
102                                 pa_operation* operation) {
103   if (!operation) {
104     DLOG(WARNING) << "Operation is NULL";
105     return;
106   }
107
108   while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
109     pa_threaded_mainloop_wait(pa_mainloop);
110
111   pa_operation_unref(operation);
112 }
113
114 int GetHardwareLatencyInBytes(pa_stream* stream,
115                               int sample_rate,
116                               int bytes_per_frame) {
117   DCHECK(stream);
118   int negative = 0;
119   pa_usec_t latency_micros = 0;
120   if (pa_stream_get_latency(stream, &latency_micros, &negative) != 0)
121     return 0;
122
123   if (negative)
124     return 0;
125
126   return latency_micros * sample_rate * bytes_per_frame /
127       base::Time::kMicrosecondsPerSecond;
128 }
129
130 // Helper macro for CreateInput/OutputStream() to avoid code spam and
131 // string bloat.
132 #define RETURN_ON_FAILURE(expression, message) do { \
133   if (!(expression)) { \
134     DLOG(ERROR) << message; \
135     return false; \
136   } \
137 } while(0)
138
139 bool CreateInputStream(pa_threaded_mainloop* mainloop,
140                        pa_context* context,
141                        pa_stream** stream,
142                        const AudioParameters& params,
143                        const std::string& device_id,
144                        pa_stream_notify_cb_t stream_callback,
145                        void* user_data) {
146   DCHECK(mainloop);
147   DCHECK(context);
148
149   // Set sample specifications.
150   pa_sample_spec sample_specifications;
151   sample_specifications.format = BitsToPASampleFormat(
152       params.bits_per_sample());
153   sample_specifications.rate = params.sample_rate();
154   sample_specifications.channels = params.channels();
155
156   // Get channel mapping and open recording stream.
157   pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
158       params.channel_layout());
159   pa_channel_map* map = (source_channel_map.channels != 0) ?
160       &source_channel_map : NULL;
161
162   // Create a new recording stream.
163   *stream = pa_stream_new(context, "RecordStream", &sample_specifications, map);
164   RETURN_ON_FAILURE(*stream, "failed to create PA recording stream");
165
166   pa_stream_set_state_callback(*stream, stream_callback, user_data);
167
168   // Set server-side capture buffer metrics. Detailed documentation on what
169   // values should be chosen can be found at
170   // freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
171   pa_buffer_attr buffer_attributes;
172   const unsigned int buffer_size = params.GetBytesPerBuffer();
173   buffer_attributes.maxlength = static_cast<uint32_t>(-1);
174   buffer_attributes.tlength = buffer_size;
175   buffer_attributes.minreq = buffer_size;
176   buffer_attributes.prebuf = static_cast<uint32_t>(-1);
177   buffer_attributes.fragsize = buffer_size;
178   int flags = PA_STREAM_AUTO_TIMING_UPDATE |
179               PA_STREAM_INTERPOLATE_TIMING |
180               PA_STREAM_ADJUST_LATENCY |
181               PA_STREAM_START_CORKED;
182   RETURN_ON_FAILURE(
183       pa_stream_connect_record(
184           *stream,
185           device_id == AudioManagerBase::kDefaultDeviceId ?
186               NULL : device_id.c_str(),
187           &buffer_attributes,
188           static_cast<pa_stream_flags_t>(flags)) == 0,
189       "pa_stream_connect_record FAILED ");
190
191   // Wait for the stream to be ready.
192   while (true) {
193     pa_stream_state_t stream_state = pa_stream_get_state(*stream);
194     RETURN_ON_FAILURE(
195         PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
196     if (stream_state == PA_STREAM_READY)
197         break;
198     pa_threaded_mainloop_wait(mainloop);
199   }
200
201   return true;
202 }
203
204 bool CreateOutputStream(pa_threaded_mainloop** mainloop,
205                         pa_context** context,
206                         pa_stream** stream,
207                         const AudioParameters& params,
208                         const std::string& device_id,
209                         pa_stream_notify_cb_t stream_callback,
210                         pa_stream_request_cb_t write_callback,
211                         void* user_data) {
212   DCHECK(!*mainloop);
213   DCHECK(!*context);
214
215   *mainloop = pa_threaded_mainloop_new();
216   RETURN_ON_FAILURE(*mainloop, "Failed to create PulseAudio main loop.");
217
218   pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(*mainloop);
219   *context = pa_context_new(pa_mainloop_api, "Chromium");
220   RETURN_ON_FAILURE(*context, "Failed to create PulseAudio context.");
221
222   // A state callback must be set before calling pa_threaded_mainloop_lock() or
223   // pa_threaded_mainloop_wait() calls may lead to dead lock.
224   pa_context_set_state_callback(*context, &ContextStateCallback, *mainloop);
225
226   // Lock the main loop while setting up the context.  Failure to do so may lead
227   // to crashes as the PulseAudio thread tries to run before things are ready.
228   AutoPulseLock auto_lock(*mainloop);
229
230   RETURN_ON_FAILURE(pa_threaded_mainloop_start(*mainloop) == 0,
231                     "Failed to start PulseAudio main loop.");
232   RETURN_ON_FAILURE(
233       pa_context_connect(*context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
234       "Failed to connect PulseAudio context.");
235
236   // Wait until |pa_context_| is ready.  pa_threaded_mainloop_wait() must be
237   // called after pa_context_get_state() in case the context is already ready,
238   // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
239   while (true) {
240     pa_context_state_t context_state = pa_context_get_state(*context);
241     RETURN_ON_FAILURE(
242         PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
243     if (context_state == PA_CONTEXT_READY)
244       break;
245     pa_threaded_mainloop_wait(*mainloop);
246   }
247
248   // Set sample specifications.
249   pa_sample_spec sample_specifications;
250   sample_specifications.format = BitsToPASampleFormat(
251       params.bits_per_sample());
252   sample_specifications.rate = params.sample_rate();
253   sample_specifications.channels = params.channels();
254
255   // Get channel mapping and open playback stream.
256   pa_channel_map* map = NULL;
257   pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
258       params.channel_layout());
259   if (source_channel_map.channels != 0) {
260     // The source data uses a supported channel map so we will use it rather
261     // than the default channel map (NULL).
262     map = &source_channel_map;
263   }
264 #if defined(OS_TIZEN)
265   pa_proplist* proplist = pa_proplist_new();
266   pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "browser");
267   *stream = pa_stream_new_with_proplist(*context, "Playback",
268                                         &sample_specifications,
269                                         map, proplist);
270   pa_proplist_free(proplist);
271 #else
272   *stream = pa_stream_new(*context, "Playback", &sample_specifications, map);
273 #endif
274   RETURN_ON_FAILURE(*stream, "failed to create PA playback stream");
275
276   pa_stream_set_state_callback(*stream, stream_callback, user_data);
277
278   // Even though we start the stream corked above, PulseAudio will issue one
279   // stream request after setup.  write_callback() must fulfill the write.
280   pa_stream_set_write_callback(*stream, write_callback, user_data);
281
282   // Pulse is very finicky with the small buffer sizes used by Chrome.  The
283   // settings below are mostly found through trial and error.  Essentially we
284   // want Pulse to auto size its internal buffers, but call us back nearly every
285   // |minreq| bytes.  |tlength| should be a multiple of |minreq|; too low and
286   // Pulse will issue callbacks way too fast, too high and we don't get
287   // callbacks frequently enough.
288   pa_buffer_attr pa_buffer_attributes;
289   pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
290   pa_buffer_attributes.minreq = params.GetBytesPerBuffer();
291   pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
292   pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3;
293   pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);
294
295   // Connect playback stream.  Like pa_buffer_attr, the pa_stream_flags have a
296   // huge impact on the performance of the stream and were chosen through trial
297   // and error.
298   RETURN_ON_FAILURE(
299       pa_stream_connect_playback(
300           *stream,
301           device_id == AudioManagerBase::kDefaultDeviceId ?
302               NULL : device_id.c_str(),
303           &pa_buffer_attributes,
304           static_cast<pa_stream_flags_t>(
305               PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
306               PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC |
307               PA_STREAM_START_CORKED),
308           NULL,
309           NULL) == 0,
310       "pa_stream_connect_playback FAILED ");
311
312   // Wait for the stream to be ready.
313   while (true) {
314     pa_stream_state_t stream_state = pa_stream_get_state(*stream);
315     RETURN_ON_FAILURE(
316         PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
317     if (stream_state == PA_STREAM_READY)
318       break;
319     pa_threaded_mainloop_wait(*mainloop);
320   }
321
322   return true;
323 }
324
325 #undef RETURN_ON_FAILURE
326
327 }  // namespace pulse
328
329 }  // namespace media