Home | History | Annotate | Download | only in linux
      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       &current_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