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.
6 #include "media/audio/pulse/pulse_output.h"
9 #include "media/audio/pulse/pulse_util.h"
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"
22 pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
24 // PulseAudio does not differentiate between left/right and
25 // stereo-left/stereo-right, both translate to front-left/front-right.
27 return PA_CHANNEL_POSITION_FRONT_LEFT;
29 return PA_CHANNEL_POSITION_FRONT_RIGHT;
31 return PA_CHANNEL_POSITION_FRONT_CENTER;
33 return PA_CHANNEL_POSITION_LFE;
35 return PA_CHANNEL_POSITION_REAR_LEFT;
37 return PA_CHANNEL_POSITION_REAR_RIGHT;
39 return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
41 return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
43 return PA_CHANNEL_POSITION_REAR_CENTER;
45 return PA_CHANNEL_POSITION_SIDE_LEFT;
47 return PA_CHANNEL_POSITION_SIDE_RIGHT;
49 NOTREACHED() << "Invalid channel: " << channel;
50 return PA_CHANNEL_POSITION_INVALID;
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);
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);
70 pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
71 switch (bits_per_sample) {
75 return PA_SAMPLE_S16LE;
77 return PA_SAMPLE_S24LE;
79 return PA_SAMPLE_S32LE;
81 NOTREACHED() << "Invalid bits per sample: " << bits_per_sample;
82 return PA_SAMPLE_INVALID;
86 pa_channel_map ChannelLayoutToPAChannelMap(ChannelLayout channel_layout) {
87 pa_channel_map channel_map;
88 pa_channel_map_init(&channel_map);
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)
97 channel_map.map[channel_index] = ChromiumToPAChannelPosition(ch);
103 void WaitForOperationCompletion(pa_threaded_mainloop* pa_mainloop,
104 pa_operation* operation) {
106 DLOG(WARNING) << "Operation is NULL";
110 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
111 pa_threaded_mainloop_wait(pa_mainloop);
113 pa_operation_unref(operation);
116 int GetHardwareLatencyInBytes(pa_stream* stream,
118 int bytes_per_frame) {
121 pa_usec_t latency_micros = 0;
122 if (pa_stream_get_latency(stream, &latency_micros, &negative) != 0)
128 return latency_micros * sample_rate * bytes_per_frame /
129 base::Time::kMicrosecondsPerSecond;
132 // Helper macro for CreateInput/OutputStream() to avoid code spam and
134 #define RETURN_ON_FAILURE(expression, message) do { \
135 if (!(expression)) { \
136 DLOG(ERROR) << message; \
141 bool CreateInputStream(pa_threaded_mainloop* mainloop,
144 const AudioParameters& params,
145 const std::string& device_id,
146 pa_stream_notify_cb_t stream_callback,
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();
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;
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");
168 pa_stream_set_state_callback(*stream, stream_callback, user_data);
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;
185 pa_stream_connect_record(
187 device_id == AudioManagerBase::kDefaultDeviceId ?
188 NULL : device_id.c_str(),
190 static_cast<pa_stream_flags_t>(flags)) == 0,
191 "pa_stream_connect_record FAILED ");
193 // Wait for the stream to be ready.
195 pa_stream_state_t stream_state = pa_stream_get_state(*stream);
197 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
198 if (stream_state == PA_STREAM_READY)
200 pa_threaded_mainloop_wait(mainloop);
206 bool CreateOutputStream(pa_threaded_mainloop** mainloop,
207 pa_context** context,
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,
217 *mainloop = pa_threaded_mainloop_new();
218 RETURN_ON_FAILURE(*mainloop, "Failed to create PulseAudio main loop.");
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.");
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);
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);
232 RETURN_ON_FAILURE(pa_threaded_mainloop_start(*mainloop) == 0,
233 "Failed to start PulseAudio main loop.");
235 pa_context_connect(*context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
236 "Failed to connect PulseAudio context.");
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.
242 pa_context_state_t context_state = pa_context_get_state(*context);
244 PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
245 if (context_state == PA_CONTEXT_READY)
247 pa_threaded_mainloop_wait(*mainloop);
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();
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;
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,
276 pa_proplist_free(proplist);
278 *stream = pa_stream_new(*context, "Playback", &sample_specifications, map);
280 RETURN_ON_FAILURE(*stream, "failed to create PA playback stream");
282 pa_stream_set_state_callback(*stream, stream_callback, user_data);
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);
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.
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);
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
309 pa_stream_connect_playback(
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),
320 "pa_stream_connect_playback FAILED ");
322 // Wait for the stream to be ready.
324 pa_stream_state_t stream_state = pa_stream_get_state(*stream);
326 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
327 if (stream_state == PA_STREAM_READY)
329 pa_threaded_mainloop_wait(*mainloop);
335 #undef RETURN_ON_FAILURE