summaryrefslogtreecommitdiffstats
path: root/media/audio/linux/alsa_output.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/audio/linux/alsa_output.cc')
-rw-r--r--media/audio/linux/alsa_output.cc55
1 files changed, 45 insertions, 10 deletions
diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc
index 47a9f56..9a07493 100644
--- a/media/audio/linux/alsa_output.cc
+++ b/media/audio/linux/alsa_output.cc
@@ -468,16 +468,25 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
// Request more data if we have capacity.
if (buffer_->forward_capacity() > buffer_->forward_bytes()) {
- // Before making a request to source for data. We need to determine the
+ // Before making a request to source for data we need to determine the
// delay (in bytes) for the requested data to be played.
- snd_pcm_sframes_t delay = buffer_->forward_bytes() * bytes_per_frame_ /
- bytes_per_output_frame_;
+
+ // Amount of data currently in the ALSA's buffer.
+ uint32 alsa_buffered_frames = (alsa_buffer_frames_ - GetAvailableFrames());
+
+ // |buffer_delay| includes our buffer and ALSA's buffer.
+ uint32 buffer_delay = alsa_buffered_frames * bytes_per_frame_ +
+ buffer_->forward_bytes() * bytes_per_frame_ / bytes_per_output_frame_;
+
+ uint32 hardware_delay = (GetCurrentDelay() - alsa_buffered_frames) *
+ bytes_per_frame_;
scoped_refptr<media::DataBuffer> packet =
new media::DataBuffer(packet_size_);
size_t packet_size =
- shared_data_.OnMoreData(this, packet->GetWritableData(),
- packet->GetBufferSize(), delay);
+ shared_data_.OnMoreData(
+ this, packet->GetWritableData(), packet->GetBufferSize(),
+ AudioBuffersState(buffer_delay, hardware_delay));
CHECK(packet_size <= packet->GetBufferSize()) <<
"Data source overran buffer.";
@@ -715,6 +724,33 @@ std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) {
return guessed_device;
}
+snd_pcm_sframes_t AlsaPcmOutputStream::GetCurrentDelay() {
+ snd_pcm_sframes_t delay = -1;
+
+ // Don't query ALSA's delay if we have underrun since it'll be jammed at
+ // some non-zero value and potentially even negative!
+ if (wrapper_->PcmState(playback_handle_) != SND_PCM_STATE_XRUN) {
+ int error = wrapper_->PcmDelay(playback_handle_, &delay);
+ if (error < 0) {
+ // Assume a delay of zero and attempt to recover the device.
+ delay = -1;
+ error = wrapper_->PcmRecover(playback_handle_,
+ error,
+ kPcmRecoverIsSilent);
+ if (error < 0) {
+ LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error);
+ }
+ }
+ }
+
+ // snd_pcm_delay() may not work in the beginning of the stream. In this case
+ // return delay of data we know currently is in the ALSA's buffer.
+ if (delay < 0)
+ delay = alsa_buffer_frames_ - GetAvailableFrames();
+
+ return delay;
+}
+
snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() {
DCHECK_EQ(message_loop_, MessageLoop::current());
@@ -879,13 +915,12 @@ void AlsaPcmOutputStream::SharedData::set_volume(float v) {
volume_ = v;
}
-uint32 AlsaPcmOutputStream::SharedData::OnMoreData(AudioOutputStream* stream,
- void* dest,
- uint32 max_size,
- uint32 pending_bytes) {
+uint32 AlsaPcmOutputStream::SharedData::OnMoreData(
+ AudioOutputStream* stream, uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) {
AutoLock l(lock_);
if (source_callback_) {
- return source_callback_->OnMoreData(stream, dest, max_size, pending_bytes);
+ return source_callback_->OnMoreData(stream, dest, max_size, buffers_state);
}
return 0;