1// Copyright 2013 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_unified.h" 6 7#include <cras_client.h> 8 9#include "base/command_line.h" 10#include "base/logging.h" 11#include "media/audio/audio_util.h" 12#include "media/audio/cras/audio_manager_cras.h" 13#include "media/audio/linux/alsa_util.h" 14 15namespace media { 16 17// Overview of operation: 18// 1) An object of CrasUnifiedStream is created by the AudioManager 19// factory: audio_man->MakeAudioStream(). 20// 2) Next some thread will call Open(), at that point a client is created and 21// configured for the correct format and sample rate. 22// 3) Then Start(source) is called and a stream is added to the CRAS client 23// which will create its own thread that periodically calls the source for more 24// data as buffers are being consumed. 25// 4) When finished Stop() is called, which is handled by stopping the stream. 26// 5) Finally Close() is called. It cleans up and notifies the audio manager, 27// which likely will destroy this object. 28// 29// For output-only streams, a unified stream is created with 0 input channels. 30// 31// Simplified data flow for unified streams: 32// 33// +-------------+ +------------------+ 34// | CRAS Server | | Chrome Client | 35// +------+------+ Add Stream +---------+--------+ 36// |<----------------------------------| 37// | | 38// | buffer_frames captured to shm | 39// |---------------------------------->| 40// | | UnifiedCallback() 41// | | ReadWriteAudio() 42// | | 43// | buffer_frames written to shm | 44// |<----------------------------------| 45// | | 46// ... Repeats for each block. ... 47// | | 48// | | 49// | Remove stream | 50// |<----------------------------------| 51// | | 52// 53// Simplified data flow for output only streams: 54// 55// +-------------+ +------------------+ 56// | CRAS Server | | Chrome Client | 57// +------+------+ Add Stream +---------+--------+ 58// |<----------------------------------| 59// | | 60// | Near out of samples, request more | 61// |---------------------------------->| 62// | | UnifiedCallback() 63// | | WriteAudio() 64// | | 65// | buffer_frames written to shm | 66// |<----------------------------------| 67// | | 68// ... Repeats for each block. ... 69// | | 70// | | 71// | Remove stream | 72// |<----------------------------------| 73// | | 74// 75// For Unified streams the Chrome client is notified whenever buffer_frames have 76// been captured. For Output streams the client is notified a few milliseconds 77// before the hardware buffer underruns and fills the buffer with another block 78// of audio. 79 80CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params, 81 AudioManagerCras* manager) 82 : client_(NULL), 83 stream_id_(0), 84 params_(params), 85 bytes_per_frame_(0), 86 is_playing_(false), 87 volume_(1.0), 88 manager_(manager), 89 source_callback_(NULL), 90 stream_direction_(CRAS_STREAM_OUTPUT) { 91 DCHECK(manager_); 92 DCHECK(params_.channels() > 0); 93 94 // Must have at least one input or output. If there are both they must be the 95 // same. 96 int input_channels = params_.input_channels(); 97 98 if (input_channels) { 99 // A unified stream for input and output. 100 DCHECK(params_.channels() == input_channels); 101 stream_direction_ = CRAS_STREAM_UNIFIED; 102 input_bus_ = AudioBus::Create(input_channels, 103 params_.frames_per_buffer()); 104 } 105 106 output_bus_ = AudioBus::Create(params); 107} 108 109CrasUnifiedStream::~CrasUnifiedStream() { 110 DCHECK(!is_playing_); 111} 112 113bool CrasUnifiedStream::Open() { 114 // Sanity check input values. 115 if (params_.sample_rate() <= 0) { 116 LOG(WARNING) << "Unsupported audio frequency."; 117 return false; 118 } 119 120 if (alsa_util::BitsToFormat(params_.bits_per_sample()) == 121 SND_PCM_FORMAT_UNKNOWN) { 122 LOG(WARNING) << "Unsupported pcm format"; 123 return false; 124 } 125 126 // Create the client and connect to the CRAS server. 127 if (cras_client_create(&client_)) { 128 LOG(WARNING) << "Couldn't create CRAS client.\n"; 129 client_ = NULL; 130 return false; 131 } 132 133 if (cras_client_connect(client_)) { 134 LOG(WARNING) << "Couldn't connect CRAS client.\n"; 135 cras_client_destroy(client_); 136 client_ = NULL; 137 return false; 138 } 139 140 // Then start running the client. 141 if (cras_client_run_thread(client_)) { 142 LOG(WARNING) << "Couldn't run CRAS client.\n"; 143 cras_client_destroy(client_); 144 client_ = NULL; 145 return false; 146 } 147 148 return true; 149} 150 151void CrasUnifiedStream::Close() { 152 if (client_) { 153 cras_client_stop(client_); 154 cras_client_destroy(client_); 155 client_ = NULL; 156 } 157 158 // Signal to the manager that we're closed and can be removed. 159 // Should be last call in the method as it deletes "this". 160 manager_->ReleaseOutputStream(this); 161} 162 163void CrasUnifiedStream::Start(AudioSourceCallback* callback) { 164 CHECK(callback); 165 source_callback_ = callback; 166 167 // Only start if we can enter the playing state. 168 if (is_playing_) 169 return; 170 171 LOG(ERROR) << "Unified Start"; 172 // Prepare |audio_format| and |stream_params| for the stream we 173 // will create. 174 cras_audio_format* audio_format = cras_audio_format_create( 175 alsa_util::BitsToFormat(params_.bits_per_sample()), 176 params_.sample_rate(), 177 params_.channels()); 178 if (!audio_format) { 179 LOG(WARNING) << "Error setting up audio parameters."; 180 callback->OnError(this); 181 return; 182 } 183 184 cras_stream_params* stream_params = cras_client_unified_params_create( 185 stream_direction_, 186 params_.frames_per_buffer(), 187 CRAS_STREAM_TYPE_DEFAULT, 188 0, 189 this, 190 CrasUnifiedStream::UnifiedCallback, 191 CrasUnifiedStream::StreamError, 192 audio_format); 193 if (!stream_params) { 194 LOG(WARNING) << "Error setting up stream parameters."; 195 callback->OnError(this); 196 cras_audio_format_destroy(audio_format); 197 return; 198 } 199 200 // Before starting the stream, save the number of bytes in a frame for use in 201 // the callback. 202 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); 203 204 // Adding the stream will start the audio callbacks requesting data. 205 if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) { 206 LOG(WARNING) << "Failed to add the stream"; 207 callback->OnError(this); 208 cras_audio_format_destroy(audio_format); 209 cras_client_stream_params_destroy(stream_params); 210 return; 211 } 212 213 // Set initial volume. 214 cras_client_set_stream_volume(client_, stream_id_, volume_); 215 216 // Done with config params. 217 cras_audio_format_destroy(audio_format); 218 cras_client_stream_params_destroy(stream_params); 219 220 is_playing_ = true; 221} 222 223void CrasUnifiedStream::Stop() { 224 if (!client_) 225 return; 226 227 // Removing the stream from the client stops audio. 228 cras_client_rm_stream(client_, stream_id_); 229 230 is_playing_ = false; 231} 232 233void CrasUnifiedStream::SetVolume(double volume) { 234 if (!client_) 235 return; 236 volume_ = static_cast<float>(volume); 237 cras_client_set_stream_volume(client_, stream_id_, volume_); 238} 239 240void CrasUnifiedStream::GetVolume(double* volume) { 241 *volume = volume_; 242} 243 244uint32 CrasUnifiedStream::GetBytesLatency( 245 const struct timespec& latency_ts) { 246 uint32 latency_usec; 247 248 // Treat negative latency (if we are too slow to render) as 0. 249 if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) { 250 latency_usec = 0; 251 } else { 252 latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) + 253 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; 254 } 255 256 double frames_latency = 257 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; 258 259 return static_cast<unsigned int>(frames_latency * bytes_per_frame_); 260} 261 262// Static callback asking for samples. 263int CrasUnifiedStream::UnifiedCallback(cras_client* client, 264 cras_stream_id_t stream_id, 265 uint8* input_samples, 266 uint8* output_samples, 267 unsigned int frames, 268 const timespec* input_ts, 269 const timespec* output_ts, 270 void* arg) { 271 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); 272 return me->DispatchCallback(frames, 273 input_samples, 274 output_samples, 275 input_ts, 276 output_ts); 277} 278 279// Static callback for stream errors. 280int CrasUnifiedStream::StreamError(cras_client* client, 281 cras_stream_id_t stream_id, 282 int err, 283 void* arg) { 284 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); 285 me->NotifyStreamError(err); 286 return 0; 287} 288 289// Calls the appropriate rendering function for this type of stream. 290uint32 CrasUnifiedStream::DispatchCallback(size_t frames, 291 uint8* input_samples, 292 uint8* output_samples, 293 const timespec* input_ts, 294 const timespec* output_ts) { 295 switch (stream_direction_) { 296 case CRAS_STREAM_OUTPUT: 297 return WriteAudio(frames, output_samples, output_ts); 298 case CRAS_STREAM_INPUT: 299 NOTREACHED() << "CrasUnifiedStream doesn't support input streams."; 300 return 0; 301 case CRAS_STREAM_UNIFIED: 302 return ReadWriteAudio(frames, input_samples, output_samples, 303 input_ts, output_ts); 304 default: 305 break; 306 } 307 308 return 0; 309} 310 311// Note these are run from a real time thread, so don't waste cycles here. 312uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames, 313 uint8* input_samples, 314 uint8* output_samples, 315 const timespec* input_ts, 316 const timespec* output_ts) { 317 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); 318 DCHECK(source_callback_); 319 320 uint32 bytes_per_sample = bytes_per_frame_ / params_.channels(); 321 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample); 322 323 // Determine latency and pass that on to the source. We have the capture time 324 // of the first input sample and the playback time of the next audio sample 325 // passed from the audio server, add them together for total latency. 326 uint32 total_delay_bytes; 327 timespec latency_ts = {0, 0}; 328 cras_client_calc_capture_latency(input_ts, &latency_ts); 329 total_delay_bytes = GetBytesLatency(latency_ts); 330 cras_client_calc_playback_latency(output_ts, &latency_ts); 331 total_delay_bytes += GetBytesLatency(latency_ts); 332 333 int frames_filled = source_callback_->OnMoreIOData( 334 input_bus_.get(), 335 output_bus_.get(), 336 AudioBuffersState(0, total_delay_bytes)); 337 338 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples); 339 340 return frames_filled; 341} 342 343uint32 CrasUnifiedStream::WriteAudio(size_t frames, 344 uint8* buffer, 345 const timespec* sample_ts) { 346 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); 347 348 // Determine latency and pass that on to the source. 349 timespec latency_ts = {0, 0}; 350 cras_client_calc_playback_latency(sample_ts, &latency_ts); 351 352 int frames_filled = source_callback_->OnMoreData( 353 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts))); 354 355 // Note: If this ever changes to output raw float the data must be clipped and 356 // sanitized since it may come from an untrusted source such as NaCl. 357 output_bus_->ToInterleaved( 358 frames_filled, bytes_per_frame_ / params_.channels(), buffer); 359 360 return frames_filled; 361} 362 363void CrasUnifiedStream::NotifyStreamError(int err) { 364 // This will remove the stream from the client. 365 if (source_callback_) 366 source_callback_->OnError(this); 367} 368 369} // namespace media 370