alsa_input.cc revision f2477e01787aa58f445919b809d89e252beef54f
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()) / 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
48AlsaPcmInputStream::~AlsaPcmInputStream() {}
49
50bool 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
102void 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
130bool 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
155snd_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
170void 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
238void 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
250void 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
272double 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
297void 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
317double 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
335void AlsaPcmInputStream::HandleError(const char* method, int error) {
336  LOG(WARNING) << method << ": " << wrapper_->StrError(error);
337  callback_->OnError(this);
338}
339
340}  // namespace media
341