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