| 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" |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 } | 54 } |
| 55 | 55 |
| 56 PulseAudioOutputStream::~PulseAudioOutputStream() { | 56 PulseAudioOutputStream::~PulseAudioOutputStream() { |
| 57 // All internal structures should already have been freed in Close(), which | 57 // All internal structures should already have been freed in Close(), which |
| 58 // calls AudioManagerBase::ReleaseOutputStream() which deletes this object. | 58 // calls AudioManagerBase::ReleaseOutputStream() which deletes this object. |
| 59 DCHECK(!pa_stream_); | 59 DCHECK(!pa_stream_); |
| 60 DCHECK(!pa_context_); | 60 DCHECK(!pa_context_); |
| 61 DCHECK(!pa_mainloop_); | 61 DCHECK(!pa_mainloop_); |
| 62 } | 62 } |
| 63 | 63 |
| 64 bool PulseAudioOutputStream::InitializeMainloopAndContext() { |
| 65 DCHECK(!pa_mainloop_); |
| 66 DCHECK(!pa_context_); |
| 67 DCHECK(thread_checker_.CalledOnValidThread()); |
| 68 pa_mainloop_ = pa_threaded_mainloop_new(); |
| 69 if (!pa_mainloop_) { |
| 70 DLOG(ERROR) << "Failed to create PulseAudio main loop."; |
| 71 return false; |
| 72 } |
| 73 |
| 74 pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_); |
| 75 std::string app_name = AudioManager::GetGlobalAppName(); |
| 76 pa_context_ = pa_context_new( |
| 77 pa_mainloop_api, app_name.empty() ? "Chromium" : app_name.c_str()); |
| 78 if (!pa_context_) { |
| 79 DLOG(ERROR) << "Failed to create PulseAudio context."; |
| 80 return false; |
| 81 } |
| 82 |
| 83 // A state callback must be set before calling pa_threaded_mainloop_lock() or |
| 84 // pa_threaded_mainloop_wait() calls may lead to dead lock. |
| 85 pa_context_set_state_callback(pa_context_, &pulse::ContextStateCallback, |
| 86 pa_mainloop_); |
| 87 { |
| 88 // Lock the main loop while setting up the context. Failure to do so may |
| 89 // lead to crashes as the PulseAudio thread tries to run before things are |
| 90 // ready. |
| 91 AutoPulseLock auto_lock(pa_mainloop_); |
| 92 |
| 93 if (pa_threaded_mainloop_start(pa_mainloop_) != 0) { |
| 94 DLOG(ERROR) << "Failed to start PulseAudio main loop."; |
| 95 return false; |
| 96 } |
| 97 |
| 98 if (pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) != |
| 99 0) { |
| 100 DLOG(ERROR) << "Failed to connect PulseAudio context."; |
| 101 return false; |
| 102 } |
| 103 |
| 104 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be |
| 105 // called after pa_context_get_state() in case the context is already ready, |
| 106 // otherwise pa_threaded_mainloop_wait() will hang indefinitely. |
| 107 while (true) { |
| 108 pa_context_state_t context_state = pa_context_get_state(pa_context_); |
| 109 if (!PA_CONTEXT_IS_GOOD(context_state)) { |
| 110 DLOG(ERROR) << "Invalid PulseAudio context state."; |
| 111 return false; |
| 112 } |
| 113 |
| 114 if (context_state == PA_CONTEXT_READY) |
| 115 break; |
| 116 pa_threaded_mainloop_wait(pa_mainloop_); |
| 117 } |
| 118 } |
| 119 |
| 120 return true; |
| 121 } |
| 122 |
| 123 // static, used by pa_context_get_server_info. |
| 124 void PulseAudioOutputStream::GetSystemDefaultOutputDeviceCallback( |
| 125 pa_context* context, |
| 126 const pa_server_info* info, |
| 127 void* user_data) { |
| 128 media::PulseAudioOutputStream* stream = |
| 129 static_cast<media::PulseAudioOutputStream*>(user_data); |
| 130 stream->default_system_device_name_ = info->default_sink_name; |
| 131 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); |
| 132 } |
| 133 |
| 134 void PulseAudioOutputStream::GetSystemDefaultOutputDevice() { |
| 135 DCHECK(thread_checker_.CalledOnValidThread()); |
| 136 DCHECK(pa_mainloop_); |
| 137 DCHECK(pa_context_); |
| 138 pa_operation* operation = pa_context_get_server_info( |
| 139 pa_context_, PulseAudioOutputStream::GetSystemDefaultOutputDeviceCallback, |
| 140 this); |
| 141 WaitForOperationCompletion(pa_mainloop_, operation); |
| 142 } |
| 143 |
| 64 bool PulseAudioOutputStream::Open() { | 144 bool PulseAudioOutputStream::Open() { |
| 65 DCHECK(thread_checker_.CalledOnValidThread()); | 145 DCHECK(thread_checker_.CalledOnValidThread()); |
| 146 if (!InitializeMainloopAndContext()) { |
| 147 return false; |
| 148 } |
| 149 |
| 150 AutoPulseLock auto_lock(pa_mainloop_); |
| 151 |
| 152 std::string device_name_to_use = device_id_; |
| 153 if (device_id_ == AudioManagerBase::kDefaultDeviceId) { |
| 154 GetSystemDefaultOutputDevice(); |
| 155 device_name_to_use = default_system_device_name_; |
| 156 } |
| 157 |
| 66 return pulse::CreateOutputStream( | 158 return pulse::CreateOutputStream( |
| 67 &pa_mainloop_, &pa_context_, &pa_stream_, params_, device_id_, | 159 pa_mainloop_, pa_context_, &pa_stream_, params_, device_name_to_use, |
| 68 AudioManager::GetGlobalAppName(), &StreamNotifyCallback, | 160 AudioManager::GetGlobalAppName(), &StreamNotifyCallback, |
| 69 &StreamRequestCallback, this); | 161 &StreamRequestCallback, this); |
| 70 } | 162 } |
| 71 | 163 |
| 72 void PulseAudioOutputStream::Reset() { | 164 void PulseAudioOutputStream::Reset() { |
| 73 if (!pa_mainloop_) { | 165 if (!pa_mainloop_) { |
| 74 DCHECK(!pa_stream_); | 166 DCHECK(!pa_stream_); |
| 75 DCHECK(!pa_context_); | 167 DCHECK(!pa_context_); |
| 76 return; | 168 return; |
| 77 } | 169 } |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 229 volume_ = static_cast<float>(volume); | 321 volume_ = static_cast<float>(volume); |
| 230 } | 322 } |
| 231 | 323 |
| 232 void PulseAudioOutputStream::GetVolume(double* volume) { | 324 void PulseAudioOutputStream::GetVolume(double* volume) { |
| 233 DCHECK(thread_checker_.CalledOnValidThread()); | 325 DCHECK(thread_checker_.CalledOnValidThread()); |
| 234 | 326 |
| 235 *volume = volume_; | 327 *volume = volume_; |
| 236 } | 328 } |
| 237 | 329 |
| 238 } // namespace media | 330 } // namespace media |
| OLD | NEW |