[MemSheriff] More sendto parameter issues.
[chromium-blink-merge.git] / media / audio / alsa / alsa_input.cc
blob3c555a05b5d92e0578a1e15329686a5e2f08c152
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 "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"
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(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) {
50 AlsaPcmInputStream::~AlsaPcmInputStream() {}
52 bool AlsaPcmInputStream::Open() {
53 if (device_handle_)
54 return false; // Already open.
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;
64 uint32 latency_us =
65 buffer_duration_.InMicroseconds() * kNumPacketsInRingBuffer;
67 // Use the same minimum required latency as output.
68 latency_us = std::max(latency_us, AlsaPcmOutputStream::kMinLatencyMicros);
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);
77 if (device_handle_) {
78 device_name_ = device_names[i];
79 break;
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);
90 if (device_handle_) {
91 audio_buffer_.reset(new uint8[bytes_per_buffer_]);
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_);
101 return device_handle_ != NULL;
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);
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);
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;
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;
154 return true;
157 snd_pcm_sframes_t AlsaPcmInputStream::GetCurrentDelay() {
158 snd_pcm_sframes_t delay = -1;
160 int error = wrapper_->PcmDelay(device_handle_, &delay);
161 if (error < 0)
162 Recover(error);
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_);
169 return delay;
172 void AlsaPcmInputStream::ReadAudio() {
173 DCHECK(callback_);
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);
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;
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;
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;
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);
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.";
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();
237 base::MessageLoop::current()->PostDelayedTask(
238 FROM_HERE,
239 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()),
240 delay);
243 void AlsaPcmInputStream::Stop() {
244 if (!device_handle_ || !callback_)
245 return;
247 StopAgc();
249 weak_factory_.InvalidateWeakPtrs(); // Cancel the next scheduled read.
250 int error = wrapper_->PcmDrop(device_handle_);
251 if (error < 0)
252 HandleError("PcmDrop", error);
254 callback_ = NULL;
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);
264 if (mixer_handle_)
265 alsa_util::CloseMixer(wrapper_, mixer_handle_, device_name_);
267 audio_buffer_.reset();
268 device_handle_ = NULL;
269 mixer_handle_ = NULL;
270 mixer_element_handle_ = NULL;
273 audio_manager_->ReleaseInputStream(this);
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;
282 if (!wrapper_->MixerSelemHasCaptureVolume(mixer_element_handle_)) {
283 DLOG(WARNING) << "Unsupported microphone volume for " << device_name_;
284 return 0.0;
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;
295 DCHECK(min == 0);
296 DCHECK(max > 0);
298 return static_cast<double>(max);
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;
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_;
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();
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;
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;
336 return static_cast<double>(current_volume);
339 bool AlsaPcmInputStream::IsMuted() {
340 return false;
343 void AlsaPcmInputStream::HandleError(const char* method, int error) {
344 LOG(WARNING) << method << ": " << wrapper_->StrError(error);
345 callback_->OnError(this);
348 } // namespace media