cras_input.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.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 : 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 31CrasInputStream::~CrasInputStream() { 32 DCHECK(!client_); 33} 34 35bool 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 85void 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 102void 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 StartAgc(); 111 112 callback_ = callback; 113 LOG(ERROR) << "Input Start"; 114 115 // Prepare |audio_format| and |stream_params| for the stream we 116 // will create. 117 cras_audio_format* audio_format = cras_audio_format_create( 118 alsa_util::BitsToFormat(params_.bits_per_sample()), 119 params_.sample_rate(), 120 params_.channels()); 121 if (!audio_format) { 122 DLOG(WARNING) << "Error setting up audio parameters."; 123 callback_->OnError(this); 124 callback_ = NULL; 125 return; 126 } 127 128 unsigned int frames_per_packet = params_.frames_per_buffer(); 129 cras_stream_params* stream_params = cras_client_stream_params_create( 130 CRAS_STREAM_INPUT, 131 frames_per_packet, // Total latency. 132 frames_per_packet, // Call back when this many ready. 133 frames_per_packet, // Minimum Callback level ignored for capture streams. 134 CRAS_STREAM_TYPE_DEFAULT, 135 0, // Unused flags. 136 this, 137 CrasInputStream::SamplesReady, 138 CrasInputStream::StreamError, 139 audio_format); 140 if (!stream_params) { 141 DLOG(WARNING) << "Error setting up stream parameters."; 142 callback_->OnError(this); 143 callback_ = NULL; 144 cras_audio_format_destroy(audio_format); 145 return; 146 } 147 148 // Before starting the stream, save the number of bytes in a frame for use in 149 // the callback. 150 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); 151 152 // Adding the stream will start the audio callbacks. 153 if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) { 154 audio_manager_->IncreaseActiveInputStreamCount(); 155 } else { 156 DLOG(WARNING) << "Failed to add the stream."; 157 callback_->OnError(this); 158 callback_ = NULL; 159 } 160 161 // Done with config params. 162 cras_audio_format_destroy(audio_format); 163 cras_client_stream_params_destroy(stream_params); 164 165 started_ = true; 166} 167 168void CrasInputStream::Stop() { 169 DCHECK(client_); 170 171 if (!callback_ || !started_) 172 return; 173 174 StopAgc(); 175 176 // Removing the stream from the client stops audio. 177 cras_client_rm_stream(client_, stream_id_); 178 179 audio_manager_->DecreaseActiveInputStreamCount(); 180 181 started_ = false; 182} 183 184// Static callback asking for samples. Run on high priority thread. 185int CrasInputStream::SamplesReady(cras_client* client, 186 cras_stream_id_t stream_id, 187 uint8* samples, 188 size_t frames, 189 const timespec* sample_ts, 190 void* arg) { 191 CrasInputStream* me = static_cast<CrasInputStream*>(arg); 192 me->ReadAudio(frames, samples, sample_ts); 193 return frames; 194} 195 196// Static callback for stream errors. 197int CrasInputStream::StreamError(cras_client* client, 198 cras_stream_id_t stream_id, 199 int err, 200 void* arg) { 201 CrasInputStream* me = static_cast<CrasInputStream*>(arg); 202 me->NotifyStreamError(err); 203 return 0; 204} 205 206void CrasInputStream::ReadAudio(size_t frames, 207 uint8* buffer, 208 const timespec* sample_ts) { 209 DCHECK(callback_); 210 211 timespec latency_ts = {0, 0}; 212 213 // Determine latency and pass that on to the sink. sample_ts is the wall time 214 // indicating when the first sample in the buffer was captured. Convert that 215 // to latency in bytes. 216 cras_client_calc_capture_latency(sample_ts, &latency_ts); 217 double latency_usec = 218 latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond + 219 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; 220 double frames_latency = 221 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; 222 unsigned int bytes_latency = 223 static_cast<unsigned int>(frames_latency * bytes_per_frame_); 224 225 // Update the AGC volume level once every second. Note that, |volume| is 226 // also updated each time SetVolume() is called through IPC by the 227 // render-side AGC. 228 double normalized_volume = 0.0; 229 GetAgcVolume(&normalized_volume); 230 231 callback_->OnData(this, 232 buffer, 233 frames * bytes_per_frame_, 234 bytes_latency, 235 normalized_volume); 236} 237 238void CrasInputStream::NotifyStreamError(int err) { 239 if (callback_) 240 callback_->OnError(this); 241} 242 243double CrasInputStream::GetMaxVolume() { 244 DCHECK(client_); 245 246 // Capture gain is returned as dB * 100 (150 => 1.5dBFS). Convert the dB 247 // value to a ratio before returning. 248 double dB = cras_client_get_system_max_capture_gain(client_) / 100.0; 249 return GetVolumeRatioFromDecibels(dB); 250} 251 252void CrasInputStream::SetVolume(double volume) { 253 DCHECK(client_); 254 255 // Convert from the passed volume ratio, to dB * 100. 256 double dB = GetDecibelsFromVolumeRatio(volume); 257 cras_client_set_system_capture_gain(client_, static_cast<long>(dB * 100.0)); 258 259 // Update the AGC volume level based on the last setting above. Note that, 260 // the volume-level resolution is not infinite and it is therefore not 261 // possible to assume that the volume provided as input parameter can be 262 // used directly. Instead, a new query to the audio hardware is required. 263 // This method does nothing if AGC is disabled. 264 UpdateAgcVolume(); 265} 266 267double CrasInputStream::GetVolume() { 268 if (!client_) 269 return 0.0; 270 271 long dB = cras_client_get_system_capture_gain(client_) / 100.0; 272 return GetVolumeRatioFromDecibels(dB); 273} 274 275double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const { 276 return pow(10, dB / 20.0); 277} 278 279double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const { 280 return 20 * log10(volume_ratio); 281} 282 283} // namespace media 284