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 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 | 154 |
| 155 // Helper macro for CreateInput/OutputStream() to avoid code spam and | 155 // Helper macro for CreateInput/OutputStream() to avoid code spam and |
| 156 // string bloat. | 156 // string bloat. |
| 157 #define RETURN_ON_FAILURE(expression, message) do { \ | 157 #define RETURN_ON_FAILURE(expression, message) do { \ |
| 158 if (!(expression)) { \ | 158 if (!(expression)) { \ |
| 159 DLOG(ERROR) << message; \ | 159 DLOG(ERROR) << message; \ |
| 160 return false; \ | 160 return false; \ |
| 161 } \ | 161 } \ |
| 162 } while (0) | 162 } while (0) |
| 163 | 163 |
| 164 bool CreateInputStream(pa_threaded_mainloop* mainloop, | 164 bool CreateInputStream(pa_threaded_mainloop* mainloop, |
|
Henrik Grunell
2016/03/07 11:37:18
Add a todo comment in the header file for this and
rchtara
2016/03/08 17:28:17
Done.
| |
| 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 |
| 174 // Set sample specifications. | 174 // Set sample specifications. |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 203 const unsigned int buffer_size = params.GetBytesPerBuffer(); | 203 const unsigned int buffer_size = params.GetBytesPerBuffer(); |
| 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( |
|
Henrik Grunell
2016/03/07 11:37:17
Add DCHECK that device_id != AudioManagerBase::kDe
rchtara
2016/03/08 17:28:17
Done.
| |
| 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, |
|
Henrik Grunell
2016/03/07 11:37:17
Change to take pa_threaded_mainloop* and pa_contex
rchtara
2016/03/08 17:28:17
Done.
| |
| 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 | |
| 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 | 242 |
| 281 // Set sample specifications. | 243 // Set sample specifications. |
|
Henrik Grunell
2016/03/07 11:37:17
Do we need to grab the lock here? The old code hel
rchtara
2016/03/08 17:28:17
No sure about this too.
The last version is workin
Henrik Grunell
2016/03/09 00:49:16
Is it guaranteed that we can never get any concurr
rchtara
2016/03/09 16:16:09
So let's keep the lock. it safer
tommi (sloooow) - chröme
2016/03/19 13:44:17
We also don't want inefficiency :) Best is to und
Henrik Grunell
2016/03/21 09:44:18
Is the lock anyhow related to PulseAudio internals
Henrik Grunell
2016/03/23 12:51:27
Also, while we should sure not grab a lock if we d
| |
| 282 pa_sample_spec sample_specifications; | 244 pa_sample_spec sample_specifications; |
| 283 sample_specifications.format = BitsToPASampleFormat( | 245 sample_specifications.format = BitsToPASampleFormat( |
| 284 params.bits_per_sample()); | 246 params.bits_per_sample()); |
| 285 sample_specifications.rate = params.sample_rate(); | 247 sample_specifications.rate = params.sample_rate(); |
| 286 sample_specifications.channels = params.channels(); | 248 sample_specifications.channels = params.channels(); |
| 287 | 249 |
| 288 // Get channel mapping. | 250 // Get channel mapping. |
| 289 pa_channel_map* map = NULL; | 251 pa_channel_map* map = NULL; |
| 290 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( | 252 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap( |
| 291 params.channel_layout()); | 253 params.channel_layout()); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 325 pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2; | 287 pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2; |
| 326 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); | 288 pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1); |
| 327 pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3; | 289 pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3; |
| 328 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); | 290 pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1); |
| 329 | 291 |
| 330 // Connect playback stream. Like pa_buffer_attr, the pa_stream_flags have a | 292 // 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 | 293 // huge impact on the performance of the stream and were chosen through trial |
| 332 // and error. | 294 // and error. |
| 333 RETURN_ON_FAILURE( | 295 RETURN_ON_FAILURE( |
| 334 pa_stream_connect_playback( | 296 pa_stream_connect_playback( |
| 335 *stream, | 297 *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>( | 298 static_cast<pa_stream_flags_t>( |
| 340 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | | 299 PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | |
| 341 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC | | 300 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC | |
| 342 PA_STREAM_START_CORKED), | 301 PA_STREAM_START_CORKED), |
| 343 NULL, | 302 NULL, NULL) == 0, |
| 344 NULL) == 0, | |
| 345 "pa_stream_connect_playback FAILED "); | 303 "pa_stream_connect_playback FAILED "); |
| 346 | 304 |
| 347 // Wait for the stream to be ready. | 305 // Wait for the stream to be ready. |
| 348 while (true) { | 306 while (true) { |
| 349 pa_stream_state_t stream_state = pa_stream_get_state(*stream); | 307 pa_stream_state_t stream_state = pa_stream_get_state(*stream); |
| 350 RETURN_ON_FAILURE( | 308 RETURN_ON_FAILURE( |
| 351 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); | 309 PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state"); |
| 352 if (stream_state == PA_STREAM_READY) | 310 if (stream_state == PA_STREAM_READY) |
| 353 break; | 311 break; |
| 354 pa_threaded_mainloop_wait(*mainloop); | 312 pa_threaded_mainloop_wait(*mainloop); |
| 355 } | 313 } |
| 356 | 314 |
| 357 return true; | 315 return true; |
| 358 } | 316 } |
| 359 | 317 |
| 360 #undef RETURN_ON_FAILURE | 318 #undef RETURN_ON_FAILURE |
| 361 | 319 |
| 362 } // namespace pulse | 320 } // namespace pulse |
| 363 | 321 |
| 364 } // namespace media | 322 } // namespace media |
| OLD | NEW |