 Chromium Code Reviews
 Chromium Code Reviews Issue 5859003:
  Add ALSA support to volume keys  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src/chrome/browser/chromeos
    
  
    Issue 5859003:
  Add ALSA support to volume keys  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src/chrome/browser/chromeos| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "chrome/browser/chromeos/pulse_audio_mixer.h" | 5 #include "chrome/browser/chromeos/audio_mixer_pulse.h" | 
| 6 | 6 | 
| 7 #include <pulse/pulseaudio.h> | 7 #include <pulse/pulseaudio.h> | 
| 8 | 8 | 
| 9 #include "base/logging.h" | 9 #include "base/logging.h" | 
| 10 #include "base/task.h" | 10 #include "base/task.h" | 
| 11 | 11 | 
| 12 namespace chromeos { | 12 namespace chromeos { | 
| 13 | 13 | 
| 14 // Using asynchronous versions of the threaded PulseAudio API, as well | 14 // Using asynchronous versions of the threaded PulseAudio API, as well | 
| 15 // as a worker thread so gets, sets, and the init sequence do not block the | 15 // as a worker thread so gets, sets, and the init sequence do not block the | 
| 16 // calling thread. GetVolume() and IsMute() can still be called synchronously | 16 // calling thread. GetVolume() and IsMute() can still be called synchronously | 
| 17 // if needed, but take a bit longer (~2ms vs ~0.3ms). | 17 // if needed, but take a bit longer (~2ms vs ~0.3ms). | 
| 18 // | 18 // | 
| 19 // Set calls just return without waiting. If you must guarantee the value has | 19 // Set calls just return without waiting. If you must guarantee the value has | 
| 20 // been set before continuing, immediately call the blocking Get version to | 20 // been set before continuing, immediately call the blocking Get version to | 
| 21 // synchronously get the value back. | 21 // synchronously get the value back. | 
| 22 // | 22 // | 
| 23 // TODO(davej): Serialize volume/mute to preserve settings when restarting? | 23 // TODO(davej): Serialize volume/mute to preserve settings when restarting? | 
| 24 | 24 | 
| 25 namespace { | 25 namespace { | 
| 26 | 26 | 
| 27 const int kInvalidDeviceId = -1; | 27 const int kInvalidDeviceId = -1; | 
| 28 | 28 | 
| 29 const double kMinVolumeDb = -90.0; | |
| 30 // Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some | |
| 31 // in case sounds or their setup is too quiet for them. | |
| 32 const double kMaxVolumeDb = 6.0; | |
| 33 | |
| 29 // Used for passing custom data to the PulseAudio callbacks. | 34 // Used for passing custom data to the PulseAudio callbacks. | 
| 30 struct CallbackWrapper { | 35 struct CallbackWrapper { | 
| 31 PulseAudioMixer* instance; | 36 AudioMixerPulse* instance; | 
| 32 bool done; | 37 bool done; | 
| 33 void* userdata; | 38 void* userdata; | 
| 34 }; | 39 }; | 
| 35 | 40 | 
| 36 } // namespace | 41 } // namespace | 
| 37 | 42 | 
| 38 // AudioInfo contains all the values we care about when getting info for a | 43 // AudioInfo contains all the values we care about when getting info for a | 
| 39 // Sink (output device) used by GetAudioInfo(). | 44 // Sink (output device) used by GetAudioInfo(). | 
| 40 struct PulseAudioMixer::AudioInfo { | 45 struct AudioMixerPulse::AudioInfo { | 
| 41 pa_cvolume cvolume; | 46 pa_cvolume cvolume; | 
| 42 bool muted; | 47 bool muted; | 
| 43 }; | 48 }; | 
| 44 | 49 | 
| 45 PulseAudioMixer::PulseAudioMixer() | 50 AudioMixerPulse::AudioMixerPulse() | 
| 46 : device_id_(kInvalidDeviceId), | 51 : device_id_(kInvalidDeviceId), | 
| 47 last_channels_(0), | 52 last_channels_(0), | 
| 48 mainloop_lock_count_(0), | 53 mainloop_lock_count_(0), | 
| 49 mixer_state_lock_(), | |
| 50 mixer_state_(UNINITIALIZED), | 54 mixer_state_(UNINITIALIZED), | 
| 51 pa_context_(NULL), | 55 pa_context_(NULL), | 
| 52 pa_mainloop_(NULL), | 56 pa_mainloop_(NULL) { | 
| 53 thread_(NULL) { | |
| 54 } | 57 } | 
| 55 | 58 | 
| 56 PulseAudioMixer::~PulseAudioMixer() { | 59 AudioMixerPulse::~AudioMixerPulse() { | 
| 57 PulseAudioFree(); | 60 PulseAudioFree(); | 
| 58 thread_->Stop(); | 61 if (thread_ != NULL) { | 
| 59 thread_.reset(); | 62 thread_->Stop(); | 
| 63 thread_.reset(); | |
| 64 } | |
| 60 } | 65 } | 
| 61 | 66 | 
| 62 bool PulseAudioMixer::Init(InitDoneCallback* callback) { | 67 bool AudioMixerPulse::Init(InitDoneCallback* callback) { | 
| 63 if (!InitThread()) | 68 if (!InitThread()) | 
| 64 return false; | 69 return false; | 
| 
scherkus (not reviewing)
2011/01/11 00:24:12
what happens to the callback?
 
davejcool
2011/01/11 02:52:54
Ahhh! This callback wasn't being deleted in earlie
 | |
| 65 | 70 | 
| 66 // Post the task of starting up, which can block for 200-500ms, | 71 // Post the task of starting up, which can block for 200-500ms, | 
| 67 // so best not to do it on the caller's thread. | 72 // so best not to do it on the caller's thread. | 
| 68 thread_->message_loop()->PostTask(FROM_HERE, | 73 thread_->message_loop()->PostTask(FROM_HERE, | 
| 69 NewRunnableMethod(this, &PulseAudioMixer::DoInit, callback)); | 74 NewRunnableMethod(this, &AudioMixerPulse::DoInit, callback)); | 
| 70 return true; | 75 return true; | 
| 71 } | 76 } | 
| 72 | 77 | 
| 73 bool PulseAudioMixer::InitSync() { | 78 bool AudioMixerPulse::InitSync() { | 
| 74 if (!InitThread()) | 79 if (!InitThread()) | 
| 75 return false; | 80 return false; | 
| 76 return PulseAudioInit(); | 81 return PulseAudioInit(); | 
| 77 } | 82 } | 
| 78 | 83 | 
| 79 double PulseAudioMixer::GetVolumeDb() const { | 84 double AudioMixerPulse::GetVolumeDb() const { | 
| 80 if (!MainloopLockIfReady()) | 85 if (!MainloopLockIfReady()) | 
| 81 return pa_sw_volume_to_dB(0); // this returns -inf. | 86 return AudioMixer::kSilenceDb; | 
| 82 AudioInfo data; | 87 AudioInfo data; | 
| 83 GetAudioInfo(&data); | 88 GetAudioInfo(&data); | 
| 84 MainloopUnlock(); | 89 MainloopUnlock(); | 
| 85 return pa_sw_volume_to_dB(data.cvolume.values[0]); | 90 return pa_sw_volume_to_dB(data.cvolume.values[0]); | 
| 86 } | 91 } | 
| 87 | 92 | 
| 88 bool PulseAudioMixer::GetVolumeDbAsync(GetVolumeCallback* callback, | 93 bool AudioMixerPulse::GetVolumeLimits(double* vol_min, double* vol_max) { | 
| 89 void* user) { | 94 if (vol_min) | 
| 90 if (CheckState() != READY) | 95 *vol_min = kMinVolumeDb; | 
| 91 return false; | 96 if (vol_max) | 
| 92 thread_->message_loop()->PostTask(FROM_HERE, | 97 *vol_max = kMaxVolumeDb; | 
| 93 NewRunnableMethod(this, | |
| 94 &PulseAudioMixer::DoGetVolume, | |
| 95 callback, user)); | |
| 96 return true; | 98 return true; | 
| 97 } | 99 } | 
| 98 | 100 | 
| 99 void PulseAudioMixer::SetVolumeDb(double vol_db) { | 101 void AudioMixerPulse::SetVolumeDb(double vol_db) { | 
| 100 if (!MainloopLockIfReady()) | 102 if (!MainloopLockIfReady()) | 
| 101 return; | 103 return; | 
| 102 | 104 | 
| 103 // last_channels_ determines the number of channels on the main output device, | 105 // last_channels_ determines the number of channels on the main output device, | 
| 104 // and is used later to set the volume on all channels at once. | 106 // and is used later to set the volume on all channels at once. | 
| 105 if (!last_channels_) { | 107 if (!last_channels_) { | 
| 106 AudioInfo data; | 108 AudioInfo data; | 
| 107 GetAudioInfo(&data); | 109 GetAudioInfo(&data); | 
| 108 last_channels_ = data.cvolume.channels; | 110 last_channels_ = data.cvolume.channels; | 
| 109 } | 111 } | 
| 110 | 112 | 
| 111 pa_operation* pa_op; | 113 pa_operation* pa_op; | 
| 112 pa_cvolume cvolume; | 114 pa_cvolume cvolume; | 
| 113 pa_cvolume_set(&cvolume, last_channels_, pa_sw_volume_from_dB(vol_db)); | 115 pa_cvolume_set(&cvolume, last_channels_, pa_sw_volume_from_dB(vol_db)); | 
| 114 pa_op = pa_context_set_sink_volume_by_index(pa_context_, device_id_, | 116 pa_op = pa_context_set_sink_volume_by_index(pa_context_, device_id_, | 
| 115 &cvolume, NULL, NULL); | 117 &cvolume, NULL, NULL); | 
| 116 pa_operation_unref(pa_op); | 118 pa_operation_unref(pa_op); | 
| 117 MainloopUnlock(); | 119 MainloopUnlock(); | 
| 120 VLOG(1) << "Set volume to " << vol_db << " dB"; | |
| 118 } | 121 } | 
| 119 | 122 | 
| 120 bool PulseAudioMixer::IsMute() const { | 123 bool AudioMixerPulse::IsMute() const { | 
| 121 if (!MainloopLockIfReady()) | 124 if (!MainloopLockIfReady()) | 
| 122 return false; | 125 return false; | 
| 123 AudioInfo data; | 126 AudioInfo data; | 
| 124 GetAudioInfo(&data); | 127 GetAudioInfo(&data); | 
| 125 MainloopUnlock(); | 128 MainloopUnlock(); | 
| 126 return data.muted; | 129 return data.muted; | 
| 127 } | 130 } | 
| 128 | 131 | 
| 129 void PulseAudioMixer::SetMute(bool mute) { | 132 void AudioMixerPulse::SetMute(bool mute) { | 
| 130 if (!MainloopLockIfReady()) | 133 if (!MainloopLockIfReady()) | 
| 131 return; | 134 return; | 
| 132 pa_operation* pa_op; | 135 pa_operation* pa_op; | 
| 133 pa_op = pa_context_set_sink_mute_by_index(pa_context_, device_id_, | 136 pa_op = pa_context_set_sink_mute_by_index(pa_context_, device_id_, | 
| 134 mute ? 1 : 0, NULL, NULL); | 137 mute ? 1 : 0, NULL, NULL); | 
| 135 pa_operation_unref(pa_op); | 138 pa_operation_unref(pa_op); | 
| 136 MainloopUnlock(); | 139 MainloopUnlock(); | 
| 140 VLOG(1) << "Set mute to " << mute; | |
| 137 } | 141 } | 
| 138 | 142 | 
| 139 PulseAudioMixer::State PulseAudioMixer::CheckState() const { | 143 AudioMixer::State AudioMixerPulse::CheckState() const { | 
| 140 AutoLock lock(mixer_state_lock_); | 144 AutoLock lock(mixer_state_lock_); | 
| 141 // If we think it's ready, verify it is actually so. | 145 // If we think it's ready, verify it is actually so. | 
| 142 if ((mixer_state_ == READY) && | 146 if ((mixer_state_ == READY) && | 
| 143 (pa_context_get_state(pa_context_) != PA_CONTEXT_READY)) | 147 (pa_context_get_state(pa_context_) != PA_CONTEXT_READY)) | 
| 144 mixer_state_ = IN_ERROR; | 148 mixer_state_ = IN_ERROR; | 
| 145 return mixer_state_; | 149 return mixer_state_; | 
| 146 } | 150 } | 
| 147 | 151 | 
| 148 //////////////////////////////////////////////////////////////////////////////// | 152 //////////////////////////////////////////////////////////////////////////////// | 
| 149 // Private functions follow | 153 // Private functions follow | 
| 150 | 154 | 
| 151 void PulseAudioMixer::DoInit(InitDoneCallback* callback) { | 155 void AudioMixerPulse::DoInit(InitDoneCallback* callback) { | 
| 152 bool success = PulseAudioInit(); | 156 bool success = PulseAudioInit(); | 
| 153 callback->Run(success); | 157 callback->Run(success); | 
| 154 delete callback; | 158 delete callback; | 
| 155 } | 159 } | 
| 156 | 160 | 
| 157 void PulseAudioMixer::DoGetVolume(GetVolumeCallback* callback, | 161 bool AudioMixerPulse::InitThread() { | 
| 158 void* user) { | |
| 159 callback->Run(GetVolumeDb(), user); | |
| 160 delete callback; | |
| 161 } | |
| 162 | |
| 163 bool PulseAudioMixer::InitThread() { | |
| 164 AutoLock lock(mixer_state_lock_); | 162 AutoLock lock(mixer_state_lock_); | 
| 165 | 163 | 
| 166 if (mixer_state_ != UNINITIALIZED) | 164 if (mixer_state_ != UNINITIALIZED) | 
| 167 return false; | 165 return false; | 
| 166 | |
| 168 if (thread_ == NULL) { | 167 if (thread_ == NULL) { | 
| 169 thread_.reset(new base::Thread("PulseAudioMixer")); | 168 thread_.reset(new base::Thread("AudioMixerPulse")); | 
| 170 if (!thread_->Start()) { | 169 if (!thread_->Start()) { | 
| 171 thread_.reset(); | 170 thread_.reset(); | 
| 172 return false; | 171 return false; | 
| 173 } | 172 } | 
| 174 } | 173 } | 
| 175 mixer_state_ = INITIALIZING; | 174 mixer_state_ = INITIALIZING; | 
| 176 return true; | 175 return true; | 
| 177 } | 176 } | 
| 178 | 177 | 
| 179 // static | 178 // static | 
| 180 void PulseAudioMixer::ConnectToPulseCallbackThunk( | 179 void AudioMixerPulse::ConnectToPulseCallbackThunk( | 
| 181 pa_context* context, void* userdata) { | 180 pa_context* context, void* userdata) { | 
| 182 CallbackWrapper* data = | 181 CallbackWrapper* data = | 
| 183 static_cast<CallbackWrapper*>(userdata); | 182 static_cast<CallbackWrapper*>(userdata); | 
| 184 data->instance->OnConnectToPulseCallback(context, &data->done); | 183 data->instance->OnConnectToPulseCallback(context, &data->done); | 
| 185 } | 184 } | 
| 186 | 185 | 
| 187 void PulseAudioMixer::OnConnectToPulseCallback( | 186 void AudioMixerPulse::OnConnectToPulseCallback( | 
| 188 pa_context* context, bool* connect_done) { | 187 pa_context* context, bool* connect_done) { | 
| 189 pa_context_state_t state = pa_context_get_state(context); | 188 pa_context_state_t state = pa_context_get_state(context); | 
| 190 if (state == PA_CONTEXT_READY || | 189 if (state == PA_CONTEXT_READY || | 
| 191 state == PA_CONTEXT_FAILED || | 190 state == PA_CONTEXT_FAILED || | 
| 192 state == PA_CONTEXT_TERMINATED) { | 191 state == PA_CONTEXT_TERMINATED) { | 
| 193 // Connection process has reached a terminal state. Wake PulseAudioInit(). | 192 // Connection process has reached a terminal state. Wake PulseAudioInit(). | 
| 194 *connect_done = true; | 193 *connect_done = true; | 
| 195 MainloopSignal(); | 194 MainloopSignal(); | 
| 196 } | 195 } | 
| 197 } | 196 } | 
| 198 | 197 | 
| 199 bool PulseAudioMixer::PulseAudioInit() { | 198 bool AudioMixerPulse::PulseAudioInit() { | 
| 200 pa_context_state_t state = PA_CONTEXT_FAILED; | 199 pa_context_state_t state = PA_CONTEXT_FAILED; | 
| 201 | 200 | 
| 202 { | 201 { | 
| 203 AutoLock lock(mixer_state_lock_); | 202 AutoLock lock(mixer_state_lock_); | 
| 204 if (mixer_state_ != INITIALIZING) | 203 if (mixer_state_ != INITIALIZING) | 
| 205 return false; | 204 return false; | 
| 206 | 205 | 
| 207 pa_mainloop_ = pa_threaded_mainloop_new(); | 206 pa_mainloop_ = pa_threaded_mainloop_new(); | 
| 208 if (!pa_mainloop_) { | 207 if (!pa_mainloop_) { | 
| 209 LOG(ERROR) << "Can't create PulseAudio mainloop"; | 208 LOG(ERROR) << "Can't create PulseAudio mainloop"; | 
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 291 } | 290 } | 
| 292 | 291 | 
| 293 return true; | 292 return true; | 
| 294 } | 293 } | 
| 295 | 294 | 
| 296 // Failed startup sequence, clean up now. | 295 // Failed startup sequence, clean up now. | 
| 297 PulseAudioFree(); | 296 PulseAudioFree(); | 
| 298 return false; | 297 return false; | 
| 299 } | 298 } | 
| 300 | 299 | 
| 301 void PulseAudioMixer::PulseAudioFree() { | 300 void AudioMixerPulse::PulseAudioFree() { | 
| 302 { | 301 { | 
| 303 AutoLock lock(mixer_state_lock_); | 302 AutoLock lock(mixer_state_lock_); | 
| 304 if (!pa_mainloop_) | 303 if (!pa_mainloop_) | 
| 305 mixer_state_ = UNINITIALIZED; | 304 mixer_state_ = UNINITIALIZED; | 
| 306 if ((mixer_state_ == UNINITIALIZED) || (mixer_state_ == SHUTTING_DOWN)) | 305 if ((mixer_state_ == UNINITIALIZED) || (mixer_state_ == SHUTTING_DOWN)) | 
| 307 return; | 306 return; | 
| 307 | |
| 308 // If still initializing on another thread, this will cause it to exit. | 308 // If still initializing on another thread, this will cause it to exit. | 
| 309 mixer_state_ = SHUTTING_DOWN; | 309 mixer_state_ = SHUTTING_DOWN; | 
| 310 } | 310 } | 
| 311 | 311 | 
| 312 DCHECK(pa_mainloop_); | 312 DCHECK(pa_mainloop_); | 
| 313 | 313 | 
| 314 MainloopLock(); | 314 MainloopLock(); | 
| 315 if (pa_context_) { | 315 if (pa_context_) { | 
| 316 pa_context_disconnect(pa_context_); | 316 pa_context_disconnect(pa_context_); | 
| 317 pa_context_unref(pa_context_); | 317 pa_context_unref(pa_context_); | 
| 318 pa_context_ = NULL; | 318 pa_context_ = NULL; | 
| 319 } | 319 } | 
| 320 MainloopUnlock(); | 320 MainloopUnlock(); | 
| 321 | 321 | 
| 322 pa_threaded_mainloop_stop(pa_mainloop_); | 322 pa_threaded_mainloop_stop(pa_mainloop_); | 
| 323 pa_threaded_mainloop_free(pa_mainloop_); | 323 pa_threaded_mainloop_free(pa_mainloop_); | 
| 324 pa_mainloop_ = NULL; | 324 pa_mainloop_ = NULL; | 
| 325 | 325 | 
| 326 { | 326 { | 
| 327 AutoLock lock(mixer_state_lock_); | 327 AutoLock lock(mixer_state_lock_); | 
| 328 mixer_state_ = UNINITIALIZED; | 328 mixer_state_ = UNINITIALIZED; | 
| 329 } | 329 } | 
| 330 } | 330 } | 
| 331 | 331 | 
| 332 void PulseAudioMixer::CompleteOperation(pa_operation* pa_op, | 332 void AudioMixerPulse::CompleteOperation(pa_operation* pa_op, | 
| 333 bool* done) const { | 333 bool* done) const { | 
| 334 // After starting any operation, this helper checks if it started OK, then | 334 // After starting any operation, this helper checks if it started OK, then | 
| 335 // waits for it to complete by iterating through the mainloop until the | 335 // waits for it to complete by iterating through the mainloop until the | 
| 336 // operation is not running anymore. | 336 // operation is not running anymore. | 
| 337 CHECK(pa_op); | 337 CHECK(pa_op); | 
| 338 | 338 | 
| 339 while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { | 339 while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { | 
| 340 // If operation still running, but we got what we needed, cancel it now. | 340 // If operation still running, but we got what we needed, cancel it now. | 
| 341 if (*done) { | 341 if (*done) { | 
| 342 pa_operation_cancel(pa_op); | 342 pa_operation_cancel(pa_op); | 
| 343 break; | 343 break; | 
| 344 } | 344 } | 
| 345 MainloopWait(); | 345 MainloopWait(); | 
| 346 } | 346 } | 
| 347 pa_operation_unref(pa_op); | 347 pa_operation_unref(pa_op); | 
| 348 } | 348 } | 
| 349 | 349 | 
| 350 // Must be called with mainloop lock held | 350 // Must be called with mainloop lock held | 
| 351 void PulseAudioMixer::GetDefaultPlaybackDevice() { | 351 void AudioMixerPulse::GetDefaultPlaybackDevice() { | 
| 352 DCHECK_GT(mainloop_lock_count_, 0); | 352 DCHECK_GT(mainloop_lock_count_, 0); | 
| 353 DCHECK(pa_context_); | 353 DCHECK(pa_context_); | 
| 354 DCHECK(pa_context_get_state(pa_context_) == PA_CONTEXT_READY); | 354 DCHECK(pa_context_get_state(pa_context_) == PA_CONTEXT_READY); | 
| 355 | 355 | 
| 356 CallbackWrapper data = {this, false, NULL}; | 356 CallbackWrapper data = {this, false, NULL}; | 
| 357 | 357 | 
| 358 pa_operation* pa_op = pa_context_get_sink_info_list(pa_context_, | 358 pa_operation* pa_op = pa_context_get_sink_info_list(pa_context_, | 
| 359 EnumerateDevicesCallback, | 359 EnumerateDevicesCallback, | 
| 360 &data); | 360 &data); | 
| 361 CompleteOperation(pa_op, &data.done); | 361 CompleteOperation(pa_op, &data.done); | 
| 362 return; | 362 return; | 
| 363 } | 363 } | 
| 364 | 364 | 
| 365 void PulseAudioMixer::OnEnumerateDevices(const pa_sink_info* sink_info, | 365 void AudioMixerPulse::OnEnumerateDevices(const pa_sink_info* sink_info, | 
| 366 int eol, bool* done) { | 366 int eol, bool* done) { | 
| 367 if (device_id_ != kInvalidDeviceId) | 367 if (device_id_ != kInvalidDeviceId) | 
| 368 return; | 368 return; | 
| 369 | 369 | 
| 370 // TODO(davej): Should we handle cases of more than one output sink device? | 370 // TODO(davej): Should we handle cases of more than one output sink device? | 
| 371 | 371 | 
| 372 // eol is < 0 for error, > 0 for end of list, ==0 while listing. | 372 // eol is < 0 for error, > 0 for end of list, ==0 while listing. | 
| 373 if (eol == 0) { | 373 if (eol == 0) { | 
| 374 device_id_ = sink_info->index; | 374 device_id_ = sink_info->index; | 
| 375 } | 375 } | 
| 376 *done = true; | 376 *done = true; | 
| 377 MainloopSignal(); | 377 MainloopSignal(); | 
| 378 } | 378 } | 
| 379 | 379 | 
| 380 // static | 380 // static | 
| 381 void PulseAudioMixer::EnumerateDevicesCallback(pa_context* unused, | 381 void AudioMixerPulse::EnumerateDevicesCallback(pa_context* unused, | 
| 382 const pa_sink_info* sink_info, | 382 const pa_sink_info* sink_info, | 
| 383 int eol, | 383 int eol, | 
| 384 void* userdata) { | 384 void* userdata) { | 
| 385 CallbackWrapper* data = | 385 CallbackWrapper* data = | 
| 386 static_cast<CallbackWrapper*>(userdata); | 386 static_cast<CallbackWrapper*>(userdata); | 
| 387 data->instance->OnEnumerateDevices(sink_info, eol, &data->done); | 387 data->instance->OnEnumerateDevices(sink_info, eol, &data->done); | 
| 388 } | 388 } | 
| 389 | 389 | 
| 390 // Must be called with lock held | 390 // Must be called with lock held | 
| 391 void PulseAudioMixer::GetAudioInfo(AudioInfo* info) const { | 391 void AudioMixerPulse::GetAudioInfo(AudioInfo* info) const { | 
| 392 DCHECK_GT(mainloop_lock_count_, 0); | 392 DCHECK_GT(mainloop_lock_count_, 0); | 
| 393 CallbackWrapper data = {const_cast<PulseAudioMixer*>(this), false, info}; | 393 CallbackWrapper data = {const_cast<AudioMixerPulse*>(this), false, info}; | 
| 394 pa_operation* pa_op = pa_context_get_sink_info_by_index(pa_context_, | 394 pa_operation* pa_op = pa_context_get_sink_info_by_index(pa_context_, | 
| 395 device_id_, | 395 device_id_, | 
| 396 GetAudioInfoCallback, | 396 GetAudioInfoCallback, | 
| 397 &data); | 397 &data); | 
| 398 CompleteOperation(pa_op, &data.done); | 398 CompleteOperation(pa_op, &data.done); | 
| 399 } | 399 } | 
| 400 | 400 | 
| 401 // static | 401 // static | 
| 402 void PulseAudioMixer::GetAudioInfoCallback(pa_context* unused, | 402 void AudioMixerPulse::GetAudioInfoCallback(pa_context* unused, | 
| 403 const pa_sink_info* sink_info, | 403 const pa_sink_info* sink_info, | 
| 404 int eol, | 404 int eol, | 
| 405 void* userdata) { | 405 void* userdata) { | 
| 406 CallbackWrapper* data = static_cast<CallbackWrapper*>(userdata); | 406 CallbackWrapper* data = static_cast<CallbackWrapper*>(userdata); | 
| 407 AudioInfo* info = static_cast<AudioInfo*>(data->userdata); | 407 AudioInfo* info = static_cast<AudioInfo*>(data->userdata); | 
| 408 | 408 | 
| 409 // Copy just the information we care about. | 409 // Copy just the information we care about. | 
| 410 if (eol == 0) { | 410 if (eol == 0) { | 
| 411 info->cvolume = sink_info->volume; | 411 info->cvolume = sink_info->volume; | 
| 412 info->muted = sink_info->mute ? true : false; | 412 info->muted = sink_info->mute ? true : false; | 
| 413 data->done = true; | 413 data->done = true; | 
| 414 } | 414 } | 
| 415 data->instance->MainloopSignal(); | 415 data->instance->MainloopSignal(); | 
| 416 } | 416 } | 
| 417 | 417 | 
| 418 inline void PulseAudioMixer::MainloopLock() const { | 418 inline void AudioMixerPulse::MainloopLock() const { | 
| 419 pa_threaded_mainloop_lock(pa_mainloop_); | 419 pa_threaded_mainloop_lock(pa_mainloop_); | 
| 420 ++mainloop_lock_count_; | 420 ++mainloop_lock_count_; | 
| 421 } | 421 } | 
| 422 | 422 | 
| 423 inline void PulseAudioMixer::MainloopUnlock() const { | 423 inline void AudioMixerPulse::MainloopUnlock() const { | 
| 424 --mainloop_lock_count_; | 424 --mainloop_lock_count_; | 
| 425 pa_threaded_mainloop_unlock(pa_mainloop_); | 425 pa_threaded_mainloop_unlock(pa_mainloop_); | 
| 426 } | 426 } | 
| 427 | 427 | 
| 428 // Must be called with the lock held. | 428 // Must be called with the lock held. | 
| 429 inline void PulseAudioMixer::MainloopWait() const { | 429 inline void AudioMixerPulse::MainloopWait() const { | 
| 430 DCHECK_GT(mainloop_lock_count_, 0); | 430 DCHECK_GT(mainloop_lock_count_, 0); | 
| 431 pa_threaded_mainloop_wait(pa_mainloop_); | 431 pa_threaded_mainloop_wait(pa_mainloop_); | 
| 432 } | 432 } | 
| 433 | 433 | 
| 434 // Must be called with the lock held. | 434 // Must be called with the lock held. | 
| 435 inline void PulseAudioMixer::MainloopSignal() const { | 435 inline void AudioMixerPulse::MainloopSignal() const { | 
| 436 DCHECK_GT(mainloop_lock_count_, 0); | 436 DCHECK_GT(mainloop_lock_count_, 0); | 
| 437 pa_threaded_mainloop_signal(pa_mainloop_, 0); | 437 pa_threaded_mainloop_signal(pa_mainloop_, 0); | 
| 438 } | 438 } | 
| 439 | 439 | 
| 440 inline bool PulseAudioMixer::MainloopSafeLock() const { | 440 inline bool AudioMixerPulse::MainloopSafeLock() const { | 
| 441 AutoLock lock(mixer_state_lock_); | 441 AutoLock lock(mixer_state_lock_); | 
| 442 if ((mixer_state_ == SHUTTING_DOWN) || (!pa_mainloop_)) | 442 if ((mixer_state_ == SHUTTING_DOWN) || (!pa_mainloop_)) | 
| 443 return false; | 443 return false; | 
| 444 | |
| 444 pa_threaded_mainloop_lock(pa_mainloop_); | 445 pa_threaded_mainloop_lock(pa_mainloop_); | 
| 445 ++mainloop_lock_count_; | 446 ++mainloop_lock_count_; | 
| 446 return true; | 447 return true; | 
| 447 } | 448 } | 
| 448 | 449 | 
| 449 inline bool PulseAudioMixer::MainloopLockIfReady() const { | 450 inline bool AudioMixerPulse::MainloopLockIfReady() const { | 
| 450 AutoLock lock(mixer_state_lock_); | 451 AutoLock lock(mixer_state_lock_); | 
| 451 if (mixer_state_ != READY) | 452 if (mixer_state_ != READY) | 
| 452 return false; | 453 return false; | 
| 453 if (!pa_mainloop_) | 454 if (!pa_mainloop_) | 
| 454 return false; | 455 return false; | 
| 455 pa_threaded_mainloop_lock(pa_mainloop_); | 456 pa_threaded_mainloop_lock(pa_mainloop_); | 
| 456 ++mainloop_lock_count_; | 457 ++mainloop_lock_count_; | 
| 457 return true; | 458 return true; | 
| 458 } | 459 } | 
| 459 | 460 | 
| 460 } // namespace chromeos | 461 } // namespace chromeos | 
| 461 | 462 | 
| OLD | NEW |