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