| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/pulse/pulse_util.h" | 5 #include "media/audio/pulse/pulse_util.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/macros.h" | 10 #include "base/macros.h" |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 | 163 |
| 164 bool CreateInputStream(pa_threaded_mainloop* mainloop, | 164 bool CreateInputStream(pa_threaded_mainloop* mainloop, |
| 165 pa_context* context, | 165 pa_context* context, |
| 166 pa_stream** stream, | 166 pa_stream** stream, |
| 167 const AudioParameters& params, | 167 const AudioParameters& params, |
| 168 const std::string& device_id, | 168 const std::string& device_id, |
| 169 pa_stream_notify_cb_t stream_callback, | 169 pa_stream_notify_cb_t stream_callback, |
| 170 void* user_data) { | 170 void* user_data) { |
| 171 DCHECK(mainloop); | 171 DCHECK(mainloop); |
| 172 DCHECK(context); | 172 DCHECK(context); |
| 173 DCHECK_NE(device_id, AudioDeviceDescription::kDefaultDeviceId); | |
| 174 | 173 |
| 175 // Set sample specifications. | 174 // Set sample specifications. |
| 176 pa_sample_spec sample_specifications; | 175 pa_sample_spec sample_specifications; |
| 177 sample_specifications.format = BitsToPASampleFormat( | 176 sample_specifications.format = BitsToPASampleFormat( |
| 178 params.bits_per_sample()); | 177 params.bits_per_sample()); |
| 179 sample_specifications.rate = params.sample_rate(); | 178 sample_specifications.rate = params.sample_rate(); |
| 180 sample_specifications.channels = params.channels(); | 179 sample_specifications.channels = params.channels(); |
| 181 | 180 |
| 182 // Get channel mapping and open recording stream. | 181 // Get channel mapping and open recording stream. |
| 183 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( | 182 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( |
| (...skipping 21 matching lines...) Expand all Loading... |
| 205 buffer_attributes.maxlength = static_cast<uint32_t>(-1); | 204 buffer_attributes.maxlength = static_cast<uint32_t>(-1); |
| 206 buffer_attributes.tlength = buffer_size; | 205 buffer_attributes.tlength = buffer_size; |
| 207 buffer_attributes.minreq = buffer_size; | 206 buffer_attributes.minreq = buffer_size; |
| 208 buffer_attributes.prebuf = static_cast<uint32_t>(-1); | 207 buffer_attributes.prebuf = static_cast<uint32_t>(-1); |
| 209 buffer_attributes.fragsize = buffer_size; | 208 buffer_attributes.fragsize = buffer_size; |
| 210 int flags = PA_STREAM_AUTO_TIMING_UPDATE | | 209 int flags = PA_STREAM_AUTO_TIMING_UPDATE | |
| 211 PA_STREAM_INTERPOLATE_TIMING | | 210 PA_STREAM_INTERPOLATE_TIMING | |
| 212 PA_STREAM_ADJUST_LATENCY | | 211 PA_STREAM_ADJUST_LATENCY | |
| 213 PA_STREAM_START_CORKED; | 212 PA_STREAM_START_CORKED; |
| 214 RETURN_ON_FAILURE( | 213 RETURN_ON_FAILURE( |
| 215 pa_stream_connect_record(*stream, device_id.c_str(), &buffer_attributes, | 214 pa_stream_connect_record( |
| 216 static_cast<pa_stream_flags_t>(flags)) == 0, | 215 *stream, device_id == AudioDeviceDescription::kDefaultDeviceId |
| 216 ? NULL |
| 217 : device_id.c_str(), |
| 218 &buffer_attributes, static_cast<pa_stream_flags_t>(flags)) == 0, |
| 217 "pa_stream_connect_record FAILED "); | 219 "pa_stream_connect_record FAILED "); |
| 218 | 220 |
| 219 // Wait for the stream to be ready. | 221 // Wait for the stream to be ready. |
| 220 while (true) { | 222 while (true) { |
| 221 pa_stream_state_t stream_state = pa_stream_get_state(*stream); | 223 pa_stream_state_t stream_state = pa_stream_get_state(*stream); |
| 222 RETURN_ON_FAILURE( | 224 RETURN_ON_FAILURE( |
| 223 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); | 225 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); |
| 224 if (stream_state == PA_STREAM_READY) | 226 if (stream_state == PA_STREAM_READY) |
| 225 break; | 227 break; |
| 226 pa_threaded_mainloop_wait(mainloop); | 228 pa_threaded_mainloop_wait(mainloop); |
| 227 } | 229 } |
| 228 | 230 |
| 229 return true; | 231 return true; |
| 230 } | 232 } |
| 231 | 233 |
| 232 bool CreateOutputStream(pa_threaded_mainloop* mainloop, | 234 bool CreateOutputStream(pa_threaded_mainloop** mainloop, |
| 233 pa_context* context, | 235 pa_context** context, |
| 234 pa_stream** stream, | 236 pa_stream** stream, |
| 235 const AudioParameters& params, | 237 const AudioParameters& params, |
| 236 const std::string& device_id, | 238 const std::string& device_id, |
| 237 const std::string& app_name, | 239 const std::string& app_name, |
| 238 pa_stream_notify_cb_t stream_callback, | 240 pa_stream_notify_cb_t stream_callback, |
| 239 pa_stream_request_cb_t write_callback, | 241 pa_stream_request_cb_t write_callback, |
| 240 void* user_data) { | 242 void* user_data) { |
| 241 DCHECK(mainloop); | 243 DCHECK(!*mainloop); |
| 242 DCHECK(context); | 244 DCHECK(!*context); |
| 243 DCHECK(device_id != AudioDeviceDescription::kDefaultDeviceId); | 245 |
| 246 *mainloop = pa_threaded_mainloop_new(); |
| 247 RETURN_ON_FAILURE(*mainloop, "Failed to create PulseAudio main loop."); |
| 248 |
| 249 pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(*mainloop); |
| 250 *context = pa_context_new(pa_mainloop_api, |
| 251 app_name.empty() ? "Chromium" : app_name.c_str()); |
| 252 RETURN_ON_FAILURE(*context, "Failed to create PulseAudio context."); |
| 253 |
| 254 // A state callback must be set before calling pa_threaded_mainloop_lock() or |
| 255 // pa_threaded_mainloop_wait() calls may lead to dead lock. |
| 256 pa_context_set_state_callback(*context, &ContextStateCallback, *mainloop); |
| 257 |
| 258 // Lock the main loop while setting up the context. Failure to do so may lead |
| 259 // to crashes as the PulseAudio thread tries to run before things are ready. |
| 260 AutoPulseLock auto_lock(*mainloop); |
| 261 |
| 262 RETURN_ON_FAILURE(pa_threaded_mainloop_start(*mainloop) == 0, |
| 263 "Failed to start PulseAudio main loop."); |
| 264 RETURN_ON_FAILURE( |
| 265 pa_context_connect(*context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0, |
| 266 "Failed to connect PulseAudio context."); |
| 267 |
| 268 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be |
| 269 // called after pa_context_get_state() in case the context is already ready, |
| 270 // otherwise pa_threaded_mainloop_wait() will hang indefinitely. |
| 271 while (true) { |
| 272 pa_context_state_t context_state = pa_context_get_state(*context); |
| 273 RETURN_ON_FAILURE(PA_CONTEXT_IS_GOOD(context_state), |
| 274 "Invalid PulseAudio context state."); |
| 275 if (context_state == PA_CONTEXT_READY) |
| 276 break; |
| 277 pa_threaded_mainloop_wait(*mainloop); |
| 278 } |
| 244 | 279 |
| 245 // Set sample specifications. | 280 // Set sample specifications. |
| 246 pa_sample_spec sample_specifications; | 281 pa_sample_spec sample_specifications; |
| 247 sample_specifications.format = BitsToPASampleFormat( | 282 sample_specifications.format = BitsToPASampleFormat( |
| 248 params.bits_per_sample()); | 283 params.bits_per_sample()); |
| 249 sample_specifications.rate = params.sample_rate(); | 284 sample_specifications.rate = params.sample_rate(); |
| 250 sample_specifications.channels = params.channels(); | 285 sample_specifications.channels = params.channels(); |
| 251 | 286 |
| 252 // Get channel mapping. | 287 // Get channel mapping. |
| 253 pa_channel_map* map = NULL; | 288 pa_channel_map* map = NULL; |
| 254 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( | 289 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( |
| 255 params.channel_layout()); | 290 params.channel_layout()); |
| 256 if (source_channel_map.channels != 0) { | 291 if (source_channel_map.channels != 0) { |
| 257 // The source data uses a supported channel map so we will use it rather | 292 // The source data uses a supported channel map so we will use it rather |
| 258 // than the default channel map (NULL). | 293 // than the default channel map (NULL). |
| 259 map = &source_channel_map; | 294 map = &source_channel_map; |
| 260 } | 295 } |
| 261 | 296 |
| 262 // Open playback stream and | 297 // Open playback stream and |
| 263 // tell PulseAudio what the stream icon should be. | 298 // tell PulseAudio what the stream icon should be. |
| 264 ScopedPropertyList property_list; | 299 ScopedPropertyList property_list; |
| 265 pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME, | 300 pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME, |
| 266 kBrowserDisplayName); | 301 kBrowserDisplayName); |
| 267 *stream = pa_stream_new_with_proplist( | 302 *stream = pa_stream_new_with_proplist( |
| 268 context, "Playback", &sample_specifications, map, property_list.get()); | 303 *context, "Playback", &sample_specifications, map, property_list.get()); |
| 269 RETURN_ON_FAILURE(*stream, "failed to create PA playback stream"); | 304 RETURN_ON_FAILURE(*stream, "failed to create PA playback stream"); |
| 270 | 305 |
| 271 pa_stream_set_state_callback(*stream, stream_callback, user_data); | 306 pa_stream_set_state_callback(*stream, stream_callback, user_data); |
| 272 | 307 |
| 273 // Even though we start the stream corked above, PulseAudio will issue one | 308 // Even though we start the stream corked above, PulseAudio will issue one |
| 274 // stream request after setup. write_callback() must fulfill the write. | 309 // stream request after setup. write_callback() must fulfill the write. |
| 275 pa_stream_set_write_callback(*stream, write_callback, user_data); | 310 pa_stream_set_write_callback(*stream, write_callback, user_data); |
| 276 | 311 |
| 277 // Pulse is very finicky with the small buffer sizes used by Chrome. The | 312 // Pulse is very finicky with the small buffer sizes used by Chrome. The |
| 278 // settings below are mostly found through trial and error. Essentially we | 313 // settings below are mostly found through trial and error. Essentially we |
| (...skipping 10 matching lines...) Expand all Loading... |
| 289 pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2; | 324 pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2; |
| 290 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); | 325 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); |
| 291 pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3; | 326 pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3; |
| 292 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); | 327 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); |
| 293 | 328 |
| 294 // Connect playback stream. Like pa_buffer_attr, the pa_stream_flags have a | 329 // Connect playback stream. Like pa_buffer_attr, the pa_stream_flags have a |
| 295 // huge impact on the performance of the stream and were chosen through trial | 330 // huge impact on the performance of the stream and were chosen through trial |
| 296 // and error. | 331 // and error. |
| 297 RETURN_ON_FAILURE( | 332 RETURN_ON_FAILURE( |
| 298 pa_stream_connect_playback( | 333 pa_stream_connect_playback( |
| 299 *stream, device_id.c_str(), &pa_buffer_attributes, | 334 *stream, device_id == AudioDeviceDescription::kDefaultDeviceId |
| 335 ? NULL |
| 336 : device_id.c_str(), |
| 337 &pa_buffer_attributes, |
| 300 static_cast<pa_stream_flags_t>( | 338 static_cast<pa_stream_flags_t>( |
| 301 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | | 339 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | |
| 302 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC | | 340 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC | |
| 303 PA_STREAM_START_CORKED), | 341 PA_STREAM_START_CORKED), |
| 304 NULL, NULL) == 0, | 342 NULL, NULL) == 0, |
| 305 "pa_stream_connect_playback FAILED "); | 343 "pa_stream_connect_playback FAILED "); |
| 306 | 344 |
| 307 // Wait for the stream to be ready. | 345 // Wait for the stream to be ready. |
| 308 while (true) { | 346 while (true) { |
| 309 pa_stream_state_t stream_state = pa_stream_get_state(*stream); | 347 pa_stream_state_t stream_state = pa_stream_get_state(*stream); |
| 310 RETURN_ON_FAILURE( | 348 RETURN_ON_FAILURE( |
| 311 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); | 349 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); |
| 312 if (stream_state == PA_STREAM_READY) | 350 if (stream_state == PA_STREAM_READY) |
| 313 break; | 351 break; |
| 314 pa_threaded_mainloop_wait(mainloop); | 352 pa_threaded_mainloop_wait(*mainloop); |
| 315 } | 353 } |
| 316 | 354 |
| 317 return true; | 355 return true; |
| 318 } | 356 } |
| 319 | 357 |
| 320 #undef RETURN_ON_FAILURE | 358 #undef RETURN_ON_FAILURE |
| 321 | 359 |
| 322 } // namespace pulse | 360 } // namespace pulse |
| 323 | 361 |
| 324 } // namespace media | 362 } // namespace media |
| OLD | NEW |