| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/audio/linux/cras_input.h" |
| 6 |
| 7 #include <math.h> |
| 8 |
| 9 #include "base/basictypes.h" |
| 10 #include "base/bind.h" |
| 11 #include "base/logging.h" |
| 12 #include "base/time.h" |
| 13 #include "media/audio/audio_manager.h" |
| 14 #include "media/audio/linux/alsa_util.h" |
| 15 #include "media/audio/linux/audio_manager_linux.h" |
| 16 |
| 17 namespace media { |
| 18 |
| 19 CrasInputStream::CrasInputStream(const AudioParameters& params, |
| 20 AudioManagerLinux* manager) |
| 21 : audio_manager_(manager), |
| 22 bytes_per_frame_(0), |
| 23 callback_(NULL), |
| 24 client_(NULL), |
| 25 params_(params), |
| 26 started_(false), |
| 27 stream_id_(0) { |
| 28 DCHECK(audio_manager_); |
| 29 } |
| 30 |
| 31 CrasInputStream::~CrasInputStream() { |
| 32 DCHECK(!client_); |
| 33 } |
| 34 |
| 35 bool CrasInputStream::Open() { |
| 36 if (client_) { |
| 37 NOTREACHED() << "CrasInputStream already open"; |
| 38 return false; // Already open. |
| 39 } |
| 40 |
| 41 // Sanity check input values. |
| 42 if (params_.sample_rate() <= 0) { |
| 43 DLOG(WARNING) << "Unsupported audio frequency."; |
| 44 return false; |
| 45 } |
| 46 |
| 47 if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() && |
| 48 AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) { |
| 49 DLOG(WARNING) << "Unsupported audio format."; |
| 50 return false; |
| 51 } |
| 52 |
| 53 snd_pcm_format_t pcm_format = |
| 54 alsa_util::BitsToFormat(params_.bits_per_sample()); |
| 55 if (pcm_format == SND_PCM_FORMAT_UNKNOWN) { |
| 56 DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample(); |
| 57 return false; |
| 58 } |
| 59 |
| 60 // Create the client and connect to the CRAS server. |
| 61 if (cras_client_create(&client_) < 0) { |
| 62 DLOG(WARNING) << "Couldn't create CRAS client.\n"; |
| 63 client_ = NULL; |
| 64 return false; |
| 65 } |
| 66 |
| 67 if (cras_client_connect(client_)) { |
| 68 DLOG(WARNING) << "Couldn't connect CRAS client.\n"; |
| 69 cras_client_destroy(client_); |
| 70 client_ = NULL; |
| 71 return false; |
| 72 } |
| 73 |
| 74 // Then start running the client. |
| 75 if (cras_client_run_thread(client_)) { |
| 76 DLOG(WARNING) << "Couldn't run CRAS client.\n"; |
| 77 cras_client_destroy(client_); |
| 78 client_ = NULL; |
| 79 return false; |
| 80 } |
| 81 |
| 82 return true; |
| 83 } |
| 84 |
| 85 void CrasInputStream::Close() { |
| 86 if (client_) { |
| 87 cras_client_stop(client_); |
| 88 cras_client_destroy(client_); |
| 89 client_ = NULL; |
| 90 } |
| 91 |
| 92 if (callback_) { |
| 93 callback_->OnClose(this); |
| 94 callback_ = NULL; |
| 95 } |
| 96 |
| 97 // Signal to the manager that we're closed and can be removed. |
| 98 // Should be last call in the method as it deletes "this". |
| 99 audio_manager_->ReleaseInputStream(this); |
| 100 } |
| 101 |
| 102 void CrasInputStream::Start(AudioInputCallback* callback) { |
| 103 DCHECK(client_); |
| 104 DCHECK(callback); |
| 105 |
| 106 // If already playing, stop before re-starting. |
| 107 if (started_) |
| 108 return; |
| 109 |
| 110 callback_ = callback; |
| 111 |
| 112 // Prepare |audio_format| and |stream_params| for the stream we |
| 113 // will create. |
| 114 cras_audio_format* audio_format = cras_audio_format_create( |
| 115 alsa_util::BitsToFormat(params_.bits_per_sample()), |
| 116 params_.sample_rate(), |
| 117 params_.channels()); |
| 118 if (!audio_format) { |
| 119 DLOG(WARNING) << "Error setting up audio parameters."; |
| 120 callback_->OnError(this, -ENOMEM); |
| 121 callback_ = NULL; |
| 122 return; |
| 123 } |
| 124 |
| 125 unsigned int frames_per_packet = params_.frames_per_buffer(); |
| 126 cras_stream_params* stream_params = cras_client_stream_params_create( |
| 127 CRAS_STREAM_INPUT, |
| 128 frames_per_packet, // Total latency. |
| 129 frames_per_packet, // Call back when this many ready. |
| 130 frames_per_packet, // Minimum Callback level ignored for capture streams. |
| 131 CRAS_STREAM_TYPE_DEFAULT, |
| 132 0, // Unused flags. |
| 133 this, |
| 134 CrasInputStream::SamplesReady, |
| 135 CrasInputStream::StreamError, |
| 136 audio_format); |
| 137 if (!stream_params) { |
| 138 DLOG(WARNING) << "Error setting up stream parameters."; |
| 139 callback_->OnError(this, -ENOMEM); |
| 140 callback_ = NULL; |
| 141 cras_audio_format_destroy(audio_format); |
| 142 return; |
| 143 } |
| 144 |
| 145 // Before starting the stream, save the number of bytes in a frame for use in |
| 146 // the callback. |
| 147 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); |
| 148 |
| 149 // Adding the stream will start the audio callbacks. |
| 150 if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) { |
| 151 audio_manager_->IncreaseActiveInputStreamCount(); |
| 152 } else { |
| 153 DLOG(WARNING) << "Failed to add the stream."; |
| 154 callback_->OnError(this, -EIO); |
| 155 callback_ = NULL; |
| 156 } |
| 157 |
| 158 // Done with config params. |
| 159 cras_audio_format_destroy(audio_format); |
| 160 cras_client_stream_params_destroy(stream_params); |
| 161 |
| 162 started_ = true; |
| 163 } |
| 164 |
| 165 void CrasInputStream::Stop() { |
| 166 DCHECK(client_); |
| 167 |
| 168 if (!callback_ || !started_) |
| 169 return; |
| 170 |
| 171 // Removing the stream from the client stops audio. |
| 172 cras_client_rm_stream(client_, stream_id_); |
| 173 |
| 174 audio_manager_->DecreaseActiveInputStreamCount(); |
| 175 |
| 176 started_ = false; |
| 177 } |
| 178 |
| 179 // Static callback asking for samples. Run on high priority thread. |
| 180 int CrasInputStream::SamplesReady(cras_client* client, |
| 181 cras_stream_id_t stream_id, |
| 182 uint8* samples, |
| 183 size_t frames, |
| 184 const timespec* sample_ts, |
| 185 void* arg) { |
| 186 CrasInputStream* me = static_cast<CrasInputStream*>(arg); |
| 187 me->ReadAudio(frames, samples, sample_ts); |
| 188 return frames; |
| 189 } |
| 190 |
| 191 // Static callback for stream errors. |
| 192 int CrasInputStream::StreamError(cras_client* client, |
| 193 cras_stream_id_t stream_id, |
| 194 int err, |
| 195 void* arg) { |
| 196 CrasInputStream* me = static_cast<CrasInputStream*>(arg); |
| 197 me->NotifyStreamError(err); |
| 198 return 0; |
| 199 } |
| 200 |
| 201 void CrasInputStream::ReadAudio(size_t frames, |
| 202 uint8* buffer, |
| 203 const timespec* sample_ts) { |
| 204 DCHECK(callback_); |
| 205 |
| 206 timespec latency_ts = {0, 0}; |
| 207 |
| 208 // Determine latency and pass that on to the sink. sample_ts is the wall time |
| 209 // indicating when the first sample in the buffer was captured. Convert that |
| 210 // to latency in bytes. |
| 211 cras_client_calc_capture_latency(sample_ts, &latency_ts); |
| 212 double latency_usec = |
| 213 latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond + |
| 214 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
| 215 double frames_latency = |
| 216 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; |
| 217 unsigned int bytes_latency = |
| 218 static_cast<unsigned int>(frames_latency * bytes_per_frame_); |
| 219 |
| 220 // Update the AGC volume level once every second. Note that, |volume| is |
| 221 // also updated each time SetVolume() is called through IPC by the |
| 222 // render-side AGC. |
| 223 double normalized_volume = 0.0; |
| 224 QueryAgcVolume(&normalized_volume); |
| 225 |
| 226 callback_->OnData(this, |
| 227 buffer, |
| 228 frames * bytes_per_frame_, |
| 229 bytes_latency, |
| 230 normalized_volume); |
| 231 } |
| 232 |
| 233 void CrasInputStream::NotifyStreamError(int err) { |
| 234 if (callback_) |
| 235 callback_->OnError(this, err); |
| 236 } |
| 237 |
| 238 double CrasInputStream::GetMaxVolume() { |
| 239 DCHECK(client_); |
| 240 |
| 241 // Capture gain is returned as dB * 100 (150 => 1.5dBFS). Convert the dB |
| 242 // value to a ratio before returning. |
| 243 double dB = cras_client_get_system_max_capture_gain(client_) / 100.0; |
| 244 return GetVolumeRatioFromDecibels(dB); |
| 245 } |
| 246 |
| 247 void CrasInputStream::SetVolume(double volume) { |
| 248 DCHECK(client_); |
| 249 |
| 250 // Convert from the passed volume ratio, to dB * 100. |
| 251 double dB = GetDecibelsFromVolumeRatio(volume); |
| 252 cras_client_set_system_capture_gain(client_, static_cast<long>(dB * 100.0)); |
| 253 |
| 254 // Update the AGC volume level based on the last setting above. Note that, |
| 255 // the volume-level resolution is not infinite and it is therefore not |
| 256 // possible to assume that the volume provided as input parameter can be |
| 257 // used directly. Instead, a new query to the audio hardware is required. |
| 258 // This method does nothing if AGC is disabled. |
| 259 UpdateAgcVolume(); |
| 260 } |
| 261 |
| 262 double CrasInputStream::GetVolume() { |
| 263 if (!client_) |
| 264 return 0.0; |
| 265 |
| 266 long dB = cras_client_get_system_capture_gain(client_) / 100.0; |
| 267 return GetVolumeRatioFromDecibels(dB); |
| 268 } |
| 269 |
| 270 double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const { |
| 271 return pow(10, dB / 20.0); |
| 272 } |
| 273 |
| 274 double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const { |
| 275 return 20 * log10(volume_ratio); |
| 276 } |
| 277 |
| 278 } // namespace media |
| OLD | NEW |