| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2012 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/logging.h" |
| 10 #include "media/audio/audio_util.h" |
| 11 #include "media/audio/cras/audio_manager_cras.h" |
| 12 #include "media/audio/linux/alsa_util.h" |
| 13 |
| 14 namespace 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 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params, |
| 29 AudioManagerCras* manager) |
| 30 : client_(NULL), |
| 31 stream_id_(0), |
| 32 samples_per_packet_(params.frames_per_buffer()), |
| 33 bytes_per_frame_(0), |
| 34 frame_rate_(params.sample_rate()), |
| 35 output_channels_(params.channels()), |
| 36 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), |
| 37 is_playing_(false), |
| 38 volume_(1.0), |
| 39 manager_(manager), |
| 40 source_callback_(NULL), |
| 41 input_bus_(NULL), |
| 42 output_bus_(NULL) { |
| 43 int input_channels = params.input_channels(); |
| 44 |
| 45 // We must have a manager. |
| 46 DCHECK(manager_); |
| 47 // Must have at least one input or output. If there are both they must be the |
| 48 // same. |
| 49 DCHECK(output_channels_ || input_channels); |
| 50 if (output_channels_ && input_channels) |
| 51 DCHECK(output_channels_ && input_channels); |
| 52 |
| 53 if (input_channels) |
| 54 input_bus_ = AudioBus::Create(input_channels, |
| 55 params.frames_per_buffer()); |
| 56 if (output_channels_) |
| 57 output_bus_ = AudioBus::Create(params); |
| 58 |
| 59 if (output_channels_ && input_channels) |
| 60 stream_direction_ = CRAS_STREAM_UNIFIED; |
| 61 else if (output_channels_) |
| 62 stream_direction_ = CRAS_STREAM_OUTPUT; |
| 63 else |
| 64 NOTREACHED() << "Input only streams not supported by CrasUnifiedStream."; |
| 65 } |
| 66 |
| 67 CrasUnifiedStream::~CrasUnifiedStream() { |
| 68 DCHECK(!is_playing_); |
| 69 } |
| 70 |
| 71 bool CrasUnifiedStream::Open() { |
| 72 // Sanity check input values. |
| 73 if (frame_rate_ <= 0) { |
| 74 LOG(WARNING) << "Unsupported audio frequency."; |
| 75 return false; |
| 76 } |
| 77 |
| 78 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
| 79 LOG(WARNING) << "Unsupported pcm format"; |
| 80 return false; |
| 81 } |
| 82 |
| 83 // Create the client and connect to the CRAS server. |
| 84 int err = cras_client_create(&client_); |
| 85 if (err < 0) { |
| 86 LOG(WARNING) << "Couldn't create CRAS client.\n"; |
| 87 client_ = NULL; |
| 88 return false; |
| 89 } |
| 90 |
| 91 err = cras_client_connect(client_); |
| 92 if (err) { |
| 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 err = cras_client_run_thread(client_); |
| 101 if (err) { |
| 102 LOG(WARNING) << "Couldn't run CRAS client.\n"; |
| 103 cras_client_destroy(client_); |
| 104 client_ = NULL; |
| 105 return false; |
| 106 } |
| 107 |
| 108 return true; |
| 109 } |
| 110 |
| 111 void CrasUnifiedStream::Close() { |
| 112 if (client_) { |
| 113 cras_client_stop(client_); |
| 114 cras_client_destroy(client_); |
| 115 client_ = NULL; |
| 116 } |
| 117 |
| 118 // Signal to the manager that we're closed and can be removed. |
| 119 // Should be last call in the method as it deletes "this". |
| 120 manager_->ReleaseOutputStream(this); |
| 121 } |
| 122 |
| 123 void CrasUnifiedStream::Start(AudioSourceCallback* callback) { |
| 124 CHECK(callback); |
| 125 source_callback_ = callback; |
| 126 |
| 127 // Only start if we can enter the playing state. |
| 128 if (is_playing_) |
| 129 return; |
| 130 |
| 131 // Prepare |audio_format| and |stream_params| for the stream we |
| 132 // will create. |
| 133 cras_audio_format* audio_format = cras_audio_format_create( |
| 134 pcm_format_, |
| 135 frame_rate_, |
| 136 output_channels_); |
| 137 if (audio_format == NULL) { |
| 138 LOG(WARNING) << "Error setting up audio parameters."; |
| 139 callback->OnError(this, -ENOMEM); |
| 140 return; |
| 141 } |
| 142 |
| 143 cras_stream_params* stream_params = cras_client_unified_params_create( |
| 144 stream_direction_, |
| 145 samples_per_packet_, |
| 146 CRAS_STREAM_TYPE_DEFAULT, |
| 147 0, |
| 148 this, |
| 149 CrasUnifiedStream::UnifiedCallback, |
| 150 CrasUnifiedStream::StreamError, |
| 151 audio_format); |
| 152 if (stream_params == NULL) { |
| 153 LOG(WARNING) << "Error setting up stream parameters."; |
| 154 callback->OnError(this, -ENOMEM); |
| 155 cras_audio_format_destroy(audio_format); |
| 156 return; |
| 157 } |
| 158 |
| 159 // Before starting the stream, save the number of bytes in a frame for use in |
| 160 // the callback. |
| 161 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); |
| 162 |
| 163 // Adding the stream will start the audio callbacks requesting data. |
| 164 int err = cras_client_add_stream(client_, &stream_id_, stream_params); |
| 165 if (err < 0) { |
| 166 LOG(WARNING) << "Failed to add the stream"; |
| 167 callback->OnError(this, err); |
| 168 cras_audio_format_destroy(audio_format); |
| 169 cras_client_stream_params_destroy(stream_params); |
| 170 return; |
| 171 } |
| 172 |
| 173 // Set initial volume. |
| 174 cras_client_set_stream_volume(client_, stream_id_, volume_); |
| 175 |
| 176 // Done with config params. |
| 177 cras_audio_format_destroy(audio_format); |
| 178 cras_client_stream_params_destroy(stream_params); |
| 179 |
| 180 is_playing_ = true; |
| 181 } |
| 182 |
| 183 void CrasUnifiedStream::Stop() { |
| 184 if (!client_) |
| 185 return; |
| 186 |
| 187 // Removing the stream from the client stops audio. |
| 188 cras_client_rm_stream(client_, stream_id_); |
| 189 |
| 190 is_playing_ = false; |
| 191 } |
| 192 |
| 193 void CrasUnifiedStream::SetVolume(double volume) { |
| 194 if (!client_) |
| 195 return; |
| 196 volume_ = static_cast<float>(volume); |
| 197 cras_client_set_stream_volume(client_, stream_id_, volume_); |
| 198 } |
| 199 |
| 200 void CrasUnifiedStream::GetVolume(double* volume) { |
| 201 *volume = volume_; |
| 202 } |
| 203 |
| 204 unsigned int CrasUnifiedStream::GetBytesLatency( |
| 205 const struct timespec* latency_ts) { |
| 206 uint32 latency_usec; |
| 207 |
| 208 // Treat negative latency (if we are too slow to render) as 0. |
| 209 if (latency_ts->tv_sec < 0 || latency_ts->tv_nsec < 0) |
| 210 latency_usec = 0; |
| 211 else |
| 212 latency_usec = (latency_ts->tv_sec * base::Time::kMicrosecondsPerSecond) + |
| 213 latency_ts->tv_nsec / base::Time::kNanosecondsPerMicrosecond; |
| 214 |
| 215 double frames_latency = |
| 216 latency_usec * frame_rate_ / base::Time::kMicrosecondsPerSecond; |
| 217 |
| 218 return static_cast<unsigned int>(frames_latency * bytes_per_frame_); |
| 219 } |
| 220 |
| 221 // Static callback asking for samples. |
| 222 int CrasUnifiedStream::UnifiedCallback(cras_client* client, |
| 223 cras_stream_id_t stream_id, |
| 224 uint8* input_samples, |
| 225 uint8* output_samples, |
| 226 unsigned int frames, |
| 227 const timespec* input_ts, |
| 228 const timespec* output_ts, |
| 229 void* arg) { |
| 230 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); |
| 231 return me->DispatchCalback(frames, |
| 232 input_samples, |
| 233 output_samples, |
| 234 input_ts, |
| 235 output_ts); |
| 236 } |
| 237 |
| 238 // Static callback for stream errors. |
| 239 int CrasUnifiedStream::StreamError(cras_client* client, |
| 240 cras_stream_id_t stream_id, |
| 241 int err, |
| 242 void* arg) { |
| 243 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); |
| 244 me->NotifyStreamError(err); |
| 245 return 0; |
| 246 } |
| 247 |
| 248 // Calls the appropriate rendering funciton for this type of stream. |
| 249 uint32 CrasUnifiedStream::DispatchCalback(size_t frames, |
| 250 uint8* input_samples, |
| 251 uint8* output_samples, |
| 252 const timespec* input_ts, |
| 253 const timespec* output_ts) { |
| 254 switch (stream_direction_) { |
| 255 case CRAS_STREAM_OUTPUT: |
| 256 return WriteAudio(frames, output_samples, output_ts); |
| 257 case CRAS_STREAM_INPUT: |
| 258 NOTREACHED() << "CrasUnifiedStream doesn't support input streams."; |
| 259 return 0; |
| 260 case CRAS_STREAM_UNIFIED: |
| 261 return ReadWriteAudio(frames, input_samples, output_samples, |
| 262 input_ts, output_ts); |
| 263 } |
| 264 |
| 265 return 0; |
| 266 } |
| 267 |
| 268 // Note these are run from a real time thread, so don't waste cycles here. |
| 269 uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames, |
| 270 uint8* input_samples, |
| 271 uint8* output_samples, |
| 272 const timespec* input_ts, |
| 273 const timespec* output_ts) { |
| 274 unsigned int bytes_per_sample = bytes_per_frame_ / output_channels_; |
| 275 |
| 276 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); |
| 277 DCHECK(source_callback_); |
| 278 |
| 279 if (input_samples) |
| 280 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample); |
| 281 |
| 282 int frames_filled = source_callback_->OnMoreIOData( |
| 283 input_bus_.get(), output_bus_.get(), AudioBuffersState(0, 0)); |
| 284 |
| 285 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples); |
| 286 |
| 287 return frames_filled; |
| 288 } |
| 289 |
| 290 uint32 CrasUnifiedStream::WriteAudio(size_t frames, |
| 291 uint8* buffer, |
| 292 const timespec* sample_ts) { |
| 293 timespec latency_ts = {0, 0}; |
| 294 |
| 295 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); |
| 296 |
| 297 // Determine latency and pass that on to the source. |
| 298 cras_client_calc_playback_latency(sample_ts, &latency_ts); |
| 299 |
| 300 int frames_filled = source_callback_->OnMoreData( |
| 301 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(&latency_ts))); |
| 302 |
| 303 // Note: If this ever changes to output raw float the data must be clipped and |
| 304 // sanitized since it may come from an untrusted source such as NaCl. |
| 305 output_bus_->ToInterleaved( |
| 306 frames_filled, bytes_per_frame_ / output_channels_, buffer); |
| 307 |
| 308 return frames_filled; |
| 309 } |
| 310 |
| 311 void CrasUnifiedStream::NotifyStreamError(int err) { |
| 312 // This will remove the stream from the client. |
| 313 if (source_callback_) |
| 314 source_callback_->OnError(this, err); |
| 315 } |
| 316 |
| 317 } // namespace media |
| OLD | NEW |