cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / media / audio / linux / alsa_input.cc
blob929cbe79063dcc9ab1c7ff754d7f80806a5639d2
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.
5 #include "media/audio/linux/alsa_input.h"
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"
18 namespace media {
20 static const int kNumPacketsInRingBuffer = 3;
22 static const char kDefaultDevice1[] = "default";
23 static const char kDefaultDevice2[] = "plug:default";
25 const char AlsaPcmInputStream::kAutoSelectDevice[] = "";
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) {
48 AlsaPcmInputStream::~AlsaPcmInputStream() {}
50 bool AlsaPcmInputStream::Open() {
51 if (device_handle_)
52 return false; // Already open.
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;
62 uint32 latency_us =
63 buffer_duration_.InMicroseconds() * kNumPacketsInRingBuffer;
65 // Use the same minimum required latency as output.
66 latency_us = std::max(latency_us, AlsaPcmOutputStream::kMinLatencyMicros);
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);
75 if (device_handle_) {
76 device_name_ = device_names[i];
77 break;
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);
88 if (device_handle_) {
89 audio_buffer_.reset(new uint8[bytes_per_buffer_]);
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_);
99 return device_handle_ != NULL;
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);
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);
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;
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;
152 return true;
155 snd_pcm_sframes_t AlsaPcmInputStream::GetCurrentDelay() {
156 snd_pcm_sframes_t delay = -1;
158 int error = wrapper_->PcmDelay(device_handle_, &delay);
159 if (error < 0)
160 Recover(error);
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_);
167 return delay;
170 void AlsaPcmInputStream::ReadAudio() {
171 DCHECK(callback_);
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);
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;
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;
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;
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);
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.";
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();
232 base::MessageLoop::current()->PostDelayedTask(
233 FROM_HERE,
234 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()),
235 delay);
238 void AlsaPcmInputStream::Stop() {
239 if (!device_handle_ || !callback_)
240 return;
242 StopAgc();
244 weak_factory_.InvalidateWeakPtrs(); // Cancel the next scheduled read.
245 int error = wrapper_->PcmDrop(device_handle_);
246 if (error < 0)
247 HandleError("PcmDrop", error);
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);
257 if (mixer_handle_)
258 alsa_util::CloseMixer(wrapper_, mixer_handle_, device_name_);
260 audio_buffer_.reset();
261 device_handle_ = NULL;
262 mixer_handle_ = NULL;
263 mixer_element_handle_ = NULL;
265 if (callback_)
266 callback_->OnClose(this);
269 audio_manager_->ReleaseInputStream(this);
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;
278 if (!wrapper_->MixerSelemHasCaptureVolume(mixer_element_handle_)) {
279 DLOG(WARNING) << "Unsupported microphone volume for " << device_name_;
280 return 0.0;
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;
291 DCHECK(min == 0);
292 DCHECK(max > 0);
294 return static_cast<double>(max);
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;
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_;
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();
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;
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;
332 return static_cast<double>(current_volume);
335 void AlsaPcmInputStream::HandleError(const char* method, int error) {
336 LOG(WARNING) << method << ": " << wrapper_->StrError(error);
337 callback_->OnError(this);
340 } // namespace media