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