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