| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // THREAD SAFETY | 5 // THREAD SAFETY |
| 6 // | 6 // |
| 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: | 7 // AlsaPcmOutputStream object is *not* thread-safe -- we assume that |
| 8 // | |
| 9 // client thread - creates the object and calls the public APIs. | 8 // client thread - creates the object and calls the public APIs. |
| 10 // message loop thread - executes all the internal tasks including querying | 9 // message loop thread - executes all the internal tasks including querying |
| 11 // the data source for more data, writing to the alsa device, and closing | 10 // the data source for more data, writing to the alsa device, and closing |
| 12 // the alsa device. It does *not* handle opening the device though. | 11 // the alsa device. |
| 13 // | 12 // is actually the same thread. |
| 14 // The class is designed so that most operations that read/modify the object's | |
| 15 // internal state are done on the message loop thread. The exception is data | |
| 16 // conatined in the |shared_data_| structure. Data in this structure needs to | |
| 17 // be accessed by both threads, and should only be accessed when the | |
| 18 // |shared_data_.lock_| is held. | |
| 19 // | |
| 20 // All member variables that are not in |shared_data_| are created/destroyed on | |
| 21 // the |message_loop_|. This allows safe access to them from any task posted to | |
| 22 // |message_loop_|. The values in |shared_data_| are considered to be read-only | |
| 23 // signals by tasks posted to |message_loop_| (see the SEMANTICS of | |
| 24 // |shared_data_| section below). Because of these two constraints, tasks can, | |
| 25 // and must, be coded to be safe in the face of a changing |shared_data_|. | |
| 26 // | |
| 27 // | |
| 28 // SEMANTICS OF |shared_data_| | |
| 29 // | |
| 30 // Though |shared_data_| is accessable by both threads, the code has been | |
| 31 // structured so that all mutations to |shared_data_| are only done in the | |
| 32 // client thread. The message loop thread only ever reads the shared data. | |
| 33 // | |
| 34 // This reduces the need for critical sections because the public API code can | |
| 35 // assume that no mutations occur to the |shared_data_| between queries. | |
| 36 // | |
| 37 // On the message loop side, tasks have been coded such that they can | |
| 38 // operate safely regardless of when state changes happen to |shared_data_|. | |
| 39 // Code that is sensitive to the timing of state changes are delegated to the | |
| 40 // |shared_data_| object so they can executed while holding | |
| 41 // |shared_data_.lock_|. | |
| 42 // | 13 // |
| 43 // | 14 // |
| 44 // SEMANTICS OF CloseTask() | 15 // SEMANTICS OF CloseTask() |
| 45 // | 16 // |
| 46 // The CloseTask() is responsible for cleaning up any resources that were | 17 // The CloseTask() is responsible for cleaning up any resources that were |
| 47 // acquired after a successful Open(). After a CloseTask() has executed, | 18 // acquired after a successful Open(). CloseTask() would revoke any |
| 48 // scheduling of reads should stop. Currently scheduled tasks may run, but | 19 // scheduled outstanding runnable methods. |
| 49 // they should not attempt to access any of the internal structures beyond | |
| 50 // querying the |stop_stream_| flag and no-oping themselves. This will | |
| 51 // guarantee that eventually no more tasks will be posted into the message | |
| 52 // loop, and the AlsaPcmOutputStream will be able to delete itself. | |
| 53 // | 20 // |
| 54 // | 21 // |
| 55 // SEMANTICS OF ERROR STATES | 22 // SEMANTICS OF ERROR STATES |
| 56 // | 23 // |
| 57 // The object has two distinct error states: |shared_data_.state_| == kInError | 24 // The object has two distinct error states: |state_| == kInError |
| 58 // and |stop_stream_|. The |shared_data_.state_| state is only settable | 25 // and |stop_stream_|. The |stop_stream_| variable is used to indicate |
| 59 // by the client thread, and thus cannot be used to signal when the ALSA device | |
| 60 // fails due to a hardware (or other low-level) event. The |stop_stream_| | |
| 61 // variable is only accessed by the message loop thread; it is used to indicate | |
| 62 // that the playback_handle should no longer be used either because of a | 26 // that the playback_handle should no longer be used either because of a |
| 63 // hardware/low-level event, or because the CloseTask() has been run. | 27 // hardware/low-level event. |
| 64 // | 28 // |
| 65 // When |shared_data_.state_| == kInError, all public API functions will fail | 29 // When |state_| == kInError, all public API functions will fail |
| 66 // with an error (Start() will call the OnError() function on the callback | 30 // with an error (Start() will call the OnError() function on the callback |
| 67 // immediately), or no-op themselves with the exception of Close(). Even if an | 31 // immediately), or no-op themselves with the exception of Close(). Even if an |
| 68 // error state has been entered, if Open() has previously returned successfully, | 32 // error state has been entered, if Open() has previously returned successfully, |
| 69 // Close() must be called to cleanup the ALSA devices and release resources. | 33 // Close() must be called to cleanup the ALSA devices and release resources. |
| 70 // | 34 // |
| 71 // When |stop_stream_| is set, no more commands will be made against the | 35 // When |stop_stream_| is set, no more commands will be made against the |
| 72 // ALSA device, and playback will effectively stop. From the client's point of | 36 // ALSA device, and playback will effectively stop. From the client's point of |
| 73 // view, it will seem that the device has just clogged and stopped requesting | 37 // view, it will seem that the device has just clogged and stopped requesting |
| 74 // data. | 38 // data. |
| 75 | 39 |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 // Since we expect to only be able to wake up with a resolution of | 175 // Since we expect to only be able to wake up with a resolution of |
| 212 // kSleepErrorMilliseconds, double that for our minimum required latency. | 176 // kSleepErrorMilliseconds, double that for our minimum required latency. |
| 213 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = | 177 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = |
| 214 kSleepErrorMilliseconds * 2 * 1000; | 178 kSleepErrorMilliseconds * 2 * 1000; |
| 215 | 179 |
| 216 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 180 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
| 217 AudioParameters params, | 181 AudioParameters params, |
| 218 AlsaWrapper* wrapper, | 182 AlsaWrapper* wrapper, |
| 219 AudioManagerLinux* manager, | 183 AudioManagerLinux* manager, |
| 220 MessageLoop* message_loop) | 184 MessageLoop* message_loop) |
| 221 : shared_data_(MessageLoop::current()), | 185 : requested_device_name_(device_name), |
| 222 requested_device_name_(device_name), | |
| 223 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), | 186 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), |
| 224 channels_(params.channels), | 187 channels_(params.channels), |
| 225 sample_rate_(params.sample_rate), | 188 sample_rate_(params.sample_rate), |
| 226 bytes_per_sample_(params.bits_per_sample / 8), | 189 bytes_per_sample_(params.bits_per_sample / 8), |
| 227 bytes_per_frame_(channels_ * params.bits_per_sample / 8), | 190 bytes_per_frame_(channels_ * params.bits_per_sample / 8), |
| 228 should_downmix_(false), | 191 should_downmix_(false), |
| 229 packet_size_(params.GetPacketSize()), | 192 packet_size_(params.GetPacketSize()), |
| 230 micros_per_packet_(FramesToMicros( | 193 micros_per_packet_(FramesToMicros( |
| 231 params.samples_per_packet, sample_rate_)), | 194 params.samples_per_packet, sample_rate_)), |
| 232 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, | 195 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
| 233 micros_per_packet_ * 2)), | 196 micros_per_packet_ * 2)), |
| 234 bytes_per_output_frame_(bytes_per_frame_), | 197 bytes_per_output_frame_(bytes_per_frame_), |
| 235 alsa_buffer_frames_(0), | 198 alsa_buffer_frames_(0), |
| 236 stop_stream_(false), | 199 stop_stream_(false), |
| 237 wrapper_(wrapper), | 200 wrapper_(wrapper), |
| 238 manager_(manager), | 201 manager_(manager), |
| 239 playback_handle_(NULL), | 202 playback_handle_(NULL), |
| 240 frames_per_packet_(packet_size_ / bytes_per_frame_), | 203 frames_per_packet_(packet_size_ / bytes_per_frame_), |
| 241 client_thread_loop_(MessageLoop::current()), | 204 message_loop_(message_loop), |
| 242 message_loop_(message_loop) { | 205 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), |
| 206 state_(kCreated), |
| 207 volume_(1.0f), |
| 208 source_callback_(NULL) { |
| 209 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 243 | 210 |
| 244 // Sanity check input values. | 211 // Sanity check input values. |
| 245 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { | 212 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { |
| 246 LOG(WARNING) << "Unsupported audio frequency."; | 213 LOG(WARNING) << "Unsupported audio frequency."; |
| 247 shared_data_.TransitionTo(kInError); | 214 TransitionTo(kInError); |
| 248 } | 215 } |
| 249 | 216 |
| 250 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && | 217 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && |
| 251 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { | 218 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { |
| 252 LOG(WARNING) << "Unsupported audio format"; | 219 LOG(WARNING) << "Unsupported audio format"; |
| 253 shared_data_.TransitionTo(kInError); | 220 TransitionTo(kInError); |
| 254 } | 221 } |
| 255 | 222 |
| 256 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 223 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
| 257 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; | 224 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; |
| 258 shared_data_.TransitionTo(kInError); | 225 TransitionTo(kInError); |
| 259 } | 226 } |
| 260 } | 227 } |
| 261 | 228 |
| 262 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 229 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
| 263 InternalState state = shared_data_.state(); | 230 InternalState current_state = state(); |
| 264 DCHECK(state == kCreated || state == kIsClosed || state == kInError); | 231 DCHECK(current_state == kCreated || |
| 232 current_state == kIsClosed || |
| 233 current_state == kInError); |
| 265 | 234 |
| 266 // TODO(ajwong): Ensure that CloseTask has been called and the | 235 // TODO(ajwong): Ensure that CloseTask has been called and the |
| 267 // playback handle released by DCHECKing that playback_handle_ is NULL. | 236 // playback handle released by DCHECKing that playback_handle_ is NULL. |
| 268 // Currently, because of Bug 18217, there is a race condition on destruction | 237 // Currently, because of Bug 18217, there is a race condition on destruction |
| 269 // where the stream is not always stopped and closed, causing this to fail. | 238 // where the stream is not always stopped and closed, causing this to fail. |
| 270 } | 239 } |
| 271 | 240 |
| 272 bool AlsaPcmOutputStream::Open() { | 241 bool AlsaPcmOutputStream::Open() { |
| 273 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 242 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 274 | 243 |
| 275 if (shared_data_.state() == kInError) { | 244 if (state() == kInError) { |
| 276 return false; | 245 return false; |
| 277 } | 246 } |
| 278 | 247 |
| 279 if (!shared_data_.CanTransitionTo(kIsOpened)) { | 248 if (!CanTransitionTo(kIsOpened)) { |
| 280 NOTREACHED() << "Invalid state: " << shared_data_.state(); | 249 NOTREACHED() << "Invalid state: " << state(); |
| 281 return false; | 250 return false; |
| 282 } | 251 } |
| 283 | 252 |
| 284 // We do not need to check if the transition was successful because | 253 // We do not need to check if the transition was successful because |
| 285 // CanTransitionTo() was checked above, and it is assumed that this | 254 // CanTransitionTo() was checked above, and it is assumed that this |
| 286 // object's public API is only called on one thread so the state cannot | 255 // object's public API is only called on one thread so the state cannot |
| 287 // transition out from under us. | 256 // transition out from under us. |
| 288 shared_data_.TransitionTo(kIsOpened); | 257 TransitionTo(kIsOpened); |
| 289 message_loop_->PostTask( | 258 message_loop_->PostTask( |
| 290 FROM_HERE, | 259 FROM_HERE, |
| 291 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask)); | 260 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::OpenTask)); |
| 292 | 261 |
| 293 return true; | 262 return true; |
| 294 } | 263 } |
| 295 | 264 |
| 296 void AlsaPcmOutputStream::Close() { | 265 void AlsaPcmOutputStream::Close() { |
| 297 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 266 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 298 | 267 |
| 299 // Sanity check that the transition occurs correctly. It is safe to | 268 // Sanity check that the transition occurs correctly. It is safe to |
| 300 // continue anyways because all operations for closing are idempotent. | 269 // continue anyways because all operations for closing are idempotent. |
| 301 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { | 270 if (TransitionTo(kIsClosed) != kIsClosed) { |
| 302 NOTREACHED() << "Unable to transition Closed."; | 271 NOTREACHED() << "Unable to transition Closed."; |
| 303 } | 272 } |
| 304 | 273 |
| 305 message_loop_->PostTask( | 274 message_loop_->PostTask( |
| 306 FROM_HERE, | 275 FROM_HERE, |
| 307 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); | 276 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::CloseTask)); |
| 308 | |
| 309 // Signal to the manager that we're closed and can be removed. Since | |
| 310 // we just posted a CloseTask to the message loop, we won't be deleted | |
| 311 // immediately, but it will happen soon afterwards. | |
| 312 manager()->ReleaseOutputStream(this); | |
| 313 } | 277 } |
| 314 | 278 |
| 315 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { | 279 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { |
| 316 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 280 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 317 | 281 |
| 318 CHECK(callback); | 282 CHECK(callback); |
| 319 | 283 |
| 320 shared_data_.set_source_callback(callback); | 284 set_source_callback(callback); |
| 321 | 285 |
| 322 // Only post the task if we can enter the playing state. | 286 // Only post the task if we can enter the playing state. |
| 323 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { | 287 if (TransitionTo(kIsPlaying) == kIsPlaying) { |
| 324 message_loop_->PostTask( | 288 message_loop_->PostTask( |
| 325 FROM_HERE, | 289 FROM_HERE, |
| 326 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); | 290 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::StartTask)); |
| 327 } | 291 } |
| 328 } | 292 } |
| 329 | 293 |
| 330 void AlsaPcmOutputStream::Stop() { | 294 void AlsaPcmOutputStream::Stop() { |
| 331 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 295 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 332 | 296 |
| 333 // Reset the callback, so that it is not called anymore. | 297 // Reset the callback, so that it is not called anymore. |
| 334 shared_data_.set_source_callback(NULL); | 298 set_source_callback(NULL); |
| 335 | 299 |
| 336 shared_data_.TransitionTo(kIsStopped); | 300 TransitionTo(kIsStopped); |
| 337 } | 301 } |
| 338 | 302 |
| 339 void AlsaPcmOutputStream::SetVolume(double volume) { | 303 void AlsaPcmOutputStream::SetVolume(double volume) { |
| 340 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 304 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 341 | 305 |
| 342 shared_data_.set_volume(static_cast<float>(volume)); | 306 volume_ = static_cast<float>(volume); |
| 343 } | 307 } |
| 344 | 308 |
| 345 void AlsaPcmOutputStream::GetVolume(double* volume) { | 309 void AlsaPcmOutputStream::GetVolume(double* volume) { |
| 346 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 310 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 347 | 311 |
| 348 *volume = shared_data_.volume(); | 312 *volume = volume_; |
| 349 } | 313 } |
| 350 | 314 |
| 351 void AlsaPcmOutputStream::OpenTask() { | 315 void AlsaPcmOutputStream::OpenTask() { |
| 352 DCHECK_EQ(message_loop_, MessageLoop::current()); | 316 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 353 | 317 |
| 354 // Try to open the device. | 318 // Try to open the device. |
| 355 if (requested_device_name_ == kAutoSelectDevice) { | 319 if (requested_device_name_ == kAutoSelectDevice) { |
| 356 playback_handle_ = AutoSelectDevice(latency_micros_); | 320 playback_handle_ = AutoSelectDevice(latency_micros_); |
| 357 if (playback_handle_) | 321 if (playback_handle_) |
| 358 VLOG(1) << "Auto-selected device: " << device_name_; | 322 VLOG(1) << "Auto-selected device: " << device_name_; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 389 } | 353 } |
| 390 } | 354 } |
| 391 | 355 |
| 392 void AlsaPcmOutputStream::StartTask() { | 356 void AlsaPcmOutputStream::StartTask() { |
| 393 DCHECK_EQ(message_loop_, MessageLoop::current()); | 357 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 394 | 358 |
| 395 if (stop_stream_) { | 359 if (stop_stream_) { |
| 396 return; | 360 return; |
| 397 } | 361 } |
| 398 | 362 |
| 399 if (shared_data_.state() != kIsPlaying) { | 363 if (state() != kIsPlaying) { |
| 400 return; | 364 return; |
| 401 } | 365 } |
| 402 | 366 |
| 403 // Before starting, the buffer might have audio from previous user of this | 367 // Before starting, the buffer might have audio from previous user of this |
| 404 // device. | 368 // device. |
| 405 buffer_->Clear(); | 369 buffer_->Clear(); |
| 406 | 370 |
| 407 // When starting again, drop all packets in the device and prepare it again | 371 // When starting again, drop all packets in the device and prepare it again |
| 408 // incase we are restarting from a pause state and need to flush old data. | 372 // incase we are restarting from a pause state and need to flush old data. |
| 409 int error = wrapper_->PcmDrop(playback_handle_); | 373 int error = wrapper_->PcmDrop(playback_handle_); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 421 << wrapper_->PcmName(playback_handle_) << "): " | 385 << wrapper_->PcmName(playback_handle_) << "): " |
| 422 << wrapper_->StrError(error); | 386 << wrapper_->StrError(error); |
| 423 stop_stream_ = true; | 387 stop_stream_ = true; |
| 424 return; | 388 return; |
| 425 } | 389 } |
| 426 | 390 |
| 427 ScheduleNextWrite(false); | 391 ScheduleNextWrite(false); |
| 428 } | 392 } |
| 429 | 393 |
| 430 void AlsaPcmOutputStream::CloseTask() { | 394 void AlsaPcmOutputStream::CloseTask() { |
| 431 // NOTE: Keep this function idempotent to handle errors that might cause | |
| 432 // multiple CloseTasks to be posted. | |
| 433 DCHECK_EQ(message_loop_, MessageLoop::current()); | 395 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 434 | 396 |
| 435 // Shutdown the audio device. | 397 // Shutdown the audio device. |
| 436 if (playback_handle_ && | 398 if (playback_handle_ && |
| 437 alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { | 399 alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { |
| 438 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | 400 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
| 439 } | 401 } |
| 440 playback_handle_ = NULL; | 402 playback_handle_ = NULL; |
| 441 | 403 |
| 442 // Release the buffer. | 404 // Release the buffer. |
| 443 buffer_.reset(); | 405 buffer_.reset(); |
| 444 | 406 |
| 445 // Signal anything that might already be scheduled to stop. | 407 // Signal anything that might already be scheduled to stop. |
| 446 stop_stream_ = true; | 408 stop_stream_ = true; // Not necessary in production, but unit tests |
| 409 // uses the flag to verify that stream was closed. |
| 410 method_factory_.RevokeAll(); |
| 411 |
| 412 // Signal to the manager that we're closed and can be removed. |
| 413 // Should be last call in the method as it deletes "this". |
| 414 manager_->ReleaseOutputStream(this); |
| 447 } | 415 } |
| 448 | 416 |
| 449 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 417 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
| 450 DCHECK_EQ(message_loop_, MessageLoop::current()); | 418 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 451 | 419 |
| 452 // If stopped, simulate a 0-lengthed packet. | 420 // If stopped, simulate a 0-lengthed packet. |
| 453 if (stop_stream_) { | 421 if (stop_stream_) { |
| 454 buffer_->Clear(); | 422 buffer_->Clear(); |
| 455 *source_exhausted = true; | 423 *source_exhausted = true; |
| 456 return; | 424 return; |
| 457 } | 425 } |
| 458 | 426 |
| 459 *source_exhausted = false; | 427 *source_exhausted = false; |
| 460 | 428 |
| 461 // Request more data if we have capacity. | 429 // Request more data if we have capacity. |
| 462 if (buffer_->forward_capacity() > buffer_->forward_bytes()) { | 430 if (buffer_->forward_capacity() > buffer_->forward_bytes()) { |
| 463 // Before making a request to source for data we need to determine the | 431 // Before making a request to source for data we need to determine the |
| 464 // delay (in bytes) for the requested data to be played. | 432 // delay (in bytes) for the requested data to be played. |
| 465 | 433 |
| 466 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / | 434 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / |
| 467 bytes_per_output_frame_; | 435 bytes_per_output_frame_; |
| 468 | 436 |
| 469 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 437 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; |
| 470 | 438 |
| 471 scoped_refptr<media::DataBuffer> packet = | 439 scoped_refptr<media::DataBuffer> packet = |
| 472 new media::DataBuffer(packet_size_); | 440 new media::DataBuffer(packet_size_); |
| 473 size_t packet_size = | 441 size_t packet_size = RunDataCallback(packet->GetWritableData(), |
| 474 shared_data_.OnMoreData( | 442 packet->GetBufferSize(), |
| 475 this, packet->GetWritableData(), packet->GetBufferSize(), | 443 AudioBuffersState(buffer_delay, |
| 476 AudioBuffersState(buffer_delay, hardware_delay)); | 444 hardware_delay)); |
| 477 CHECK(packet_size <= packet->GetBufferSize()) << | 445 CHECK(packet_size <= packet->GetBufferSize()) << |
| 478 "Data source overran buffer."; | 446 "Data source overran buffer."; |
| 479 | 447 |
| 480 // This should not happen, but in case it does, drop any trailing bytes | 448 // This should not happen, but in case it does, drop any trailing bytes |
| 481 // that aren't large enough to make a frame. Without this, packet writing | 449 // that aren't large enough to make a frame. Without this, packet writing |
| 482 // may stall because the last few bytes in the packet may never get used by | 450 // may stall because the last few bytes in the packet may never get used by |
| 483 // WritePacket. | 451 // WritePacket. |
| 484 DCHECK(packet_size % bytes_per_frame_ == 0); | 452 DCHECK(packet_size % bytes_per_frame_ == 0); |
| 485 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; | 453 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; |
| 486 | 454 |
| 487 if (should_downmix_) { | 455 if (should_downmix_) { |
| 488 if (media::FoldChannels(packet->GetWritableData(), | 456 if (media::FoldChannels(packet->GetWritableData(), |
| 489 packet_size, | 457 packet_size, |
| 490 channels_, | 458 channels_, |
| 491 bytes_per_sample_, | 459 bytes_per_sample_, |
| 492 shared_data_.volume())) { | 460 volume_)) { |
| 493 // Adjust packet size for downmix. | 461 // Adjust packet size for downmix. |
| 494 packet_size = | 462 packet_size = |
| 495 packet_size / bytes_per_frame_ * bytes_per_output_frame_; | 463 packet_size / bytes_per_frame_ * bytes_per_output_frame_; |
| 496 } else { | 464 } else { |
| 497 LOG(ERROR) << "Folding failed"; | 465 LOG(ERROR) << "Folding failed"; |
| 498 } | 466 } |
| 499 } else { | 467 } else { |
| 500 // TODO(ajwong): Handle other channel orderings. | 468 // TODO(ajwong): Handle other channel orderings. |
| 501 | 469 |
| 502 // Handle channel order for 5.0 audio. | 470 // Handle channel order for 5.0 audio. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 518 Swizzle51Layout(packet->GetWritableData(), packet_size); | 486 Swizzle51Layout(packet->GetWritableData(), packet_size); |
| 519 } else if (bytes_per_sample_ == 4) { | 487 } else if (bytes_per_sample_ == 4) { |
| 520 Swizzle51Layout(packet->GetWritableData(), packet_size); | 488 Swizzle51Layout(packet->GetWritableData(), packet_size); |
| 521 } | 489 } |
| 522 } | 490 } |
| 523 | 491 |
| 524 media::AdjustVolume(packet->GetWritableData(), | 492 media::AdjustVolume(packet->GetWritableData(), |
| 525 packet_size, | 493 packet_size, |
| 526 channels_, | 494 channels_, |
| 527 bytes_per_sample_, | 495 bytes_per_sample_, |
| 528 shared_data_.volume()); | 496 volume_); |
| 529 } | 497 } |
| 530 | 498 |
| 531 if (packet_size > 0) { | 499 if (packet_size > 0) { |
| 532 packet->SetDataSize(packet_size); | 500 packet->SetDataSize(packet_size); |
| 533 // Add the packet to the buffer. | 501 // Add the packet to the buffer. |
| 534 buffer_->Append(packet); | 502 buffer_->Append(packet); |
| 535 } else { | 503 } else { |
| 536 *source_exhausted = true; | 504 *source_exhausted = true; |
| 537 } | 505 } |
| 538 } | 506 } |
| 539 } | 507 } |
| 540 | 508 |
| 541 void AlsaPcmOutputStream::WritePacket() { | 509 void AlsaPcmOutputStream::WritePacket() { |
| 542 DCHECK_EQ(message_loop_, MessageLoop::current()); | 510 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 543 | 511 |
| 544 // If the device is in error, just eat the bytes. | 512 // If the device is in error, just eat the bytes. |
| 545 if (stop_stream_) { | 513 if (stop_stream_) { |
| 546 buffer_->Clear(); | 514 buffer_->Clear(); |
| 547 return; | 515 return; |
| 548 } | 516 } |
| 549 | 517 |
| 550 if (shared_data_.state() == kIsStopped) { | 518 if (state() == kIsStopped) { |
| 551 return; | 519 return; |
| 552 } | 520 } |
| 553 | 521 |
| 554 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); | 522 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); |
| 555 | 523 |
| 556 const uint8* buffer_data; | 524 const uint8* buffer_data; |
| 557 size_t buffer_size; | 525 size_t buffer_size; |
| 558 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { | 526 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { |
| 559 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); | 527 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); |
| 560 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; | 528 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 572 frames_written, | 540 frames_written, |
| 573 kPcmRecoverIsSilent); | 541 kPcmRecoverIsSilent); |
| 574 } | 542 } |
| 575 | 543 |
| 576 if (frames_written < 0) { | 544 if (frames_written < 0) { |
| 577 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping | 545 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping |
| 578 // the pcm playback? | 546 // the pcm playback? |
| 579 if (frames_written != -EAGAIN) { | 547 if (frames_written != -EAGAIN) { |
| 580 LOG(ERROR) << "Failed to write to pcm device: " | 548 LOG(ERROR) << "Failed to write to pcm device: " |
| 581 << wrapper_->StrError(frames_written); | 549 << wrapper_->StrError(frames_written); |
| 582 shared_data_.OnError(this, frames_written); | 550 RunErrorCallback(frames_written); |
| 583 stop_stream_ = true; | 551 stop_stream_ = true; |
| 584 } | 552 } |
| 585 } else { | 553 } else { |
| 586 if (frames_written > frames) { | 554 if (frames_written > frames) { |
| 587 LOG(WARNING) | 555 LOG(WARNING) |
| 588 << "snd_pcm_writei() has written more frame that we asked."; | 556 << "snd_pcm_writei() has written more frame that we asked."; |
| 589 frames_written = frames; | 557 frames_written = frames; |
| 590 } | 558 } |
| 591 | 559 |
| 592 // Seek forward in the buffer after we've written some data to ALSA. | 560 // Seek forward in the buffer after we've written some data to ALSA. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 603 } | 571 } |
| 604 } | 572 } |
| 605 | 573 |
| 606 void AlsaPcmOutputStream::WriteTask() { | 574 void AlsaPcmOutputStream::WriteTask() { |
| 607 DCHECK_EQ(message_loop_, MessageLoop::current()); | 575 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 608 | 576 |
| 609 if (stop_stream_) { | 577 if (stop_stream_) { |
| 610 return; | 578 return; |
| 611 } | 579 } |
| 612 | 580 |
| 613 if (shared_data_.state() == kIsStopped) { | 581 if (state() == kIsStopped) { |
| 614 return; | 582 return; |
| 615 } | 583 } |
| 616 | 584 |
| 617 bool source_exhausted; | 585 bool source_exhausted; |
| 618 BufferPacket(&source_exhausted); | 586 BufferPacket(&source_exhausted); |
| 619 WritePacket(); | 587 WritePacket(); |
| 620 | 588 |
| 621 ScheduleNextWrite(source_exhausted); | 589 ScheduleNextWrite(source_exhausted); |
| 622 } | 590 } |
| 623 | 591 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 648 } else { | 616 } else { |
| 649 next_fill_time_ms -= kSleepErrorMilliseconds; | 617 next_fill_time_ms -= kSleepErrorMilliseconds; |
| 650 } | 618 } |
| 651 | 619 |
| 652 // Avoid busy looping if the data source is exhausted. | 620 // Avoid busy looping if the data source is exhausted. |
| 653 if (source_exhausted) { | 621 if (source_exhausted) { |
| 654 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); | 622 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); |
| 655 } | 623 } |
| 656 | 624 |
| 657 // Only schedule more reads/writes if we are still in the playing state. | 625 // Only schedule more reads/writes if we are still in the playing state. |
| 658 if (shared_data_.state() == kIsPlaying) { | 626 if (state() == kIsPlaying) { |
| 659 if (next_fill_time_ms == 0) { | 627 if (next_fill_time_ms == 0) { |
| 660 message_loop_->PostTask( | 628 message_loop_->PostTask( |
| 661 FROM_HERE, | 629 FROM_HERE, |
| 662 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask)); | 630 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask)); |
| 663 } else { | 631 } else { |
| 664 // TODO(ajwong): Measure the reliability of the delay interval. Use | 632 // TODO(ajwong): Measure the reliability of the delay interval. Use |
| 665 // base/metrics/histogram.h. | 633 // base/metrics/histogram.h. |
| 666 message_loop_->PostDelayedTask( | 634 message_loop_->PostDelayedTask( |
| 667 FROM_HERE, | 635 FROM_HERE, |
| 668 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask), | 636 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask), |
| 669 next_fill_time_ms); | 637 next_fill_time_ms); |
| 670 } | 638 } |
| 671 } | 639 } |
| 672 } | 640 } |
| 673 | 641 |
| 674 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { | 642 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { |
| 675 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 643 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; |
| 676 } | 644 } |
| 677 | 645 |
| 678 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { | 646 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 835 default_channels, sample_rate_, | 803 default_channels, sample_rate_, |
| 836 pcm_format_, latency)) != NULL) { | 804 pcm_format_, latency)) != NULL) { |
| 837 return handle; | 805 return handle; |
| 838 } | 806 } |
| 839 | 807 |
| 840 // Unable to open any device. | 808 // Unable to open any device. |
| 841 device_name_.clear(); | 809 device_name_.clear(); |
| 842 return NULL; | 810 return NULL; |
| 843 } | 811 } |
| 844 | 812 |
| 845 AudioManagerLinux* AlsaPcmOutputStream::manager() { | 813 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) { |
| 846 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | |
| 847 return manager_; | |
| 848 } | |
| 849 | |
| 850 AlsaPcmOutputStream::SharedData::SharedData( | |
| 851 MessageLoop* state_transition_loop) | |
| 852 : state_(kCreated), | |
| 853 volume_(1.0f), | |
| 854 source_callback_(NULL), | |
| 855 state_transition_loop_(state_transition_loop) { | |
| 856 } | |
| 857 | |
| 858 bool AlsaPcmOutputStream::SharedData::CanTransitionTo(InternalState to) { | |
| 859 base::AutoLock l(lock_); | |
| 860 return CanTransitionTo_Locked(to); | |
| 861 } | |
| 862 | |
| 863 bool AlsaPcmOutputStream::SharedData::CanTransitionTo_Locked( | |
| 864 InternalState to) { | |
| 865 lock_.AssertAcquired(); | |
| 866 | |
| 867 switch (state_) { | 814 switch (state_) { |
| 868 case kCreated: | 815 case kCreated: |
| 869 return to == kIsOpened || to == kIsClosed || to == kInError; | 816 return to == kIsOpened || to == kIsClosed || to == kInError; |
| 870 | 817 |
| 871 case kIsOpened: | 818 case kIsOpened: |
| 872 return to == kIsPlaying || to == kIsStopped || | 819 return to == kIsPlaying || to == kIsStopped || |
| 873 to == kIsClosed || to == kInError; | 820 to == kIsClosed || to == kInError; |
| 874 | 821 |
| 875 case kIsPlaying: | 822 case kIsPlaying: |
| 876 return to == kIsPlaying || to == kIsStopped || | 823 return to == kIsPlaying || to == kIsStopped || |
| 877 to == kIsClosed || to == kInError; | 824 to == kIsClosed || to == kInError; |
| 878 | 825 |
| 879 case kIsStopped: | 826 case kIsStopped: |
| 880 return to == kIsPlaying || to == kIsStopped || | 827 return to == kIsPlaying || to == kIsStopped || |
| 881 to == kIsClosed || to == kInError; | 828 to == kIsClosed || to == kInError; |
| 882 | 829 |
| 883 case kInError: | 830 case kInError: |
| 884 return to == kIsClosed || to == kInError; | 831 return to == kIsClosed || to == kInError; |
| 885 | 832 |
| 886 case kIsClosed: | 833 case kIsClosed: |
| 887 default: | 834 default: |
| 888 return false; | 835 return false; |
| 889 } | 836 } |
| 890 } | 837 } |
| 891 | 838 |
| 892 AlsaPcmOutputStream::InternalState | 839 AlsaPcmOutputStream::InternalState |
| 893 AlsaPcmOutputStream::SharedData::TransitionTo(InternalState to) { | 840 AlsaPcmOutputStream::TransitionTo(InternalState to) { |
| 894 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 841 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 895 | 842 |
| 896 base::AutoLock l(lock_); | 843 if (!CanTransitionTo(to)) { |
| 897 if (!CanTransitionTo_Locked(to)) { | |
| 898 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; | 844 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; |
| 899 state_ = kInError; | 845 state_ = kInError; |
| 900 } else { | 846 } else { |
| 901 state_ = to; | 847 state_ = to; |
| 902 } | 848 } |
| 903 return state_; | 849 return state_; |
| 904 } | 850 } |
| 905 | 851 |
| 906 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::SharedData::state() { | 852 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
| 907 base::AutoLock l(lock_); | |
| 908 return state_; | 853 return state_; |
| 909 } | 854 } |
| 910 | 855 |
| 911 float AlsaPcmOutputStream::SharedData::volume() { | 856 uint32 AlsaPcmOutputStream::RunDataCallback(uint8* dest, |
| 912 base::AutoLock l(lock_); | 857 uint32 max_size, |
| 913 return volume_; | 858 AudioBuffersState buffers_state) { |
| 914 } | |
| 915 | |
| 916 void AlsaPcmOutputStream::SharedData::set_volume(float v) { | |
| 917 base::AutoLock l(lock_); | |
| 918 volume_ = v; | |
| 919 } | |
| 920 | |
| 921 uint32 AlsaPcmOutputStream::SharedData::OnMoreData( | |
| 922 AudioOutputStream* stream, uint8* dest, uint32 max_size, | |
| 923 AudioBuffersState buffers_state) { | |
| 924 base::AutoLock l(lock_); | |
| 925 if (source_callback_) { | 859 if (source_callback_) { |
| 926 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); | 860 return source_callback_->OnMoreData(this, dest, max_size, buffers_state); |
| 927 } | 861 } |
| 928 | 862 |
| 929 return 0; | 863 return 0; |
| 930 } | 864 } |
| 931 | 865 |
| 932 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, | 866 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
| 933 int code) { | |
| 934 base::AutoLock l(lock_); | |
| 935 if (source_callback_) { | 867 if (source_callback_) { |
| 936 source_callback_->OnError(stream, code); | 868 source_callback_->OnError(this, code); |
| 937 } | 869 } |
| 938 } | 870 } |
| 939 | 871 |
| 940 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 872 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 941 // release ownership of the currently registered callback. | 873 // release ownership of the currently registered callback. |
| 942 void AlsaPcmOutputStream::SharedData::set_source_callback( | 874 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
| 943 AudioSourceCallback* callback) { | 875 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 944 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | |
| 945 base::AutoLock l(lock_); | |
| 946 source_callback_ = callback; | 876 source_callback_ = callback; |
| 947 } | 877 } |
| OLD | NEW |