OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/audio/pulse/pulse_util.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "media/audio/audio_manager_base.h" |
| 9 |
| 10 namespace media { |
| 11 |
| 12 namespace { |
| 13 |
| 14 pa_channel_position ChromiumToPAChannelPosition(Channels channel) { |
| 15 switch (channel) { |
| 16 // PulseAudio does not differentiate between left/right and |
| 17 // stereo-left/stereo-right, both translate to front-left/front-right. |
| 18 case LEFT: |
| 19 return PA_CHANNEL_POSITION_FRONT_LEFT; |
| 20 case RIGHT: |
| 21 return PA_CHANNEL_POSITION_FRONT_RIGHT; |
| 22 case CENTER: |
| 23 return PA_CHANNEL_POSITION_FRONT_CENTER; |
| 24 case LFE: |
| 25 return PA_CHANNEL_POSITION_LFE; |
| 26 case BACK_LEFT: |
| 27 return PA_CHANNEL_POSITION_REAR_LEFT; |
| 28 case BACK_RIGHT: |
| 29 return PA_CHANNEL_POSITION_REAR_RIGHT; |
| 30 case LEFT_OF_CENTER: |
| 31 return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; |
| 32 case RIGHT_OF_CENTER: |
| 33 return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; |
| 34 case BACK_CENTER: |
| 35 return PA_CHANNEL_POSITION_REAR_CENTER; |
| 36 case SIDE_LEFT: |
| 37 return PA_CHANNEL_POSITION_SIDE_LEFT; |
| 38 case SIDE_RIGHT: |
| 39 return PA_CHANNEL_POSITION_SIDE_RIGHT; |
| 40 case CHANNELS_MAX: |
| 41 return PA_CHANNEL_POSITION_INVALID; |
| 42 default: |
| 43 NOTREACHED() << "Invalid channel: " << channel; |
| 44 return PA_CHANNEL_POSITION_INVALID; |
| 45 } |
| 46 } |
| 47 |
| 48 } // namespace |
| 49 |
| 50 PulseInputObject::PulseInputObject() |
| 51 : pa_mainloop_(NULL), |
| 52 pa_context_(NULL), |
| 53 devices(NULL), |
| 54 sample_rate_(0), |
| 55 initialized_(false) { |
| 56 Initialize(); |
| 57 } |
| 58 |
| 59 PulseInputObject::~PulseInputObject() { |
| 60 Terminate(); |
| 61 } |
| 62 |
| 63 void PulseInputObject::GetInputDevices(AudioDeviceNames* device_names) { |
| 64 DCHECK(pa_mainloop_); |
| 65 DCHECK(pa_context_); |
| 66 devices = device_names; |
| 67 AutoPulseLock auto_lock(pa_mainloop_); |
| 68 pa_operation* operation = pa_context_get_source_info_list( |
| 69 pa_context_, DevicesInfoCallback, this); |
| 70 WaitForOperationCompletion(pa_mainloop_, operation); |
| 71 |
| 72 if (!device_names->empty()) { |
| 73 device_names->push_front( |
| 74 AudioDeviceName(AudioManagerBase::kDefaultDeviceName, |
| 75 AudioManagerBase::kDefaultDeviceId)); |
| 76 } |
| 77 } |
| 78 |
| 79 int PulseInputObject::GetNativeSampleRate() { |
| 80 DCHECK(pa_mainloop_); |
| 81 DCHECK(pa_context_); |
| 82 AutoPulseLock auto_lock(pa_mainloop_); |
| 83 pa_operation* operation = pa_context_get_server_info( |
| 84 pa_context_, SamplerateInfoCallback, this); |
| 85 WaitForOperationCompletion(pa_mainloop_, operation); |
| 86 |
| 87 return sample_rate_; |
| 88 } |
| 89 |
| 90 void PulseInputObject::Initialize() { |
| 91 DCHECK(!initialized_); |
| 92 DCHECK(!pa_mainloop_); |
| 93 |
| 94 // Create a mainloop API and connect to the default server. |
| 95 // The mainloop is the internal asynchronous API event loop. |
| 96 pa_mainloop_ = pa_threaded_mainloop_new(); |
| 97 if (!pa_mainloop_) |
| 98 return; |
| 99 |
| 100 // Start the threaded mainloop. |
| 101 if (pa_threaded_mainloop_start(pa_mainloop_)) { |
| 102 Terminate(); |
| 103 return; |
| 104 } |
| 105 |
| 106 // Lock the event loop object, effectively blocking the event loop thread |
| 107 // from processing events. This is necessary. |
| 108 AutoPulseLock auto_lock(pa_mainloop_); |
| 109 |
| 110 pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_); |
| 111 pa_context_ = pa_context_new(pa_mainloop_api, "Chrome input"); |
| 112 DCHECK(pa_context_) << "Failed to create PA context"; |
| 113 if (!pa_context_) { |
| 114 return; |
| 115 } |
| 116 |
| 117 pa_context_set_state_callback(pa_context_, &ContextStateCallback, this); |
| 118 if (pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) { |
| 119 DLOG(ERROR) << "Failed to connect to the context"; |
| 120 return; |
| 121 } |
| 122 |
| 123 // Wait until |pa_context_| is ready. pa_threaded_mainloop_wait() must be |
| 124 // called after pa_context_get_state() in case the context is already ready, |
| 125 // otherwise pa_threaded_mainloop_wait() will hang indefinitely. |
| 126 while (true) { |
| 127 pa_context_state_t context_state = pa_context_get_state(pa_context_); |
| 128 if (!PA_CONTEXT_IS_GOOD(context_state)) { |
| 129 return; |
| 130 } |
| 131 if (context_state == PA_CONTEXT_READY) |
| 132 break; |
| 133 pa_threaded_mainloop_wait(pa_mainloop_); |
| 134 } |
| 135 |
| 136 initialized_ = true; |
| 137 } |
| 138 |
| 139 void PulseInputObject::Terminate() { |
| 140 if (!pa_mainloop_) { |
| 141 DCHECK(!pa_context_); |
| 142 return; |
| 143 } |
| 144 |
| 145 { |
| 146 AutoPulseLock auto_lock(pa_mainloop_); |
| 147 if (pa_context_) { |
| 148 // Clear our state callback. |
| 149 pa_context_set_state_callback(pa_context_, NULL, NULL); |
| 150 pa_context_disconnect(pa_context_); |
| 151 pa_context_unref(pa_context_); |
| 152 pa_context_ = NULL; |
| 153 } |
| 154 } |
| 155 |
| 156 pa_threaded_mainloop_stop(pa_mainloop_); |
| 157 pa_threaded_mainloop_free(pa_mainloop_); |
| 158 pa_mainloop_ = NULL; |
| 159 initialized_ = false; |
| 160 } |
| 161 |
| 162 void PulseInputObject::ContextStateCallback(pa_context* context, |
| 163 void* user_data) { |
| 164 PulseInputObject* pulse = reinterpret_cast<PulseInputObject*>(user_data); |
| 165 pa_threaded_mainloop_signal(pulse->mainloop(), 0); |
| 166 } |
| 167 |
| 168 void PulseInputObject::DevicesInfoCallback(pa_context* context, |
| 169 const pa_source_info* info, |
| 170 int error, void *user_data) { |
| 171 PulseInputObject* pulse = reinterpret_cast<PulseInputObject*>(user_data); |
| 172 |
| 173 if (error) { |
| 174 // Signal the pulse object that it is done. |
| 175 pa_threaded_mainloop_signal(pulse->mainloop(), 0); |
| 176 return; |
| 177 } |
| 178 |
| 179 // Exclude the output devices. |
| 180 if (info->monitor_of_sink == PA_INVALID_INDEX) { |
| 181 pulse->devices->push_back(media::AudioDeviceName(info->description, |
| 182 info->name)); |
| 183 } |
| 184 } |
| 185 |
| 186 void PulseInputObject::SamplerateInfoCallback(pa_context* context, |
| 187 const pa_server_info* info, |
| 188 void* user_data) { |
| 189 PulseInputObject* pulse = reinterpret_cast<PulseInputObject*>(user_data); |
| 190 |
| 191 pulse->sample_rate_ = info->sample_spec.rate; |
| 192 pa_threaded_mainloop_signal(pulse->mainloop(), 0); |
| 193 } |
| 194 |
| 195 pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) { |
| 196 switch (bits_per_sample) { |
| 197 case 8: |
| 198 return PA_SAMPLE_U8; |
| 199 case 16: |
| 200 return PA_SAMPLE_S16LE; |
| 201 case 24: |
| 202 return PA_SAMPLE_S24LE; |
| 203 case 32: |
| 204 return PA_SAMPLE_S32LE; |
| 205 default: |
| 206 NOTREACHED() << "Invalid bits per sample: " << bits_per_sample; |
| 207 return PA_SAMPLE_INVALID; |
| 208 } |
| 209 } |
| 210 |
| 211 // pa_stream_success_cb_t |
| 212 void StreamSuccessCallback(pa_stream* s, int error, void* mainloop) { |
| 213 pa_threaded_mainloop* pa_mainloop = |
| 214 static_cast<pa_threaded_mainloop*>(mainloop); |
| 215 pa_threaded_mainloop_signal(pa_mainloop, 0); |
| 216 } |
| 217 |
| 218 pa_channel_map ChannelLayoutToPAChannelMap(ChannelLayout channel_layout) { |
| 219 pa_channel_map channel_map; |
| 220 pa_channel_map_init(&channel_map); |
| 221 |
| 222 channel_map.channels = ChannelLayoutToChannelCount(channel_layout); |
| 223 for (Channels ch = LEFT; ch < CHANNELS_MAX; |
| 224 ch = static_cast<Channels>(ch + 1)) { |
| 225 int channel_index = ChannelOrder(channel_layout, ch); |
| 226 if (channel_index < 0) |
| 227 continue; |
| 228 |
| 229 channel_map.map[channel_index] = ChromiumToPAChannelPosition(ch); |
| 230 } |
| 231 |
| 232 return channel_map; |
| 233 } |
| 234 |
| 235 void WaitForOperationCompletion(pa_threaded_mainloop* pa_mainloop, |
| 236 pa_operation* operation) { |
| 237 if (!operation) { |
| 238 DLOG(WARNING) << "Operation is NULL"; |
| 239 return; |
| 240 } |
| 241 |
| 242 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) |
| 243 pa_threaded_mainloop_wait(pa_mainloop); |
| 244 |
| 245 pa_operation_unref(operation); |
| 246 } |
| 247 |
| 248 } // namespace media |
OLD | NEW |