Chromium Code Reviews| 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 | 173 DCHECK_NE(device_id, AudioManagerBase::kDefaultDeviceId); |
|
Henrik Grunell
2016/03/21 09:46:14
Keep the empty line after last DCHECK.
rchtara
2016/03/22 16:45:49
Done.
| |
| 174 // Set sample specifications. | 174 // Set sample specifications. |
| 175 pa_sample_spec sample_specifications; | 175 pa_sample_spec sample_specifications; |
| 176 sample_specifications.format = BitsToPASampleFormat( | 176 sample_specifications.format = BitsToPASampleFormat( |
| 177 params.bits_per_sample()); | 177 params.bits_per_sample()); |
| 178 sample_specifications.rate = params.sample_rate(); | 178 sample_specifications.rate = params.sample_rate(); |
| 179 sample_specifications.channels = params.channels(); | 179 sample_specifications.channels = params.channels(); |
| 180 | 180 |
| 181 // Get channel mapping and open recording stream. | 181 // Get channel mapping and open recording stream. |
| 182 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( | 182 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( |
| 183 params.channel_layout()); | 183 params.channel_layout()); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 204 buffer_attributes.maxlength = static_cast<uint32_t>(-1); | 204 buffer_attributes.maxlength = static_cast<uint32_t>(-1); |
| 205 buffer_attributes.tlength = buffer_size; | 205 buffer_attributes.tlength = buffer_size; |
| 206 buffer_attributes.minreq = buffer_size; | 206 buffer_attributes.minreq = buffer_size; |
| 207 buffer_attributes.prebuf = static_cast<uint32_t>(-1); | 207 buffer_attributes.prebuf = static_cast<uint32_t>(-1); |
| 208 buffer_attributes.fragsize = buffer_size; | 208 buffer_attributes.fragsize = buffer_size; |
| 209 int flags = PA_STREAM_AUTO_TIMING_UPDATE | | 209 int flags = PA_STREAM_AUTO_TIMING_UPDATE | |
| 210 PA_STREAM_INTERPOLATE_TIMING | | 210 PA_STREAM_INTERPOLATE_TIMING | |
| 211 PA_STREAM_ADJUST_LATENCY | | 211 PA_STREAM_ADJUST_LATENCY | |
| 212 PA_STREAM_START_CORKED; | 212 PA_STREAM_START_CORKED; |
| 213 RETURN_ON_FAILURE( | 213 RETURN_ON_FAILURE( |
| 214 pa_stream_connect_record( | 214 pa_stream_connect_record(*stream, device_id.c_str(), &buffer_attributes, |
| 215 *stream, | 215 static_cast<pa_stream_flags_t>(flags)) == 0, |
| 216 device_id == AudioManagerBase::kDefaultDeviceId ? | |
| 217 NULL : device_id.c_str(), | |
| 218 &buffer_attributes, | |
| 219 static_cast<pa_stream_flags_t>(flags)) == 0, | |
| 220 "pa_stream_connect_record FAILED "); | 216 "pa_stream_connect_record FAILED "); |
| 221 | 217 |
| 222 // Wait for the stream to be ready. | 218 // Wait for the stream to be ready. |
| 223 while (true) { | 219 while (true) { |
| 224 pa_stream_state_t stream_state = pa_stream_get_state(*stream); | 220 pa_stream_state_t stream_state = pa_stream_get_state(*stream); |
| 225 RETURN_ON_FAILURE( | 221 RETURN_ON_FAILURE( |
| 226 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); | 222 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); |
| 227 if (stream_state == PA_STREAM_READY) | 223 if (stream_state == PA_STREAM_READY) |
| 228 break; | 224 break; |
| 229 pa_threaded_mainloop_wait(mainloop); | 225 pa_threaded_mainloop_wait(mainloop); |
| 230 } | 226 } |
| 231 | 227 |
| 232 return true; | 228 return true; |
| 233 } | 229 } |
| 234 | 230 |
| 235 bool CreateOutputStream(pa_threaded_mainloop** mainloop, | 231 bool CreateOutputStream(pa_threaded_mainloop* mainloop, |
| 236 pa_context** context, | 232 pa_context* context, |
| 237 pa_stream** stream, | 233 pa_stream** stream, |
| 238 const AudioParameters& params, | 234 const AudioParameters& params, |
| 239 const std::string& device_id, | 235 const std::string& device_id, |
| 240 const std::string& app_name, | 236 const std::string& app_name, |
| 241 pa_stream_notify_cb_t stream_callback, | 237 pa_stream_notify_cb_t stream_callback, |
| 242 pa_stream_request_cb_t write_callback, | 238 pa_stream_request_cb_t write_callback, |
| 243 void* user_data) { | 239 void* user_data) { |
| 244 DCHECK(!*mainloop); | 240 DCHECK(mainloop); |
| 245 DCHECK(!*context); | 241 DCHECK(context); |
| 246 | 242 DCHECK(device_id != AudioManagerBase::kDefaultDeviceId); |
| 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 } | |
| 280 | 243 |
| 281 // Set sample specifications. | 244 // Set sample specifications. |
| 282 pa_sample_spec sample_specifications; | 245 pa_sample_spec sample_specifications; |
| 283 sample_specifications.format = BitsToPASampleFormat( | 246 sample_specifications.format = BitsToPASampleFormat( |
| 284 params.bits_per_sample()); | 247 params.bits_per_sample()); |
| 285 sample_specifications.rate = params.sample_rate(); | 248 sample_specifications.rate = params.sample_rate(); |
| 286 sample_specifications.channels = params.channels(); | 249 sample_specifications.channels = params.channels(); |
| 287 | 250 |
| 288 // Get channel mapping. | 251 // Get channel mapping. |
| 289 pa_channel_map* map = NULL; | 252 pa_channel_map* map = NULL; |
| 290 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( | 253 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( |
| 291 params.channel_layout()); | 254 params.channel_layout()); |
| 292 if (source_channel_map.channels != 0) { | 255 if (source_channel_map.channels != 0) { |
| 293 // The source data uses a supported channel map so we will use it rather | 256 // The source data uses a supported channel map so we will use it rather |
| 294 // than the default channel map (NULL). | 257 // than the default channel map (NULL). |
| 295 map = &source_channel_map; | 258 map = &source_channel_map; |
| 296 } | 259 } |
| 297 | 260 |
| 298 // Open playback stream and | 261 // Open playback stream and |
| 299 // tell PulseAudio what the stream icon should be. | 262 // tell PulseAudio what the stream icon should be. |
| 300 ScopedPropertyList property_list; | 263 ScopedPropertyList property_list; |
| 301 pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME, | 264 pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME, |
| 302 kBrowserDisplayName); | 265 kBrowserDisplayName); |
| 303 *stream = pa_stream_new_with_proplist( | 266 *stream = pa_stream_new_with_proplist( |
| 304 *context, "Playback", &sample_specifications, map, property_list.get()); | 267 context, "Playback", &sample_specifications, map, property_list.get()); |
| 305 RETURN_ON_FAILURE(*stream, "failed to create PA playback stream"); | 268 RETURN_ON_FAILURE(*stream, "failed to create PA playback stream"); |
| 306 | 269 |
| 307 pa_stream_set_state_callback(*stream, stream_callback, user_data); | 270 pa_stream_set_state_callback(*stream, stream_callback, user_data); |
| 308 | 271 |
| 309 // Even though we start the stream corked above, PulseAudio will issue one | 272 // Even though we start the stream corked above, PulseAudio will issue one |
| 310 // stream request after setup. write_callback() must fulfill the write. | 273 // stream request after setup. write_callback() must fulfill the write. |
| 311 pa_stream_set_write_callback(*stream, write_callback, user_data); | 274 pa_stream_set_write_callback(*stream, write_callback, user_data); |
| 312 | 275 |
| 313 // Pulse is very finicky with the small buffer sizes used by Chrome. The | 276 // Pulse is very finicky with the small buffer sizes used by Chrome. The |
| 314 // settings below are mostly found through trial and error. Essentially we | 277 // settings below are mostly found through trial and error. Essentially we |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 325 pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2; | 288 pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2; |
| 326 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); | 289 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); |
| 327 pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3; | 290 pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3; |
| 328 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); | 291 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); |
| 329 | 292 |
| 330 // Connect playback stream. Like pa_buffer_attr, the pa_stream_flags have a | 293 // Connect playback stream. Like pa_buffer_attr, the pa_stream_flags have a |
| 331 // huge impact on the performance of the stream and were chosen through trial | 294 // huge impact on the performance of the stream and were chosen through trial |
| 332 // and error. | 295 // and error. |
| 333 RETURN_ON_FAILURE( | 296 RETURN_ON_FAILURE( |
| 334 pa_stream_connect_playback( | 297 pa_stream_connect_playback( |
| 335 *stream, | 298 *stream, device_id.c_str(), &pa_buffer_attributes, |
| 336 device_id == AudioManagerBase::kDefaultDeviceId ? | |
| 337 NULL : device_id.c_str(), | |
| 338 &pa_buffer_attributes, | |
| 339 static_cast<pa_stream_flags_t>( | 299 static_cast<pa_stream_flags_t>( |
| 340 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | | 300 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | |
| 341 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC | | 301 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC | |
| 342 PA_STREAM_START_CORKED), | 302 PA_STREAM_START_CORKED), |
| 343 NULL, | 303 NULL, NULL) == 0, |
| 344 NULL) == 0, | |
| 345 "pa_stream_connect_playback FAILED "); | 304 "pa_stream_connect_playback FAILED "); |
| 346 | 305 |
| 347 // Wait for the stream to be ready. | 306 // Wait for the stream to be ready. |
| 348 while (true) { | 307 while (true) { |
| 349 pa_stream_state_t stream_state = pa_stream_get_state(*stream); | 308 pa_stream_state_t stream_state = pa_stream_get_state(*stream); |
| 350 RETURN_ON_FAILURE( | 309 RETURN_ON_FAILURE( |
| 351 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); | 310 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); |
| 352 if (stream_state == PA_STREAM_READY) | 311 if (stream_state == PA_STREAM_READY) |
| 353 break; | 312 break; |
| 354 pa_threaded_mainloop_wait(*mainloop); | 313 pa_threaded_mainloop_wait(mainloop); |
| 355 } | 314 } |
| 356 | 315 |
| 357 return true; | 316 return true; |
| 358 } | 317 } |
| 359 | 318 |
| 360 #undef RETURN_ON_FAILURE | 319 #undef RETURN_ON_FAILURE |
| 361 | 320 |
| 362 } // namespace pulse | 321 } // namespace pulse |
| 363 | 322 |
| 364 } // namespace media | 323 } // namespace media |
| OLD | NEW |