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