• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AlsaPcmInputStream(AudioManagerBase * audio_manager,const std::string & device_name,const AudioParameters & params,AlsaWrapper * wrapper)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()) / 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 
~AlsaPcmInputStream()48 AlsaPcmInputStream::~AlsaPcmInputStream() {}
49 
Open()50 bool 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 
Start(AudioInputCallback * callback)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);
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 
Recover(int original_error)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;
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 
GetCurrentDelay()155 snd_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 
ReadAudio()170 void 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 
Stop()238 void 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 
Close()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);
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 
GetMaxVolume()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;
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 
SetVolume(double volume)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;
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 
GetVolume()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;
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 
HandleError(const char * method,int error)335 void AlsaPcmInputStream::HandleError(const char* method, int error) {
336   LOG(WARNING) << method << ": " << wrapper_->StrError(error);
337   callback_->OnError(this);
338 }
339 
340 }  // namespace media
341