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

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

@ -42,64 +42,64 @@ 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(),
tell_ += bytes_read; dest.size_bytes(), portMAX_DELAY);
if (bytes_read == dest.size_bytes()) { tell_ += bytes_read;
return bytes_read; bytes_written += bytes_read;
dest = dest.subspan(bytes_read);
} }
dest = dest.subspan(bytes_read); // After the loop, we've either written everything that was asked for, or
// we're out of data.
// Are we currently fetching more bytes? if (!dest.empty()) {
ssize_t extra_bytes = 0; // Out of data in the buffer. Finish using the wrapped stream.
if (!is_refilling_) { size_t extra_bytes = wrapped_->Read(dest);
// No! Pass through directly to the wrapped source for the fastest tell_ += extra_bytes;
// response. bytes_written += extra_bytes;
extra_bytes = wrapped_->Read(dest);
} else { // Check for EOF in the wrapped stream.
// Yes! Wait for the refill to catch up, then try again. if (extra_bytes < dest.size_bytes()) {
is_refilling_.wait(true); return bytes_written;
extra_bytes = }
xStreamBufferReceive(buffer_, dest.data(), dest.size_bytes(), 0);
} }
// After this point, we're done writing to `dest`. It's either empty, or the
// No need to check whether the dest buffer is actually filled, since at this // underlying source is EOF.
// point we've read as many bytes as were available.
tell_ += extra_bytes; // If we're here, then there is more data to be read from the wrapped stream.
bytes_read += extra_bytes; // Ensure the readahead is running.
if (!is_refilling_ &&
// Before returning, make sure the readahead task is kicked off again. xStreamBufferBytesAvailable(buffer_) < kBufferSize / 4) {
ESP_LOGI(kTag, "triggering readahead"); 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 * 16;
constexpr size_t kMaxSingleRead = 1024 * 64; 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>( kMaxSingleRead, xStreamBufferSpacesAvailable(buffer_));
kMaxSingleRead, xStreamBufferSpacesAvailable(buffer_)); if (bytes_to_read == 0) {
if (bytes_to_read == 0) { break;
break; }
} size_t read = wrapped_->Read({working_buf, bytes_to_read});
size_t read = wrapped_->Read({working_buf, bytes_to_read}); if (read > 0) {
if (read > 0) { xStreamBufferSend(buffer_, working_buf, read, 0);
xStreamBufferSend(buffer_, working_buf, read, 0); }
if (read < bytes_to_read) {
break;
}
} }
if (read < bytes_to_read) { is_refilling_ = false;
break; is_refilling_.notify_all();
} };
} worker_.Dispatch(refill);
is_refilling_ = false; }
is_refilling_.notify_all();
};
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