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/linux/alsa_input.h" 6 7 #include "base/basictypes.h" 8 #include "base/bind.h" 9 #include "base/logging.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/time/time.h" 12 #include "media/audio/audio_manager.h" 13 #include "media/audio/linux/alsa_output.h" 14 #include "media/audio/linux/alsa_util.h" 15 #include "media/audio/linux/alsa_wrapper.h" 16 #include "media/audio/linux/audio_manager_linux.h" 17 18 namespace media { 19 20 static const int kNumPacketsInRingBuffer = 3; 21 22 static const char kDefaultDevice1[] = "default"; 23 static const char kDefaultDevice2[] = "plug:default"; 24 25 const char AlsaPcmInputStream::kAutoSelectDevice[] = ""; 26 27 AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerLinux* audio_manager, 28 const std::string& device_name, 29 const AudioParameters& params, 30 AlsaWrapper* wrapper) 31 : audio_manager_(audio_manager), 32 device_name_(device_name), 33 params_(params), 34 bytes_per_buffer_(params.frames_per_buffer() * 35 (params.channels() * params.bits_per_sample()) / 8), 36 wrapper_(wrapper), 37 buffer_duration_(base::TimeDelta::FromMicroseconds( 38 params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / 39 static_cast<float>(params.sample_rate()))), 40 callback_(NULL), 41 device_handle_(NULL), 42 mixer_handle_(NULL), 43 mixer_element_handle_(NULL), 44 weak_factory_(this), 45 read_callback_behind_schedule_(false) { 46 } 47 48 AlsaPcmInputStream::~AlsaPcmInputStream() {} 49 50 bool AlsaPcmInputStream::Open() { 51 if (device_handle_) 52 return false; // Already open. 53 54 snd_pcm_format_t pcm_format = alsa_util::BitsToFormat( 55 params_.bits_per_sample()); 56 if (pcm_format == SND_PCM_FORMAT_UNKNOWN) { 57 LOG(WARNING) << "Unsupported bits per sample: " 58 << params_.bits_per_sample(); 59 return false; 60 } 61 62 uint32 latency_us = 63 buffer_duration_.InMicroseconds() * kNumPacketsInRingBuffer; 64 65 // Use the same minimum required latency as output. 66 latency_us = std::max(latency_us, AlsaPcmOutputStream::kMinLatencyMicros); 67 68 if (device_name_ == kAutoSelectDevice) { 69 const char* device_names[] = { kDefaultDevice1, kDefaultDevice2 }; 70 for (size_t i = 0; i < arraysize(device_names); ++i) { 71 device_handle_ = alsa_util::OpenCaptureDevice( 72 wrapper_, device_names[i], params_.channels(), 73 params_.sample_rate(), pcm_format, latency_us); 74 75 if (device_handle_) { 76 device_name_ = device_names[i]; 77 break; 78 } 79 } 80 } else { 81 device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, 82 device_name_.c_str(), 83 params_.channels(), 84 params_.sample_rate(), 85 pcm_format, latency_us); 86 } 87 88 if (device_handle_) { 89 audio_buffer_.reset(new uint8[bytes_per_buffer_]); 90 91 // Open the microphone mixer. 92 mixer_handle_ = alsa_util::OpenMixer(wrapper_, device_name_); 93 if (mixer_handle_) { 94 mixer_element_handle_ = alsa_util::LoadCaptureMixerElement( 95 wrapper_, mixer_handle_); 96 } 97 } 98 99 return device_handle_ != NULL; 100 } 101 102 void AlsaPcmInputStream::Start(AudioInputCallback* callback) { 103 DCHECK(!callback_ && callback); 104 callback_ = callback; 105 StartAgc(); 106 int error = wrapper_->PcmPrepare(device_handle_); 107 if (error < 0) { 108 HandleError("PcmPrepare", error); 109 } else { 110 error = wrapper_->PcmStart(device_handle_); 111 if (error < 0) 112 HandleError("PcmStart", error); 113 } 114 115 if (error < 0) { 116 callback_ = NULL; 117 } else { 118 // We start reading data half |buffer_duration_| later than when the 119 // buffer might have got filled, to accommodate some delays in the audio 120 // driver. This could also give us a smooth read sequence going forward. 121 base::TimeDelta delay = buffer_duration_ + buffer_duration_ / 2; 122 next_read_time_ = base::TimeTicks::Now() + delay; 123 base::MessageLoop::current()->PostDelayedTask( 124 FROM_HERE, 125 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), 126 delay); 127 } 128 } 129 130 bool AlsaPcmInputStream::Recover(int original_error) { 131 int error = wrapper_->PcmRecover(device_handle_, original_error, 1); 132 if (error < 0) { 133 // Docs say snd_pcm_recover returns the original error if it is not one 134 // of the recoverable ones, so this log message will probably contain the 135 // same error twice. 136 LOG(WARNING) << "Unable to recover from \"" 137 << wrapper_->StrError(original_error) << "\": " 138 << wrapper_->StrError(error); 139 return false; 140 } 141 142 if (original_error == -EPIPE) { // Buffer underrun/overrun. 143 // For capture streams we have to repeat the explicit start() to get 144 // data flowing again. 145 error = wrapper_->PcmStart(device_handle_); 146 if (error < 0) { 147 HandleError("PcmStart", error); 148 return false; 149 } 150 } 151 152 return true; 153 } 154 155 snd_pcm_sframes_t AlsaPcmInputStream::GetCurrentDelay() { 156 snd_pcm_sframes_t delay = -1; 157 158 int error = wrapper_->PcmDelay(device_handle_, &delay); 159 if (error < 0) 160 Recover(error); 161 162 // snd_pcm_delay() may not work in the beginning of the stream. In this case 163 // return delay of data we know currently is in the ALSA's buffer. 164 if (delay < 0) 165 delay = wrapper_->PcmAvailUpdate(device_handle_); 166 167 return delay; 168 } 169 170 void AlsaPcmInputStream::ReadAudio() { 171 DCHECK(callback_); 172 173 snd_pcm_sframes_t frames = wrapper_->PcmAvailUpdate(device_handle_); 174 if (frames < 0) { // Potentially recoverable error? 175 LOG(WARNING) << "PcmAvailUpdate(): " << wrapper_->StrError(frames); 176 Recover(frames); 177 } 178 179 if (frames < params_.frames_per_buffer()) { 180 // Not enough data yet or error happened. In both cases wait for a very 181 // small duration before checking again. 182 // Even Though read callback was behind schedule, there is no data, so 183 // reset the next_read_time_. 184 if (read_callback_behind_schedule_) { 185 next_read_time_ = base::TimeTicks::Now(); 186 read_callback_behind_schedule_ = false; 187 } 188 189 base::TimeDelta next_check_time = buffer_duration_ / 2; 190 base::MessageLoop::current()->PostDelayedTask( 191 FROM_HERE, 192 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), 193 next_check_time); 194 return; 195 } 196 197 int num_buffers = frames / params_.frames_per_buffer(); 198 uint32 hardware_delay_bytes = 199 static_cast<uint32>(GetCurrentDelay() * params_.GetBytesPerFrame()); 200 double normalized_volume = 0.0; 201 202 // Update the AGC volume level once every second. Note that, |volume| is 203 // also updated each time SetVolume() is called through IPC by the 204 // render-side AGC. 205 GetAgcVolume(&normalized_volume); 206 207 while (num_buffers--) { 208 int frames_read = wrapper_->PcmReadi(device_handle_, audio_buffer_.get(), 209 params_.frames_per_buffer()); 210 if (frames_read == params_.frames_per_buffer()) { 211 callback_->OnData(this, audio_buffer_.get(), bytes_per_buffer_, 212 hardware_delay_bytes, normalized_volume); 213 } else { 214 LOG(WARNING) << "PcmReadi returning less than expected frames: " 215 << frames_read << " vs. " << params_.frames_per_buffer() 216 << ". Dropping this buffer."; 217 } 218 } 219 220 next_read_time_ += buffer_duration_; 221 base::TimeDelta delay = next_read_time_ - base::TimeTicks::Now(); 222 if (delay < base::TimeDelta()) { 223 DVLOG(1) << "Audio read callback behind schedule by " 224 << (buffer_duration_ - delay).InMicroseconds() 225 << " (us)."; 226 // Read callback is behind schedule. Assuming there is data pending in 227 // the soundcard, invoke the read callback immediate in order to catch up. 228 read_callback_behind_schedule_ = true; 229 delay = base::TimeDelta(); 230 } 231 232 base::MessageLoop::current()->PostDelayedTask( 233 FROM_HERE, 234 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), 235 delay); 236 } 237 238 void AlsaPcmInputStream::Stop() { 239 if (!device_handle_ || !callback_) 240 return; 241 242 StopAgc(); 243 244 weak_factory_.InvalidateWeakPtrs(); // Cancel the next scheduled read. 245 int error = wrapper_->PcmDrop(device_handle_); 246 if (error < 0) 247 HandleError("PcmDrop", error); 248 } 249 250 void AlsaPcmInputStream::Close() { 251 if (device_handle_) { 252 weak_factory_.InvalidateWeakPtrs(); // Cancel the next scheduled read. 253 int error = alsa_util::CloseDevice(wrapper_, device_handle_); 254 if (error < 0) 255 HandleError("PcmClose", error); 256 257 if (mixer_handle_) 258 alsa_util::CloseMixer(wrapper_, mixer_handle_, device_name_); 259 260 audio_buffer_.reset(); 261 device_handle_ = NULL; 262 mixer_handle_ = NULL; 263 mixer_element_handle_ = NULL; 264 265 if (callback_) 266 callback_->OnClose(this); 267 } 268 269 audio_manager_->ReleaseInputStream(this); 270 } 271 272 double AlsaPcmInputStream::GetMaxVolume() { 273 if (!mixer_handle_ || !mixer_element_handle_) { 274 DLOG(WARNING) << "GetMaxVolume is not supported for " << device_name_; 275 return 0.0; 276 } 277 278 if (!wrapper_->MixerSelemHasCaptureVolume(mixer_element_handle_)) { 279 DLOG(WARNING) << "Unsupported microphone volume for " << device_name_; 280 return 0.0; 281 } 282 283 long min = 0; 284 long max = 0; 285 if (wrapper_->MixerSelemGetCaptureVolumeRange(mixer_element_handle_, 286 &min, 287 &max)) { 288 DLOG(WARNING) << "Unsupported max microphone volume for " << device_name_; 289 return 0.0; 290 } 291 DCHECK(min == 0); 292 DCHECK(max > 0); 293 294 return static_cast<double>(max); 295 } 296 297 void AlsaPcmInputStream::SetVolume(double volume) { 298 if (!mixer_handle_ || !mixer_element_handle_) { 299 DLOG(WARNING) << "SetVolume is not supported for " << device_name_; 300 return; 301 } 302 303 int error = wrapper_->MixerSelemSetCaptureVolumeAll( 304 mixer_element_handle_, static_cast<long>(volume)); 305 if (error < 0) { 306 DLOG(WARNING) << "Unable to set volume for " << device_name_; 307 } 308 309 // Update the AGC volume level based on the last setting above. Note that, 310 // the volume-level resolution is not infinite and it is therefore not 311 // possible to assume that the volume provided as input parameter can be 312 // used directly. Instead, a new query to the audio hardware is required. 313 // This method does nothing if AGC is disabled. 314 UpdateAgcVolume(); 315 } 316 317 double AlsaPcmInputStream::GetVolume() { 318 if (!mixer_handle_ || !mixer_element_handle_) { 319 DLOG(WARNING) << "GetVolume is not supported for " << device_name_; 320 return 0.0; 321 } 322 323 long current_volume = 0; 324 int error = wrapper_->MixerSelemGetCaptureVolume( 325 mixer_element_handle_, static_cast<snd_mixer_selem_channel_id_t>(0), 326 ¤t_volume); 327 if (error < 0) { 328 DLOG(WARNING) << "Unable to get volume for " << device_name_; 329 return 0.0; 330 } 331 332 return static_cast<double>(current_volume); 333 } 334 335 void AlsaPcmInputStream::HandleError(const char* method, int error) { 336 LOG(WARNING) << method << ": " << wrapper_->StrError(error); 337 callback_->OnError(this); 338 } 339 340 } // namespace media 341