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