| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 // The AlsaPcmOutputStream object's internal state is accessed by two threads: |
| 8 // | 8 // |
| 9 // client thread - creates the object and calls the public APIs. | 9 // client thread - creates the object and calls the public APIs. |
| 10 // message loop thread - executes all the internal tasks including querying | 10 // message loop thread - executes all the internal tasks including querying |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 shared_data_.set_volume(static_cast<float>(volume)); | 369 shared_data_.set_volume(static_cast<float>(volume)); |
| 370 } | 370 } |
| 371 | 371 |
| 372 void AlsaPcmOutputStream::GetVolume(double* volume) { | 372 void AlsaPcmOutputStream::GetVolume(double* volume) { |
| 373 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 373 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 374 | 374 |
| 375 *volume = shared_data_.volume(); | 375 *volume = shared_data_.volume(); |
| 376 } | 376 } |
| 377 | 377 |
| 378 void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { | 378 void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { |
| 379 DCHECK_EQ(MessageLoop::current(), message_loop_); | 379 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 380 | 380 |
| 381 // Initialize the configuration variables. | 381 // Initialize the configuration variables. |
| 382 packet_size_ = packet_size; | 382 packet_size_ = packet_size; |
| 383 frames_per_packet_ = packet_size_ / bytes_per_frame_; | 383 frames_per_packet_ = packet_size_ / bytes_per_frame_; |
| 384 | 384 |
| 385 // Try to open the device. | 385 // Try to open the device. |
| 386 micros_per_packet_ = | 386 micros_per_packet_ = |
| 387 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); | 387 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); |
| 388 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros, | 388 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
| 389 micros_per_packet_ * 2); | 389 micros_per_packet_ * 2); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 415 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " | 415 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " |
| 416 << wrapper_->StrError(error); | 416 << wrapper_->StrError(error); |
| 417 alsa_buffer_frames_ = frames_per_packet_; | 417 alsa_buffer_frames_ = frames_per_packet_; |
| 418 } else { | 418 } else { |
| 419 alsa_buffer_frames_ = buffer_size; | 419 alsa_buffer_frames_ = buffer_size; |
| 420 } | 420 } |
| 421 } | 421 } |
| 422 } | 422 } |
| 423 | 423 |
| 424 void AlsaPcmOutputStream::StartTask() { | 424 void AlsaPcmOutputStream::StartTask() { |
| 425 DCHECK_EQ(MessageLoop::current(), message_loop_); | 425 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 426 | 426 |
| 427 if (stop_stream_) { | 427 if (stop_stream_) { |
| 428 return; | 428 return; |
| 429 } | 429 } |
| 430 | 430 |
| 431 // When starting again, drop all packets in the device and prepare it again | 431 // When starting again, drop all packets in the device and prepare it again |
| 432 // incase we are restarting from a pause state and need to flush old data. | 432 // incase we are restarting from a pause state and need to flush old data. |
| 433 int error = wrapper_->PcmDrop(playback_handle_); | 433 int error = wrapper_->PcmDrop(playback_handle_); |
| 434 if (error < 0 && error != -EAGAIN) { | 434 if (error < 0 && error != -EAGAIN) { |
| 435 LOG(ERROR) << "Failure clearing playback device (" | 435 LOG(ERROR) << "Failure clearing playback device (" |
| (...skipping 11 matching lines...) Expand all Loading... |
| 447 stop_stream_ = true; | 447 stop_stream_ = true; |
| 448 return; | 448 return; |
| 449 } | 449 } |
| 450 | 450 |
| 451 ScheduleNextWrite(false); | 451 ScheduleNextWrite(false); |
| 452 } | 452 } |
| 453 | 453 |
| 454 void AlsaPcmOutputStream::CloseTask() { | 454 void AlsaPcmOutputStream::CloseTask() { |
| 455 // NOTE: Keep this function idempotent to handle errors that might cause | 455 // NOTE: Keep this function idempotent to handle errors that might cause |
| 456 // multiple CloseTasks to be posted. | 456 // multiple CloseTasks to be posted. |
| 457 DCHECK_EQ(MessageLoop::current(), message_loop_); | 457 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 458 | 458 |
| 459 // Shutdown the audio device. | 459 // Shutdown the audio device. |
| 460 if (playback_handle_ && !CloseDevice(playback_handle_)) { | 460 if (playback_handle_ && !CloseDevice(playback_handle_)) { |
| 461 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | 461 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
| 462 } | 462 } |
| 463 playback_handle_ = NULL; | 463 playback_handle_ = NULL; |
| 464 | 464 |
| 465 // Release the buffer. | 465 // Release the buffer. |
| 466 buffer_.reset(); | 466 buffer_.reset(); |
| 467 | 467 |
| 468 // Signal anything that might already be scheduled to stop. | 468 // Signal anything that might already be scheduled to stop. |
| 469 stop_stream_ = true; | 469 stop_stream_ = true; |
| 470 } | 470 } |
| 471 | 471 |
| 472 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 472 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
| 473 DCHECK_EQ(MessageLoop::current(), message_loop_); | 473 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 474 | 474 |
| 475 // If stopped, simulate a 0-lengthed packet. | 475 // If stopped, simulate a 0-lengthed packet. |
| 476 if (stop_stream_) { | 476 if (stop_stream_) { |
| 477 buffer_->Clear(); | 477 buffer_->Clear(); |
| 478 *source_exhausted = true; | 478 *source_exhausted = true; |
| 479 return; | 479 return; |
| 480 } | 480 } |
| 481 | 481 |
| 482 *source_exhausted = false; | 482 *source_exhausted = false; |
| 483 | 483 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 551 packet->SetDataSize(packet_size); | 551 packet->SetDataSize(packet_size); |
| 552 // Add the packet to the buffer. | 552 // Add the packet to the buffer. |
| 553 buffer_->Append(packet); | 553 buffer_->Append(packet); |
| 554 } else { | 554 } else { |
| 555 *source_exhausted = true; | 555 *source_exhausted = true; |
| 556 } | 556 } |
| 557 } | 557 } |
| 558 } | 558 } |
| 559 | 559 |
| 560 void AlsaPcmOutputStream::WritePacket() { | 560 void AlsaPcmOutputStream::WritePacket() { |
| 561 DCHECK_EQ(MessageLoop::current(), message_loop_); | 561 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 562 | 562 |
| 563 // If the device is in error, just eat the bytes. | 563 // If the device is in error, just eat the bytes. |
| 564 if (stop_stream_) { | 564 if (stop_stream_) { |
| 565 buffer_->Clear(); | 565 buffer_->Clear(); |
| 566 return; | 566 return; |
| 567 } | 567 } |
| 568 | 568 |
| 569 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); | 569 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); |
| 570 | 570 |
| 571 const uint8* buffer_data; | 571 const uint8* buffer_data; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 604 frames_written = frames; | 604 frames_written = frames; |
| 605 } | 605 } |
| 606 | 606 |
| 607 // Seek forward in the buffer after we've written some data to ALSA. | 607 // Seek forward in the buffer after we've written some data to ALSA. |
| 608 buffer_->Seek(frames_written * bytes_per_output_frame_); | 608 buffer_->Seek(frames_written * bytes_per_output_frame_); |
| 609 } | 609 } |
| 610 } | 610 } |
| 611 } | 611 } |
| 612 | 612 |
| 613 void AlsaPcmOutputStream::WriteTask() { | 613 void AlsaPcmOutputStream::WriteTask() { |
| 614 DCHECK_EQ(MessageLoop::current(), message_loop_); | 614 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 615 | 615 |
| 616 if (stop_stream_) { | 616 if (stop_stream_) { |
| 617 return; | 617 return; |
| 618 } | 618 } |
| 619 | 619 |
| 620 bool source_exhausted; | 620 bool source_exhausted; |
| 621 BufferPacket(&source_exhausted); | 621 BufferPacket(&source_exhausted); |
| 622 WritePacket(); | 622 WritePacket(); |
| 623 | 623 |
| 624 ScheduleNextWrite(source_exhausted); | 624 ScheduleNextWrite(source_exhausted); |
| 625 } | 625 } |
| 626 | 626 |
| 627 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { | 627 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { |
| 628 DCHECK_EQ(MessageLoop::current(), message_loop_); | 628 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 629 | 629 |
| 630 if (stop_stream_) { | 630 if (stop_stream_) { |
| 631 return; | 631 return; |
| 632 } | 632 } |
| 633 | 633 |
| 634 // Next write is scheduled for the moment when half of the buffer is | 634 // Next write is scheduled for the moment when half of the buffer is |
| 635 // available. | 635 // available. |
| 636 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; | 636 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; |
| 637 uint32 available_frames = GetAvailableFrames(); | 637 uint32 available_frames = GetAvailableFrames(); |
| 638 uint32 next_fill_time_ms = 0; | 638 uint32 next_fill_time_ms = 0; |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 771 if (error < 0) { | 771 if (error < 0) { |
| 772 LOG(ERROR) << "Error closing audio device (" << name << "): " | 772 LOG(ERROR) << "Error closing audio device (" << name << "): " |
| 773 << wrapper_->StrError(error); | 773 << wrapper_->StrError(error); |
| 774 return false; | 774 return false; |
| 775 } | 775 } |
| 776 | 776 |
| 777 return true; | 777 return true; |
| 778 } | 778 } |
| 779 | 779 |
| 780 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { | 780 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { |
| 781 DCHECK_EQ(MessageLoop::current(), message_loop_); | 781 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 782 | 782 |
| 783 if (stop_stream_) { | 783 if (stop_stream_) { |
| 784 return 0; | 784 return 0; |
| 785 } | 785 } |
| 786 | 786 |
| 787 // Find the number of frames queued in the sound device. | 787 // Find the number of frames queued in the sound device. |
| 788 snd_pcm_sframes_t available_frames = | 788 snd_pcm_sframes_t available_frames = |
| 789 wrapper_->PcmAvailUpdate(playback_handle_); | 789 wrapper_->PcmAvailUpdate(playback_handle_); |
| 790 if (available_frames < 0) { | 790 if (available_frames < 0) { |
| 791 available_frames = wrapper_->PcmRecover(playback_handle_, | 791 available_frames = wrapper_->PcmRecover(playback_handle_, |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 959 } | 959 } |
| 960 | 960 |
| 961 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 961 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 962 // release ownership of the currently registered callback. | 962 // release ownership of the currently registered callback. |
| 963 void AlsaPcmOutputStream::SharedData::set_source_callback( | 963 void AlsaPcmOutputStream::SharedData::set_source_callback( |
| 964 AudioSourceCallback* callback) { | 964 AudioSourceCallback* callback) { |
| 965 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 965 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); |
| 966 AutoLock l(lock_); | 966 AutoLock l(lock_); |
| 967 source_callback_ = callback; | 967 source_callback_ = callback; |
| 968 } | 968 } |
| OLD | NEW |