Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / media / audio / alsa / alsa_input.cc
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
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(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
50 AlsaPcmInputStream::~AlsaPcmInputStream() {}
51
52 bool 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
104 void 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
132 bool 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
157 snd_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
172 void 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
243 void 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
257 void 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
276 double 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
301 void 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
321 double 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
339 bool AlsaPcmInputStream::IsMuted() {
340   return false;
341 }
342
343 void AlsaPcmInputStream::HandleError(const char* method, int error) {
344   LOG(WARNING) << method << ": " << wrapper_->StrError(error);
345   callback_->OnError(this);
346 }
347
348 }  // namespace media