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_output.h" | 5 #include "media/audio/pulse/pulse_output.h" |
| 6 | 6 |
| 7 #include <pulse/pulseaudio.h> | 7 #include <pulse/pulseaudio.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include "base/single_thread_task_runner.h" | 10 #include "base/single_thread_task_runner.h" |
| 11 #include "media/audio/audio_manager_base.h" | 11 #include "media/audio/audio_manager_base.h" |
| 12 #include "media/audio/pulse/pulse_util.h" | 12 #include "media/audio/pulse/pulse_util.h" |
| 13 | 13 |
| 14 namespace { | |
| 15 | |
| 16 // pa_context_get_server_info callback. It's used by | |
| 17 // GetSystemDefaultOutputDevice to set |default_system_device_name_| to the | |
| 18 // default system output device. | |
| 19 void GetSystemDefaultOutputDeviceCallback(pa_context* context, | |
| 20 const pa_server_info* info, | |
| 21 void* user_data) { | |
| 22 media::PulseAudioOutputStream* stream = | |
| 23 reinterpret_cast<media::PulseAudioOutputStream*>(user_data); | |
| 24 stream->SetDefaultSystemDeviceName(info->default_sink_name); | |
| 25 pa_threaded_mainloop* pa_mainloop = stream->GetPAMainloop(); | |
| 26 pa_threaded_mainloop_signal(pa_mainloop, 0); | |
|
Henrik Grunell
2016/03/10 00:57:07
You could just use stream->GetPAMainloop() directl
rchtara
2016/03/10 13:20:35
Done.
| |
| 27 } | |
| 28 } | |
| 29 | |
| 14 namespace media { | 30 namespace media { |
| 15 | 31 |
| 16 using pulse::AutoPulseLock; | 32 using pulse::AutoPulseLock; |
| 17 using pulse::WaitForOperationCompletion; | 33 using pulse::WaitForOperationCompletion; |
| 18 | 34 |
| 35 // Helper macro to avoid code spam and string bloat. | |
| 36 #define RETURN_ON_FAILURE(expression, message) \ | |
| 37 do { \ | |
| 38 if (!(expression)) { \ | |
| 39 DLOG(ERROR) << message; \ | |
| 40 return false; \ | |
| 41 } \ | |
| 42 } while (0) | |
| 43 | |
| 19 // static, pa_stream_notify_cb | 44 // static, pa_stream_notify_cb |
| 20 void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) { | 45 void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) { |
| 21 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this); | 46 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this); |
| 22 | 47 |
| 23 // Forward unexpected failures to the AudioSourceCallback if available. All | 48 // Forward unexpected failures to the AudioSourceCallback if available. All |
| 24 // these variables are only modified under pa_threaded_mainloop_lock() so this | 49 // these variables are only modified under pa_threaded_mainloop_lock() so this |
| 25 // should be thread safe. | 50 // should be thread safe. |
| 26 if (s && stream->source_callback_ && | 51 if (s && stream->source_callback_ && |
| 27 pa_stream_get_state(s) == PA_STREAM_FAILED) { | 52 pa_stream_get_state(s) == PA_STREAM_FAILED) { |
| 28 stream->source_callback_->OnError(stream); | 53 stream->source_callback_->OnError(stream); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 54 } | 79 } |
| 55 | 80 |
| 56 PulseAudioOutputStream::~PulseAudioOutputStream() { | 81 PulseAudioOutputStream::~PulseAudioOutputStream() { |
| 57 // All internal structures should already have been freed in Close(), which | 82 // All internal structures should already have been freed in Close(), which |
| 58 // calls AudioManagerBase::ReleaseOutputStream() which deletes this object. | 83 // calls AudioManagerBase::ReleaseOutputStream() which deletes this object. |
| 59 DCHECK(!pa_stream_); | 84 DCHECK(!pa_stream_); |
| 60 DCHECK(!pa_context_); | 85 DCHECK(!pa_context_); |
| 61 DCHECK(!pa_mainloop_); | 86 DCHECK(!pa_mainloop_); |
| 62 } | 87 } |
| 63 | 88 |
| 89 void PulseAudioOutputStream::GetSystemDefaultOutputDevice() { | |
| 90 DCHECK(pa_mainloop_); | |
| 91 DCHECK(pa_context_); | |
| 92 pa_operation* operation = pa_context_get_server_info( | |
| 93 pa_context_, GetSystemDefaultOutputDeviceCallback, this); | |
| 94 WaitForOperationCompletion(pa_mainloop_, operation); | |
| 95 } | |
| 96 | |
| 97 bool PulseAudioOutputStream::InitializeMainloopAndContext() { | |
| 98 DCHECK(!pa_mainloop_); | |
| 99 DCHECK(!pa_context_); | |
| 100 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 101 std::string app_name = AudioManager::GetGlobalAppName(); | |
|
Henrik Grunell
2016/03/10 00:57:07
Move this line to just before where it's used (pa_
rchtara
2016/03/10 13:20:35
Done.
| |
| 102 pa_mainloop_ = pa_threaded_mainloop_new(); | |
| 103 RETURN_ON_FAILURE(pa_mainloop_, "Failed to create PulseAudio main loop."); | |
| 104 | |
| 105 pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_); | |
| 106 pa_context_ = pa_context_new( | |
| 107 pa_mainloop_api, app_name.empty() ? "Chromium" : app_name.c_str()); | |
| 108 RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context."); | |
| 109 | |
| 110 // A state callback must be set before calling pa_threaded_mainloop_lock() or | |
| 111 // pa_threaded_mainloop_wait() calls may lead to dead lock. | |
| 112 pa_context_set_state_callback(pa_context_, &pulse::ContextStateCallback, | |
| 113 pa_mainloop_); | |
| 114 { | |
| 115 // Lock the main loop while setting up the context. Failure to do so may | |
| 116 // lead to crashes as the PulseAudio thread tries to run before things are | |
| 117 // ready. | |
| 118 AutoPulseLock auto_lock(pa_mainloop_); | |
| 119 | |
| 120 RETURN_ON_FAILURE(pa_threaded_mainloop_start(pa_mainloop_) == 0, | |
| 121 "Failed to start PulseAudio main loop."); | |
| 122 RETURN_ON_FAILURE(pa_context_connect(pa_context_, NULL, | |
| 123 PA_CONTEXT_NOAUTOSPAWN, NULL) == 0, | |
| 124 "Failed to connect PulseAudio context."); | |
| 125 | |
| 126 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be | |
| 127 // called after pa_context_get_state() in case the context is already ready, | |
| 128 // otherwise pa_threaded_mainloop_wait() will hang indefinitely. | |
| 129 while (true) { | |
| 130 pa_context_state_t context_state = pa_context_get_state(pa_context_); | |
| 131 RETURN_ON_FAILURE(PA_CONTEXT_IS_GOOD(context_state), | |
| 132 "Invalid PulseAudio context state."); | |
| 133 if (context_state == PA_CONTEXT_READY) | |
| 134 break; | |
| 135 pa_threaded_mainloop_wait(pa_mainloop_); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 return true; | |
| 140 } | |
| 141 | |
| 64 bool PulseAudioOutputStream::Open() { | 142 bool PulseAudioOutputStream::Open() { |
| 65 DCHECK(thread_checker_.CalledOnValidThread()); | 143 std::string app_name = AudioManager::GetGlobalAppName(); |
| 144 std::string device_name_to_use; | |
| 145 if (!InitializeMainloopAndContext()) { | |
| 146 return false; | |
| 147 } | |
| 148 | |
| 149 device_name_to_use = device_id_; | |
|
Henrik Grunell
2016/03/10 00:57:07
std::string device_name_to_use = device_id_;
Then
rchtara
2016/03/10 13:20:35
Done.
| |
| 150 if (device_id_ == AudioManagerBase::kDefaultDeviceId) { | |
| 151 GetSystemDefaultOutputDevice(); | |
|
Henrik Grunell
2016/03/10 00:57:07
I wonder if we should have the lock when calling t
rchtara
2016/03/10 13:20:35
I tied to use a lock before calling it but the bro
Henrik Grunell
2016/03/10 20:07:34
OK, what was the crash? And why doesn't it crash o
rchtara
2016/03/11 10:13:09
I rechecked it, and now it's working : the crash w
| |
| 152 device_name_to_use = default_system_device_name_; | |
| 153 } | |
| 154 | |
| 66 return pulse::CreateOutputStream( | 155 return pulse::CreateOutputStream( |
| 67 &pa_mainloop_, &pa_context_, &pa_stream_, params_, device_id_, | 156 pa_mainloop_, pa_context_, &pa_stream_, params_, device_name_to_use, |
| 68 AudioManager::GetGlobalAppName(), &StreamNotifyCallback, | 157 app_name, &StreamNotifyCallback, &StreamRequestCallback, this); |
|
Henrik Grunell
2016/03/10 00:57:07
Use AudioManager::GetGlobalAppName() here, remove
rchtara
2016/03/10 13:20:35
Done.
| |
| 69 &StreamRequestCallback, this); | |
| 70 } | 158 } |
| 71 | 159 |
| 72 void PulseAudioOutputStream::Reset() { | 160 void PulseAudioOutputStream::Reset() { |
| 73 if (!pa_mainloop_) { | 161 if (!pa_mainloop_) { |
| 74 DCHECK(!pa_stream_); | 162 DCHECK(!pa_stream_); |
| 75 DCHECK(!pa_context_); | 163 DCHECK(!pa_context_); |
| 76 return; | 164 return; |
| 77 } | 165 } |
| 78 | 166 |
| 79 { | 167 { |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 228 | 316 |
| 229 volume_ = static_cast<float>(volume); | 317 volume_ = static_cast<float>(volume); |
| 230 } | 318 } |
| 231 | 319 |
| 232 void PulseAudioOutputStream::GetVolume(double* volume) { | 320 void PulseAudioOutputStream::GetVolume(double* volume) { |
| 233 DCHECK(thread_checker_.CalledOnValidThread()); | 321 DCHECK(thread_checker_.CalledOnValidThread()); |
| 234 | 322 |
| 235 *volume = volume_; | 323 *volume = volume_; |
| 236 } | 324 } |
| 237 | 325 |
| 326 pa_threaded_mainloop* PulseAudioOutputStream::GetPAMainloop() { | |
|
Henrik Grunell
2016/03/10 00:57:07
Put the function definitions in the same order as
rchtara
2016/03/10 13:20:35
They're not ordered as in the header.
But I'm doin
Henrik Grunell
2016/03/10 20:07:34
Ah, OK. Well, best effort is fine then.
rchtara
2016/03/11 10:13:09
Done.
| |
| 327 return pa_mainloop_; | |
| 328 } | |
| 329 | |
| 330 void PulseAudioOutputStream::SetDefaultSystemDeviceName( | |
| 331 const std::string& name) { | |
| 332 default_system_device_name_ = name; | |
| 333 } | |
| 334 | |
| 335 #undef RETURN_ON_FAILURE | |
| 336 | |
| 238 } // namespace media | 337 } // namespace media |
| OLD | NEW |