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"
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"
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
,
31 : audio_manager_(audio_manager
),
32 device_name_(device_name
),
34 bytes_per_buffer_(params
.frames_per_buffer() *
35 (params
.channels() * params
.bits_per_sample()) /
38 buffer_duration_(base::TimeDelta::FromMicroseconds(
39 params
.frames_per_buffer() * base::Time::kMicrosecondsPerSecond
/
40 static_cast<float>(params
.sample_rate()))),
44 mixer_element_handle_(NULL
),
45 read_callback_behind_schedule_(false),
46 audio_bus_(AudioBus::Create(params
)),
50 AlsaPcmInputStream::~AlsaPcmInputStream() {}
52 bool AlsaPcmInputStream::Open() {
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();
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
);
78 device_name_
= device_names
[i
];
83 device_handle_
= alsa_util::OpenCaptureDevice(wrapper_
,
86 params_
.sample_rate(),
87 pcm_format
, latency_us
);
91 audio_buffer_
.reset(new uint8
[bytes_per_buffer_
]);
93 // Open the microphone mixer.
94 mixer_handle_
= alsa_util::OpenMixer(wrapper_
, device_name_
);
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
;
108 int error
= wrapper_
->PcmPrepare(device_handle_
);
110 HandleError("PcmPrepare", error
);
112 error
= wrapper_
->PcmStart(device_handle_
);
114 HandleError("PcmStart", error
);
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(
127 base::Bind(&AlsaPcmInputStream::ReadAudio
, weak_factory_
.GetWeakPtr()),
132 bool AlsaPcmInputStream::Recover(int original_error
) {
133 int error
= wrapper_
->PcmRecover(device_handle_
, original_error
, 1);
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
138 LOG(WARNING
) << "Unable to recover from \""
139 << wrapper_
->StrError(original_error
) << "\": "
140 << wrapper_
->StrError(error
);
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_
);
149 HandleError("PcmStart", error
);
157 snd_pcm_sframes_t
AlsaPcmInputStream::GetCurrentDelay() {
158 snd_pcm_sframes_t delay
= -1;
160 int error
= wrapper_
->PcmDelay(device_handle_
, &delay
);
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.
167 delay
= wrapper_
->PcmAvailUpdate(device_handle_
);
172 void AlsaPcmInputStream::ReadAudio() {
175 snd_pcm_sframes_t frames
= wrapper_
->PcmAvailUpdate(device_handle_
);
176 if (frames
< 0) { // Potentially recoverable error?
177 LOG(WARNING
) << "PcmAvailUpdate(): " << wrapper_
->StrError(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(
194 base::Bind(&AlsaPcmInputStream::ReadAudio
, weak_factory_
.GetWeakPtr()),
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
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);
217 this, audio_bus_
.get(), hardware_delay_bytes
, normalized_volume
);
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()
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(
239 base::Bind(&AlsaPcmInputStream::ReadAudio
, weak_factory_
.GetWeakPtr()),
243 void AlsaPcmInputStream::Stop() {
244 if (!device_handle_
|| !callback_
)
249 weak_factory_
.InvalidateWeakPtrs(); // Cancel the next scheduled read.
250 int error
= wrapper_
->PcmDrop(device_handle_
);
252 HandleError("PcmDrop", error
);
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_
);
262 HandleError("PcmClose", error
);
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_
;
282 if (!wrapper_
->MixerSelemHasCaptureVolume(mixer_element_handle_
)) {
283 DLOG(WARNING
) << "Unsupported microphone volume for " << device_name_
;
289 if (wrapper_
->MixerSelemGetCaptureVolumeRange(mixer_element_handle_
,
292 DLOG(WARNING
) << "Unsupported max microphone volume for " << device_name_
;
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_
;
307 int error
= wrapper_
->MixerSelemSetCaptureVolumeAll(
308 mixer_element_handle_
, static_cast<long>(volume
));
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.
321 double AlsaPcmInputStream::GetVolume() {
322 if (!mixer_handle_
|| !mixer_element_handle_
) {
323 DLOG(WARNING
) << "GetVolume is not supported for " << device_name_
;
327 long current_volume
= 0;
328 int error
= wrapper_
->MixerSelemGetCaptureVolume(
329 mixer_element_handle_
, static_cast<snd_mixer_selem_channel_id_t
>(0),
332 DLOG(WARNING
) << "Unable to get volume for " << device_name_
;
336 return static_cast<double>(current_volume
);
339 bool AlsaPcmInputStream::IsMuted() {
343 void AlsaPcmInputStream::HandleError(const char* method
, int error
) {
344 LOG(WARNING
) << method
<< ": " << wrapper_
->StrError(error
);
345 callback_
->OnError(this);