Make readahead a bit more robust for codecs with different io speeds

custom
jacqueline 1 year ago
parent 9dc8f5646c
commit 19f0675b44
  1. 57
      src/audio/readahead_source.cpp

@ -42,43 +42,42 @@ ReadaheadSource::~ReadaheadSource() {
} }
auto ReadaheadSource::Read(cpp::span<std::byte> dest) -> ssize_t { auto ReadaheadSource::Read(cpp::span<std::byte> dest) -> ssize_t {
// Optismise for the most frequent case: the buffer already contains enough size_t bytes_written = 0;
// data for this call. // Fill the destination from our buffer, until either the buffer is drained
size_t bytes_read = // or the destination is full.
xStreamBufferReceive(buffer_, dest.data(), dest.size_bytes(), 0); while (!dest.empty() && (is_refilling_ || !xStreamBufferIsEmpty(buffer_))) {
size_t bytes_read = xStreamBufferReceive(buffer_, dest.data(),
dest.size_bytes(), portMAX_DELAY);
tell_ += bytes_read; tell_ += bytes_read;
if (bytes_read == dest.size_bytes()) { bytes_written += bytes_read;
return bytes_read;
}
dest = dest.subspan(bytes_read); dest = dest.subspan(bytes_read);
// Are we currently fetching more bytes?
ssize_t extra_bytes = 0;
if (!is_refilling_) {
// No! Pass through directly to the wrapped source for the fastest
// response.
extra_bytes = wrapped_->Read(dest);
} else {
// Yes! Wait for the refill to catch up, then try again.
is_refilling_.wait(true);
extra_bytes =
xStreamBufferReceive(buffer_, dest.data(), dest.size_bytes(), 0);
} }
// No need to check whether the dest buffer is actually filled, since at this // After the loop, we've either written everything that was asked for, or
// point we've read as many bytes as were available. // we're out of data.
if (!dest.empty()) {
// Out of data in the buffer. Finish using the wrapped stream.
size_t extra_bytes = wrapped_->Read(dest);
tell_ += extra_bytes; tell_ += extra_bytes;
bytes_read += extra_bytes; bytes_written += extra_bytes;
// Before returning, make sure the readahead task is kicked off again. // Check for EOF in the wrapped stream.
ESP_LOGI(kTag, "triggering readahead"); if (extra_bytes < dest.size_bytes()) {
return bytes_written;
}
}
// After this point, we're done writing to `dest`. It's either empty, or the
// underlying source is EOF.
// If we're here, then there is more data to be read from the wrapped stream.
// Ensure the readahead is running.
if (!is_refilling_ &&
xStreamBufferBytesAvailable(buffer_) < kBufferSize / 4) {
is_refilling_ = true; is_refilling_ = true;
std::function<void(void)> refill = [this]() { std::function<void(void)> refill = [this]() {
// Try to keep larger than most reasonable FAT sector sizes for more // Try to keep larger than most reasonable FAT sector sizes for more
// efficient disk reads. // efficient disk reads.
constexpr size_t kMaxSingleRead = 1024 * 64; constexpr size_t kMaxSingleRead = 1024 * 16;
std::byte working_buf[kMaxSingleRead]; std::byte working_buf[kMaxSingleRead];
for (;;) { for (;;) {
size_t bytes_to_read = std::min<size_t>( size_t bytes_to_read = std::min<size_t>(
@ -98,8 +97,9 @@ auto ReadaheadSource::Read(cpp::span<std::byte> dest) -> ssize_t {
is_refilling_.notify_all(); is_refilling_.notify_all();
}; };
worker_.Dispatch(refill); worker_.Dispatch(refill);
}
return bytes_read; return bytes_written;
} }
auto ReadaheadSource::CanSeek() -> bool { auto ReadaheadSource::CanSeek() -> bool {
@ -109,6 +109,7 @@ auto ReadaheadSource::CanSeek() -> bool {
auto ReadaheadSource::SeekTo(int64_t destination, SeekFrom from) -> void { auto ReadaheadSource::SeekTo(int64_t destination, SeekFrom from) -> void {
// Seeking blows away all of our prefetched data. To do this safely, we // Seeking blows away all of our prefetched data. To do this safely, we
// first need to wait for the refill task to finish. // first need to wait for the refill task to finish.
ESP_LOGI(kTag, "dropping readahead due to seek");
is_refilling_.wait(true); is_refilling_.wait(true);
// It's now safe to clear out the buffer. // It's now safe to clear out the buffer.
xStreamBufferReset(buffer_); xStreamBufferReset(buffer_);

Loading…
Cancel
Save