cras_unified.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 // Prepare |audio_format| and |stream_params| for the stream we 172 // will create. 173 cras_audio_format* audio_format = cras_audio_format_create( 174 alsa_util::BitsToFormat(params_.bits_per_sample()), 175 params_.sample_rate(), 176 params_.channels()); 177 if (!audio_format) { 178 LOG(WARNING) << "Error setting up audio parameters."; 179 callback->OnError(this); 180 return; 181 } 182 183 cras_stream_params* stream_params = cras_client_unified_params_create( 184 stream_direction_, 185 params_.frames_per_buffer(), 186 CRAS_STREAM_TYPE_DEFAULT, 187 0, 188 this, 189 CrasUnifiedStream::UnifiedCallback, 190 CrasUnifiedStream::StreamError, 191 audio_format); 192 if (!stream_params) { 193 LOG(WARNING) << "Error setting up stream parameters."; 194 callback->OnError(this); 195 cras_audio_format_destroy(audio_format); 196 return; 197 } 198 199 // Before starting the stream, save the number of bytes in a frame for use in 200 // the callback. 201 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); 202 203 // Adding the stream will start the audio callbacks requesting data. 204 if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) { 205 LOG(WARNING) << "Failed to add the stream"; 206 callback->OnError(this); 207 cras_audio_format_destroy(audio_format); 208 cras_client_stream_params_destroy(stream_params); 209 return; 210 } 211 212 // Set initial volume. 213 cras_client_set_stream_volume(client_, stream_id_, volume_); 214 215 // Done with config params. 216 cras_audio_format_destroy(audio_format); 217 cras_client_stream_params_destroy(stream_params); 218 219 is_playing_ = true; 220} 221 222void CrasUnifiedStream::Stop() { 223 if (!client_) 224 return; 225 226 // Removing the stream from the client stops audio. 227 cras_client_rm_stream(client_, stream_id_); 228 229 is_playing_ = false; 230} 231 232void CrasUnifiedStream::SetVolume(double volume) { 233 if (!client_) 234 return; 235 volume_ = static_cast<float>(volume); 236 cras_client_set_stream_volume(client_, stream_id_, volume_); 237} 238 239void CrasUnifiedStream::GetVolume(double* volume) { 240 *volume = volume_; 241} 242 243uint32 CrasUnifiedStream::GetBytesLatency( 244 const struct timespec& latency_ts) { 245 uint32 latency_usec; 246 247 // Treat negative latency (if we are too slow to render) as 0. 248 if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) { 249 latency_usec = 0; 250 } else { 251 latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) + 252 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; 253 } 254 255 double frames_latency = 256 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; 257 258 return static_cast<unsigned int>(frames_latency * bytes_per_frame_); 259} 260 261// Static callback asking for samples. 262int CrasUnifiedStream::UnifiedCallback(cras_client* client, 263 cras_stream_id_t stream_id, 264 uint8* input_samples, 265 uint8* output_samples, 266 unsigned int frames, 267 const timespec* input_ts, 268 const timespec* output_ts, 269 void* arg) { 270 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); 271 return me->DispatchCallback(frames, 272 input_samples, 273 output_samples, 274 input_ts, 275 output_ts); 276} 277 278// Static callback for stream errors. 279int CrasUnifiedStream::StreamError(cras_client* client, 280 cras_stream_id_t stream_id, 281 int err, 282 void* arg) { 283 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); 284 me->NotifyStreamError(err); 285 return 0; 286} 287 288// Calls the appropriate rendering function for this type of stream. 289uint32 CrasUnifiedStream::DispatchCallback(size_t frames, 290 uint8* input_samples, 291 uint8* output_samples, 292 const timespec* input_ts, 293 const timespec* output_ts) { 294 switch (stream_direction_) { 295 case CRAS_STREAM_OUTPUT: 296 return WriteAudio(frames, output_samples, output_ts); 297 case CRAS_STREAM_INPUT: 298 NOTREACHED() << "CrasUnifiedStream doesn't support input streams."; 299 return 0; 300 case CRAS_STREAM_UNIFIED: 301 return ReadWriteAudio(frames, input_samples, output_samples, 302 input_ts, output_ts); 303 default: 304 break; 305 } 306 307 return 0; 308} 309 310// Note these are run from a real time thread, so don't waste cycles here. 311uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames, 312 uint8* input_samples, 313 uint8* output_samples, 314 const timespec* input_ts, 315 const timespec* output_ts) { 316 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); 317 DCHECK(source_callback_); 318 319 uint32 bytes_per_sample = bytes_per_frame_ / params_.channels(); 320 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample); 321 322 // Determine latency and pass that on to the source. We have the capture time 323 // of the first input sample and the playback time of the next audio sample 324 // passed from the audio server, add them together for total latency. 325 uint32 total_delay_bytes; 326 timespec latency_ts = {0, 0}; 327 cras_client_calc_capture_latency(input_ts, &latency_ts); 328 total_delay_bytes = GetBytesLatency(latency_ts); 329 cras_client_calc_playback_latency(output_ts, &latency_ts); 330 total_delay_bytes += GetBytesLatency(latency_ts); 331 332 int frames_filled = source_callback_->OnMoreIOData( 333 input_bus_.get(), 334 output_bus_.get(), 335 AudioBuffersState(0, total_delay_bytes)); 336 337 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples); 338 339 return frames_filled; 340} 341 342uint32 CrasUnifiedStream::WriteAudio(size_t frames, 343 uint8* buffer, 344 const timespec* sample_ts) { 345 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); 346 347 // Determine latency and pass that on to the source. 348 timespec latency_ts = {0, 0}; 349 cras_client_calc_playback_latency(sample_ts, &latency_ts); 350 351 int frames_filled = source_callback_->OnMoreData( 352 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts))); 353 354 // Note: If this ever changes to output raw float the data must be clipped and 355 // sanitized since it may come from an untrusted source such as NaCl. 356 output_bus_->ToInterleaved( 357 frames_filled, bytes_per_frame_ / params_.channels(), buffer); 358 359 return frames_filled; 360} 361 362void CrasUnifiedStream::NotifyStreamError(int err) { 363 // This will remove the stream from the client. 364 if (source_callback_) 365 source_callback_->OnError(this); 366} 367 368} // namespace media 369