Commit a84bd68c authored by Ted Meyer's avatar Ted Meyer Committed by Commit Bot

Fix for PA buffer size allocation too small

PA pa_stream_begin_write() allocates a maximum of 8184, which can cause
a check fail when the audio-buffer-size is greater than that. To fix
this, we just have to write as many as will fit in the allocated space,
allocate another space, and write the remainder.

BUG=740054

Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I4effc3a405c1bfe2727a688b237841470dc1efd4
Reviewed-on: https://chromium-review.googlesource.com/809334Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Commit-Queue: Ted Meyer <tmathmeyer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#522285}
parent 7a562bb8
...@@ -130,58 +130,66 @@ void PulseAudioOutputStream::Close() { ...@@ -130,58 +130,66 @@ void PulseAudioOutputStream::Close() {
void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
int bytes_remaining = requested_bytes; int bytes_remaining = requested_bytes;
while (bytes_remaining > 0) { while (bytes_remaining > 0) {
void* buffer = NULL; void* pa_buffer = nullptr;
size_t bytes_to_fill = params_.GetBytesPerBuffer(); size_t pa_buffer_size = params_.GetBytesPerBuffer();
CHECK_GE(pa_stream_begin_write(pa_stream_, &buffer, &bytes_to_fill), 0); CHECK_GE(pa_stream_begin_write(pa_stream_, &pa_buffer, &pa_buffer_size), 0);
CHECK_EQ(bytes_to_fill, static_cast<size_t>(params_.GetBytesPerBuffer()));
if (!source_callback_) {
// NOTE: |bytes_to_fill| may be larger than |requested_bytes| now, this is memset(pa_buffer, 0, pa_buffer_size);
// okay since pa_stream_begin_write() is the authoritative source on how pa_stream_write(pa_stream_, pa_buffer, pa_buffer_size, NULL, 0LL,
// much can be written. PA_SEEK_RELATIVE);
bytes_remaining -= pa_buffer_size;
int frames_filled = 0; continue;
if (source_callback_) { }
const base::TimeDelta delay = pulse::GetHardwareLatency(pa_stream_);
frames_filled = source_callback_->OnMoreData( size_t unwritten_frames_in_bus = audio_bus_->frames();
delay, base::TimeTicks::Now(), 0, audio_bus_.get()); size_t frames_filled = source_callback_->OnMoreData(
pulse::GetHardwareLatency(pa_stream_), base::TimeTicks::Now(), 0,
// Zero any unfilled data so it plays back as silence. audio_bus_.get());
if (frames_filled < audio_bus_->frames()) {
audio_bus_->ZeroFramesPartial(
frames_filled, audio_bus_->frames() - frames_filled);
}
audio_bus_->Scale(volume_); // Zero any unfilled data so it plays back as silence.
audio_bus_->ToInterleaved<Float32SampleTypeTraits>( if (frames_filled < unwritten_frames_in_bus) {
audio_bus_->frames(), reinterpret_cast<float*>(buffer)); audio_bus_->ZeroFramesPartial(frames_filled,
} else { unwritten_frames_in_bus - frames_filled);
memset(buffer, 0, bytes_to_fill);
} }
if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL, audio_bus_->Scale(volume_);
PA_SEEK_RELATIVE) < 0) {
if (source_callback_) { size_t frame_size = params_.GetBytesPerBuffer() / unwritten_frames_in_bus;
size_t frames_to_copy = pa_buffer_size / frame_size;
size_t frame_offset_in_bus = 0;
do {
// Grab frames and get the count.
frames_to_copy =
std::min(audio_bus_->frames() - frame_offset_in_bus, frames_to_copy);
audio_bus_->ToInterleavedPartial<Float32SampleTypeTraits>(
frame_offset_in_bus, frames_to_copy,
reinterpret_cast<float*>(pa_buffer));
frame_offset_in_bus += frames_to_copy;
unwritten_frames_in_bus -= frames_to_copy;
if (pa_stream_write(pa_stream_, pa_buffer, pa_buffer_size, NULL, 0LL,
PA_SEEK_RELATIVE) < 0) {
source_callback_->OnError(); source_callback_->OnError();
return;
} }
} bytes_remaining -= pa_buffer_size;
if (unwritten_frames_in_bus) {
// NOTE: As mentioned above, |bytes_remaining| may be negative after this. // Reset the buffer and the size:
bytes_remaining -= bytes_to_fill; // - If pa_buffer isn't nulled out, then it will get re-used, and
// there will be a race between PA reading and us writing.
// Despite telling Pulse to only request certain buffer sizes, it will not // - If we don't shrink the pa_buffer_size to a small value, we get
// always obey. In these cases we need to avoid back to back reads from // stuttering as the memory allocation can take far too long. This
// the renderer as it won't have time to complete the request. // also means that we will never get more than we want, and we
// // dont need to memset.
// We can't defer the callback as Pulse will never call us again until we've pa_buffer = nullptr;
// satisfied writing the requested number of bytes. pa_buffer_size = unwritten_frames_in_bus * frame_size;
// CHECK_GE(pa_stream_begin_write(pa_stream_, &pa_buffer, &pa_buffer_size),
// TODO(dalecurtis): It might be worth choosing the sleep duration based on 0);
// the hardware latency return above. Watch http://crbug.com/366433 to see frames_to_copy = pa_buffer_size / frame_size;
// if a more complicated wait process is necessary. We may also need to see }
// if a PostDelayedTask should be used here to avoid blocking the PulseAudio } while (unwritten_frames_in_bus);
// command thread.
if (source_callback_ && bytes_remaining > 0)
base::PlatformThread::Sleep(params_.GetBufferDuration() / 4);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment