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