| 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 |