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