OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/linux/alsa_input.h" | 5 #include "media/audio/linux/alsa_input.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/bind.h" | 8 #include "base/bind.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
(...skipping 11 matching lines...) Expand all Loading... |
22 | 22 |
23 const char* AlsaPcmInputStream::kAutoSelectDevice = ""; | 23 const char* AlsaPcmInputStream::kAutoSelectDevice = ""; |
24 | 24 |
25 AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerLinux* audio_manager, | 25 AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerLinux* audio_manager, |
26 const std::string& device_name, | 26 const std::string& device_name, |
27 const AudioParameters& params, | 27 const AudioParameters& params, |
28 AlsaWrapper* wrapper) | 28 AlsaWrapper* wrapper) |
29 : audio_manager_(audio_manager), | 29 : audio_manager_(audio_manager), |
30 device_name_(device_name), | 30 device_name_(device_name), |
31 params_(params), | 31 params_(params), |
32 bytes_per_packet_(params.samples_per_packet * | 32 bytes_per_buffer_(params.frames_per_buffer() * |
33 (params.channels * params.bits_per_sample) / 8), | 33 (params.channels() * params.bits_per_sample()) / 8), |
34 wrapper_(wrapper), | 34 wrapper_(wrapper), |
35 packet_duration_ms_( | 35 buffer_duration_ms_( |
36 (params.samples_per_packet * base::Time::kMillisecondsPerSecond) / | 36 (params.frames_per_buffer() * base::Time::kMillisecondsPerSecond) / |
37 params.sample_rate), | 37 params.sample_rate()), |
38 callback_(NULL), | 38 callback_(NULL), |
39 device_handle_(NULL), | 39 device_handle_(NULL), |
40 mixer_handle_(NULL), | 40 mixer_handle_(NULL), |
41 mixer_element_handle_(NULL), | 41 mixer_element_handle_(NULL), |
42 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 42 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
43 read_callback_behind_schedule_(false) { | 43 read_callback_behind_schedule_(false) { |
44 } | 44 } |
45 | 45 |
46 AlsaPcmInputStream::~AlsaPcmInputStream() {} | 46 AlsaPcmInputStream::~AlsaPcmInputStream() {} |
47 | 47 |
48 bool AlsaPcmInputStream::Open() { | 48 bool AlsaPcmInputStream::Open() { |
49 if (device_handle_) | 49 if (device_handle_) |
50 return false; // Already open. | 50 return false; // Already open. |
51 | 51 |
52 snd_pcm_format_t pcm_format = alsa_util::BitsToFormat( | 52 snd_pcm_format_t pcm_format = alsa_util::BitsToFormat( |
53 params_.bits_per_sample); | 53 params_.bits_per_sample()); |
54 if (pcm_format == SND_PCM_FORMAT_UNKNOWN) { | 54 if (pcm_format == SND_PCM_FORMAT_UNKNOWN) { |
55 LOG(WARNING) << "Unsupported bits per sample: " | 55 LOG(WARNING) << "Unsupported bits per sample: " |
56 << params_.bits_per_sample; | 56 << params_.bits_per_sample(); |
57 return false; | 57 return false; |
58 } | 58 } |
59 | 59 |
60 uint32 latency_us = packet_duration_ms_ * kNumPacketsInRingBuffer * | 60 uint32 latency_us = buffer_duration_ms_ * kNumPacketsInRingBuffer * |
61 base::Time::kMicrosecondsPerMillisecond; | 61 base::Time::kMicrosecondsPerMillisecond; |
62 | 62 |
63 // Use the same minimum required latency as output. | 63 // Use the same minimum required latency as output. |
64 latency_us = std::max(latency_us, AlsaPcmOutputStream::kMinLatencyMicros); | 64 latency_us = std::max(latency_us, AlsaPcmOutputStream::kMinLatencyMicros); |
65 | 65 |
66 if (device_name_ == kAutoSelectDevice) { | 66 if (device_name_ == kAutoSelectDevice) { |
67 const char* device_names[] = { kDefaultDevice1, kDefaultDevice2 }; | 67 const char* device_names[] = { kDefaultDevice1, kDefaultDevice2 }; |
68 for (size_t i = 0; i < arraysize(device_names); ++i) { | 68 for (size_t i = 0; i < arraysize(device_names); ++i) { |
69 device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, device_names[i], | 69 device_handle_ = alsa_util::OpenCaptureDevice( |
70 params_.channels, | 70 wrapper_, device_names[i], params_.channels(), |
71 params_.sample_rate, | 71 params_.sample_rate(), pcm_format, latency_us); |
72 pcm_format, latency_us); | 72 |
73 if (device_handle_) { | 73 if (device_handle_) { |
74 device_name_ = device_names[i]; | 74 device_name_ = device_names[i]; |
75 break; | 75 break; |
76 } | 76 } |
77 } | 77 } |
78 } else { | 78 } else { |
79 device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, | 79 device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, |
80 device_name_.c_str(), | 80 device_name_.c_str(), |
81 params_.channels, | 81 params_.channels(), |
82 params_.sample_rate, | 82 params_.sample_rate(), |
83 pcm_format, latency_us); | 83 pcm_format, latency_us); |
84 } | 84 } |
85 | 85 |
86 if (device_handle_) { | 86 if (device_handle_) { |
87 audio_packet_.reset(new uint8[bytes_per_packet_]); | 87 audio_buffer_.reset(new uint8[bytes_per_buffer_]); |
88 | 88 |
89 // Open the microphone mixer. | 89 // Open the microphone mixer. |
90 mixer_handle_ = alsa_util::OpenMixer(wrapper_, device_name_); | 90 mixer_handle_ = alsa_util::OpenMixer(wrapper_, device_name_); |
91 if (mixer_handle_) { | 91 if (mixer_handle_) { |
92 mixer_element_handle_ = alsa_util::LoadCaptureMixerElement( | 92 mixer_element_handle_ = alsa_util::LoadCaptureMixerElement( |
93 wrapper_, mixer_handle_); | 93 wrapper_, mixer_handle_); |
94 } | 94 } |
95 } | 95 } |
96 | 96 |
97 return device_handle_ != NULL; | 97 return device_handle_ != NULL; |
98 } | 98 } |
99 | 99 |
100 void AlsaPcmInputStream::Start(AudioInputCallback* callback) { | 100 void AlsaPcmInputStream::Start(AudioInputCallback* callback) { |
101 DCHECK(!callback_ && callback); | 101 DCHECK(!callback_ && callback); |
102 callback_ = callback; | 102 callback_ = callback; |
103 int error = wrapper_->PcmPrepare(device_handle_); | 103 int error = wrapper_->PcmPrepare(device_handle_); |
104 if (error < 0) { | 104 if (error < 0) { |
105 HandleError("PcmPrepare", error); | 105 HandleError("PcmPrepare", error); |
106 } else { | 106 } else { |
107 error = wrapper_->PcmStart(device_handle_); | 107 error = wrapper_->PcmStart(device_handle_); |
108 if (error < 0) | 108 if (error < 0) |
109 HandleError("PcmStart", error); | 109 HandleError("PcmStart", error); |
110 } | 110 } |
111 | 111 |
112 if (error < 0) { | 112 if (error < 0) { |
113 callback_ = NULL; | 113 callback_ = NULL; |
114 } else { | 114 } else { |
115 // We start reading data half |packet_duration_ms_| later than when the | 115 // We start reading data half |buffer_duration_ms_| later than when the |
116 // packet might have got filled, to accommodate some delays in the audio | 116 // buffer might have got filled, to accommodate some delays in the audio |
117 // driver. This could also give us a smooth read sequence going forward. | 117 // driver. This could also give us a smooth read sequence going forward. |
118 base::TimeDelta delay = base::TimeDelta::FromMilliseconds( | 118 base::TimeDelta delay = base::TimeDelta::FromMilliseconds( |
119 packet_duration_ms_ + packet_duration_ms_ / 2); | 119 buffer_duration_ms_ + buffer_duration_ms_ / 2); |
120 next_read_time_ = base::Time::Now() + delay; | 120 next_read_time_ = base::Time::Now() + delay; |
121 MessageLoop::current()->PostDelayedTask( | 121 MessageLoop::current()->PostDelayedTask( |
122 FROM_HERE, | 122 FROM_HERE, |
123 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), | 123 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), |
124 delay); | 124 delay); |
125 | 125 |
126 audio_manager_->IncreaseActiveInputStreamCount(); | 126 audio_manager_->IncreaseActiveInputStreamCount(); |
127 } | 127 } |
128 } | 128 } |
129 | 129 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 | 169 |
170 void AlsaPcmInputStream::ReadAudio() { | 170 void AlsaPcmInputStream::ReadAudio() { |
171 DCHECK(callback_); | 171 DCHECK(callback_); |
172 | 172 |
173 snd_pcm_sframes_t frames = wrapper_->PcmAvailUpdate(device_handle_); | 173 snd_pcm_sframes_t frames = wrapper_->PcmAvailUpdate(device_handle_); |
174 if (frames < 0) { // Potentially recoverable error? | 174 if (frames < 0) { // Potentially recoverable error? |
175 LOG(WARNING) << "PcmAvailUpdate(): " << wrapper_->StrError(frames); | 175 LOG(WARNING) << "PcmAvailUpdate(): " << wrapper_->StrError(frames); |
176 Recover(frames); | 176 Recover(frames); |
177 } | 177 } |
178 | 178 |
179 if (frames < params_.samples_per_packet) { | 179 if (frames < params_.frames_per_buffer()) { |
180 // Not enough data yet or error happened. In both cases wait for a very | 180 // Not enough data yet or error happened. In both cases wait for a very |
181 // small duration before checking again. | 181 // small duration before checking again. |
182 // Even Though read callback was behind schedule, there is no data, so | 182 // Even Though read callback was behind schedule, there is no data, so |
183 // reset the next_read_time_. | 183 // reset the next_read_time_. |
184 if (read_callback_behind_schedule_) { | 184 if (read_callback_behind_schedule_) { |
185 next_read_time_ = base::Time::Now(); | 185 next_read_time_ = base::Time::Now(); |
186 read_callback_behind_schedule_ = false; | 186 read_callback_behind_schedule_ = false; |
187 } | 187 } |
188 | 188 |
189 base::TimeDelta next_check_time = base::TimeDelta::FromMilliseconds( | 189 base::TimeDelta next_check_time = base::TimeDelta::FromMilliseconds( |
190 packet_duration_ms_ / 2); | 190 buffer_duration_ms_ / 2); |
191 MessageLoop::current()->PostDelayedTask( | 191 MessageLoop::current()->PostDelayedTask( |
192 FROM_HERE, | 192 FROM_HERE, |
193 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), | 193 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), |
194 next_check_time); | 194 next_check_time); |
195 return; | 195 return; |
196 } | 196 } |
197 | 197 |
198 int num_packets = frames / params_.samples_per_packet; | 198 int num_buffers = frames / params_.frames_per_buffer(); |
199 int num_packets_read = num_packets; | 199 int num_buffers_read = num_buffers; |
200 int bytes_per_frame = params_.channels * params_.bits_per_sample / 8; | |
201 uint32 hardware_delay_bytes = | 200 uint32 hardware_delay_bytes = |
202 static_cast<uint32>(GetCurrentDelay() * bytes_per_frame); | 201 static_cast<uint32>(GetCurrentDelay() * params_.GetBytesPerFrame()); |
203 while (num_packets--) { | 202 while (num_buffers--) { |
204 int frames_read = wrapper_->PcmReadi(device_handle_, audio_packet_.get(), | 203 int frames_read = wrapper_->PcmReadi(device_handle_, audio_buffer_.get(), |
205 params_.samples_per_packet); | 204 params_.frames_per_buffer()); |
206 if (frames_read == params_.samples_per_packet) { | 205 if (frames_read == params_.frames_per_buffer()) { |
207 callback_->OnData(this, audio_packet_.get(), bytes_per_packet_, | 206 callback_->OnData(this, audio_buffer_.get(), bytes_per_buffer_, |
208 hardware_delay_bytes); | 207 hardware_delay_bytes); |
209 } else { | 208 } else { |
210 LOG(WARNING) << "PcmReadi returning less than expected frames: " | 209 LOG(WARNING) << "PcmReadi returning less than expected frames: " |
211 << frames_read << " vs. " << params_.samples_per_packet | 210 << frames_read << " vs. " << params_.frames_per_buffer() |
212 << ". Dropping this packet."; | 211 << ". Dropping this buffer."; |
213 } | 212 } |
214 } | 213 } |
215 | 214 |
216 next_read_time_ += base::TimeDelta::FromMilliseconds( | 215 next_read_time_ += base::TimeDelta::FromMilliseconds( |
217 packet_duration_ms_ * num_packets_read); | 216 buffer_duration_ms_ * num_buffers_read); |
218 base::TimeDelta delay = next_read_time_ - base::Time::Now(); | 217 base::TimeDelta delay = next_read_time_ - base::Time::Now(); |
219 if (delay < base::TimeDelta()) { | 218 if (delay < base::TimeDelta()) { |
220 LOG(WARNING) << "Audio read callback behind schedule by " | 219 LOG(WARNING) << "Audio read callback behind schedule by " |
221 << (packet_duration_ms_ - delay.InMilliseconds()) | 220 << (buffer_duration_ms_ - delay.InMilliseconds()) |
222 << " (ms)."; | 221 << " (ms)."; |
223 // Read callback is behind schedule. Assuming there is data pending in | 222 // Read callback is behind schedule. Assuming there is data pending in |
224 // the soundcard, invoke the read callback immediate in order to catch up. | 223 // the soundcard, invoke the read callback immediate in order to catch up. |
225 read_callback_behind_schedule_ = true; | 224 read_callback_behind_schedule_ = true; |
226 delay = base::TimeDelta(); | 225 delay = base::TimeDelta(); |
227 } | 226 } |
228 | 227 |
229 MessageLoop::current()->PostDelayedTask( | 228 MessageLoop::current()->PostDelayedTask( |
230 FROM_HERE, | 229 FROM_HERE, |
231 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), | 230 base::Bind(&AlsaPcmInputStream::ReadAudio, weak_factory_.GetWeakPtr()), |
(...skipping 17 matching lines...) Expand all Loading... |
249 void AlsaPcmInputStream::Close() { | 248 void AlsaPcmInputStream::Close() { |
250 if (device_handle_) { | 249 if (device_handle_) { |
251 weak_factory_.InvalidateWeakPtrs(); // Cancel the next scheduled read. | 250 weak_factory_.InvalidateWeakPtrs(); // Cancel the next scheduled read. |
252 int error = alsa_util::CloseDevice(wrapper_, device_handle_); | 251 int error = alsa_util::CloseDevice(wrapper_, device_handle_); |
253 if (error < 0) | 252 if (error < 0) |
254 HandleError("PcmClose", error); | 253 HandleError("PcmClose", error); |
255 | 254 |
256 if (mixer_handle_) | 255 if (mixer_handle_) |
257 alsa_util::CloseMixer(wrapper_, mixer_handle_, device_name_); | 256 alsa_util::CloseMixer(wrapper_, mixer_handle_, device_name_); |
258 | 257 |
259 audio_packet_.reset(); | 258 audio_buffer_.reset(); |
260 device_handle_ = NULL; | 259 device_handle_ = NULL; |
261 mixer_handle_ = NULL; | 260 mixer_handle_ = NULL; |
262 mixer_element_handle_ = NULL; | 261 mixer_element_handle_ = NULL; |
263 | 262 |
264 if (callback_) | 263 if (callback_) |
265 callback_->OnClose(this); | 264 callback_->OnClose(this); |
266 } | 265 } |
267 | 266 |
268 audio_manager_->ReleaseInputStream(this); | 267 audio_manager_->ReleaseInputStream(this); |
269 } | 268 } |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 return 0.0; | 320 return 0.0; |
322 } | 321 } |
323 | 322 |
324 return static_cast<double>(current_volume); | 323 return static_cast<double>(current_volume); |
325 } | 324 } |
326 | 325 |
327 void AlsaPcmInputStream::HandleError(const char* method, int error) { | 326 void AlsaPcmInputStream::HandleError(const char* method, int error) { |
328 LOG(WARNING) << method << ": " << wrapper_->StrError(error); | 327 LOG(WARNING) << method << ": " << wrapper_->StrError(error); |
329 callback_->OnError(this, error); | 328 callback_->OnError(this, error); |
330 } | 329 } |
OLD | NEW |