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