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      &current_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