| OLD | NEW |
| (Empty) | |
| 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 |
| 15 namespace 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 |
| 80 CrasUnifiedStream::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 input_bus_(NULL), |
| 91 output_bus_(NULL), |
| 92 stream_direction_(CRAS_STREAM_OUTPUT) { |
| 93 DCHECK(manager_); |
| 94 DCHECK(params_.channels() > 0); |
| 95 |
| 96 // Must have at least one input or output. If there are both they must be the |
| 97 // same. |
| 98 int input_channels = params_.input_channels(); |
| 99 |
| 100 if (input_channels) { |
| 101 // A unified stream for input and output. |
| 102 DCHECK(params_.channels() == input_channels); |
| 103 stream_direction_ = CRAS_STREAM_UNIFIED; |
| 104 input_bus_ = AudioBus::Create(input_channels, |
| 105 params_.frames_per_buffer()); |
| 106 } |
| 107 |
| 108 output_bus_ = AudioBus::Create(params); |
| 109 } |
| 110 |
| 111 CrasUnifiedStream::~CrasUnifiedStream() { |
| 112 DCHECK(!is_playing_); |
| 113 } |
| 114 |
| 115 bool CrasUnifiedStream::Open() { |
| 116 // Sanity check input values. |
| 117 if (params_.sample_rate() <= 0) { |
| 118 LOG(WARNING) << "Unsupported audio frequency."; |
| 119 return false; |
| 120 } |
| 121 |
| 122 if (alsa_util::BitsToFormat(params_.bits_per_sample()) == |
| 123 SND_PCM_FORMAT_UNKNOWN) { |
| 124 LOG(WARNING) << "Unsupported pcm format"; |
| 125 return false; |
| 126 } |
| 127 |
| 128 // Create the client and connect to the CRAS server. |
| 129 if (cras_client_create(&client_)) { |
| 130 LOG(WARNING) << "Couldn't create CRAS client.\n"; |
| 131 client_ = NULL; |
| 132 return false; |
| 133 } |
| 134 |
| 135 if (cras_client_connect(client_)) { |
| 136 LOG(WARNING) << "Couldn't connect CRAS client.\n"; |
| 137 cras_client_destroy(client_); |
| 138 client_ = NULL; |
| 139 return false; |
| 140 } |
| 141 |
| 142 // Then start running the client. |
| 143 if (cras_client_run_thread(client_)) { |
| 144 LOG(WARNING) << "Couldn't run CRAS client.\n"; |
| 145 cras_client_destroy(client_); |
| 146 client_ = NULL; |
| 147 return false; |
| 148 } |
| 149 |
| 150 return true; |
| 151 } |
| 152 |
| 153 void CrasUnifiedStream::Close() { |
| 154 if (client_) { |
| 155 cras_client_stop(client_); |
| 156 cras_client_destroy(client_); |
| 157 client_ = NULL; |
| 158 } |
| 159 |
| 160 // Signal to the manager that we're closed and can be removed. |
| 161 // Should be last call in the method as it deletes "this". |
| 162 manager_->ReleaseOutputStream(this); |
| 163 } |
| 164 |
| 165 void CrasUnifiedStream::Start(AudioSourceCallback* callback) { |
| 166 CHECK(callback); |
| 167 source_callback_ = callback; |
| 168 |
| 169 // Only start if we can enter the playing state. |
| 170 if (is_playing_) |
| 171 return; |
| 172 |
| 173 LOG(ERROR) << "Unified Start"; |
| 174 // Prepare |audio_format| and |stream_params| for the stream we |
| 175 // will create. |
| 176 cras_audio_format* audio_format = cras_audio_format_create( |
| 177 alsa_util::BitsToFormat(params_.bits_per_sample()), |
| 178 params_.sample_rate(), |
| 179 params_.channels()); |
| 180 if (!audio_format) { |
| 181 LOG(WARNING) << "Error setting up audio parameters."; |
| 182 callback->OnError(this); |
| 183 return; |
| 184 } |
| 185 |
| 186 cras_stream_params* stream_params = cras_client_unified_params_create( |
| 187 stream_direction_, |
| 188 params_.frames_per_buffer(), |
| 189 CRAS_STREAM_TYPE_DEFAULT, |
| 190 0, |
| 191 this, |
| 192 CrasUnifiedStream::UnifiedCallback, |
| 193 CrasUnifiedStream::StreamError, |
| 194 audio_format); |
| 195 if (!stream_params) { |
| 196 LOG(WARNING) << "Error setting up stream parameters."; |
| 197 callback->OnError(this); |
| 198 cras_audio_format_destroy(audio_format); |
| 199 return; |
| 200 } |
| 201 |
| 202 // Before starting the stream, save the number of bytes in a frame for use in |
| 203 // the callback. |
| 204 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); |
| 205 |
| 206 // Adding the stream will start the audio callbacks requesting data. |
| 207 if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) { |
| 208 LOG(WARNING) << "Failed to add the stream"; |
| 209 callback->OnError(this); |
| 210 cras_audio_format_destroy(audio_format); |
| 211 cras_client_stream_params_destroy(stream_params); |
| 212 return; |
| 213 } |
| 214 |
| 215 // Set initial volume. |
| 216 cras_client_set_stream_volume(client_, stream_id_, volume_); |
| 217 |
| 218 // Done with config params. |
| 219 cras_audio_format_destroy(audio_format); |
| 220 cras_client_stream_params_destroy(stream_params); |
| 221 |
| 222 is_playing_ = true; |
| 223 } |
| 224 |
| 225 void CrasUnifiedStream::Stop() { |
| 226 if (!client_) |
| 227 return; |
| 228 |
| 229 // Removing the stream from the client stops audio. |
| 230 cras_client_rm_stream(client_, stream_id_); |
| 231 |
| 232 is_playing_ = false; |
| 233 } |
| 234 |
| 235 void CrasUnifiedStream::SetVolume(double volume) { |
| 236 if (!client_) |
| 237 return; |
| 238 volume_ = static_cast<float>(volume); |
| 239 cras_client_set_stream_volume(client_, stream_id_, volume_); |
| 240 } |
| 241 |
| 242 void CrasUnifiedStream::GetVolume(double* volume) { |
| 243 *volume = volume_; |
| 244 } |
| 245 |
| 246 uint32 CrasUnifiedStream::GetBytesLatency( |
| 247 const struct timespec& latency_ts) { |
| 248 uint32 latency_usec; |
| 249 |
| 250 // Treat negative latency (if we are too slow to render) as 0. |
| 251 if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) { |
| 252 latency_usec = 0; |
| 253 } else { |
| 254 latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) + |
| 255 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
| 256 } |
| 257 |
| 258 double frames_latency = |
| 259 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; |
| 260 |
| 261 return static_cast<unsigned int>(frames_latency * bytes_per_frame_); |
| 262 } |
| 263 |
| 264 // Static callback asking for samples. |
| 265 int CrasUnifiedStream::UnifiedCallback(cras_client* client, |
| 266 cras_stream_id_t stream_id, |
| 267 uint8* input_samples, |
| 268 uint8* output_samples, |
| 269 unsigned int frames, |
| 270 const timespec* input_ts, |
| 271 const timespec* output_ts, |
| 272 void* arg) { |
| 273 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); |
| 274 return me->DispatchCallback(frames, |
| 275 input_samples, |
| 276 output_samples, |
| 277 input_ts, |
| 278 output_ts); |
| 279 } |
| 280 |
| 281 // Static callback for stream errors. |
| 282 int CrasUnifiedStream::StreamError(cras_client* client, |
| 283 cras_stream_id_t stream_id, |
| 284 int err, |
| 285 void* arg) { |
| 286 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); |
| 287 me->NotifyStreamError(err); |
| 288 return 0; |
| 289 } |
| 290 |
| 291 // Calls the appropriate rendering function for this type of stream. |
| 292 uint32 CrasUnifiedStream::DispatchCallback(size_t frames, |
| 293 uint8* input_samples, |
| 294 uint8* output_samples, |
| 295 const timespec* input_ts, |
| 296 const timespec* output_ts) { |
| 297 switch (stream_direction_) { |
| 298 case CRAS_STREAM_OUTPUT: |
| 299 return WriteAudio(frames, output_samples, output_ts); |
| 300 case CRAS_STREAM_INPUT: |
| 301 NOTREACHED() << "CrasUnifiedStream doesn't support input streams."; |
| 302 return 0; |
| 303 case CRAS_STREAM_UNIFIED: |
| 304 return ReadWriteAudio(frames, input_samples, output_samples, |
| 305 input_ts, output_ts); |
| 306 } |
| 307 |
| 308 return 0; |
| 309 } |
| 310 |
| 311 // Note these are run from a real time thread, so don't waste cycles here. |
| 312 uint32 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 |
| 343 uint32 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 |
| 363 void 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 |
| OLD | NEW |