Loading debian/changelog +8 −0 Original line number Diff line number Diff line mediadb (20260424+82) unstable; urgency=medium * Bulk-prefetch entire file (≤64 MB) in one parity round-trip before FFmpeg decode, instead of 52+ small range requests that each reconstruct the full parity group. Reduces preview render from ~30s to ~2s for typical images. -- Jan Koester <jan.koester@tuxist.de> Thu, 24 Apr 2026 00:00:00 +0200 mediadb (20260424+81) unstable; urgency=medium * Fix preview inflight coalescing: use predicate-based wait_until to prevent Loading src/preview.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ static std::string ff_err2str(int err) { struct PrefetchBuffer { static constexpr std::size_t BLOCK_SIZE = 64 * 1024; // 64 KB — small blocks for fast initial decode static constexpr std::size_t AHEAD_BLOCKS = 4; // 4 blocks ahead = 256 KB look-ahead // Files up to this size are fetched in a single cluster round-trip // instead of many small range requests (avoids repeated parity reconstruction). static constexpr std::size_t BULK_FETCH_LIMIT = 64ULL * 1024 * 1024; // 64 MB MediaBackendApi* db; ThreadPool* io_pool; // bounded I/O pool (separate from render pool) Loading @@ -32,6 +35,7 @@ struct PrefetchBuffer { std::uint64_t total_size; std::uint64_t pos = 0; bool premature_eof = false; // set when read returns empty before total_size std::vector<uint8_t> bulk_data_; // pre-fetched full file (empty if not used) // prefetch ring — holds in-flight async reads struct PendingBlock { Loading @@ -53,6 +57,16 @@ struct PrefetchBuffer { } } // Fetch the entire file in a single cluster round-trip. // Call once after setting up db/media_id/total_size. void bulk_prefetch() { if (total_size == 0 || total_size > BULK_FETCH_LIMIT) return; bulk_data_ = db->read_media_data(media_id); if (!bulk_data_.empty()) { total_size = bulk_data_.size(); // align with actual bytes } } static std::string make_key(const std::string& mid, std::uint64_t off, std::uint64_t len) { return "prefetch:" + mid + ":" + std::to_string(off) + ":" + std::to_string(len); } Loading Loading @@ -119,6 +133,15 @@ struct PrefetchBuffer { int read(uint8_t* buf, int want) { if (pos >= total_size) return AVERROR_EOF; // Fast path: serve from bulk-prefetched data if (!bulk_data_.empty()) { std::uint64_t avail = bulk_data_.size() - pos; std::uint64_t n = std::min<std::uint64_t>(want, avail); std::memcpy(buf, bulk_data_.data() + pos, n); pos += n; return static_cast<int>(n); } kick_prefetch(); // drop blocks entirely before pos (after a seek) Loading Loading @@ -825,6 +848,7 @@ std::optional<PreviewResult> FFmpegPreviewer::render_streaming(MediaBackendApi& prefetch_buf.cache = &cache; prefetch_buf.media_id = media_id; prefetch_buf.total_size = media_size; prefetch_buf.bulk_prefetch(); // single round-trip for files ≤ 64 MB constexpr size_t avio_buf_size = 32768; auto* avio_buf = static_cast<uint8_t*>(av_malloc(avio_buf_size)); Loading Loading
debian/changelog +8 −0 Original line number Diff line number Diff line mediadb (20260424+82) unstable; urgency=medium * Bulk-prefetch entire file (≤64 MB) in one parity round-trip before FFmpeg decode, instead of 52+ small range requests that each reconstruct the full parity group. Reduces preview render from ~30s to ~2s for typical images. -- Jan Koester <jan.koester@tuxist.de> Thu, 24 Apr 2026 00:00:00 +0200 mediadb (20260424+81) unstable; urgency=medium * Fix preview inflight coalescing: use predicate-based wait_until to prevent Loading
src/preview.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ static std::string ff_err2str(int err) { struct PrefetchBuffer { static constexpr std::size_t BLOCK_SIZE = 64 * 1024; // 64 KB — small blocks for fast initial decode static constexpr std::size_t AHEAD_BLOCKS = 4; // 4 blocks ahead = 256 KB look-ahead // Files up to this size are fetched in a single cluster round-trip // instead of many small range requests (avoids repeated parity reconstruction). static constexpr std::size_t BULK_FETCH_LIMIT = 64ULL * 1024 * 1024; // 64 MB MediaBackendApi* db; ThreadPool* io_pool; // bounded I/O pool (separate from render pool) Loading @@ -32,6 +35,7 @@ struct PrefetchBuffer { std::uint64_t total_size; std::uint64_t pos = 0; bool premature_eof = false; // set when read returns empty before total_size std::vector<uint8_t> bulk_data_; // pre-fetched full file (empty if not used) // prefetch ring — holds in-flight async reads struct PendingBlock { Loading @@ -53,6 +57,16 @@ struct PrefetchBuffer { } } // Fetch the entire file in a single cluster round-trip. // Call once after setting up db/media_id/total_size. void bulk_prefetch() { if (total_size == 0 || total_size > BULK_FETCH_LIMIT) return; bulk_data_ = db->read_media_data(media_id); if (!bulk_data_.empty()) { total_size = bulk_data_.size(); // align with actual bytes } } static std::string make_key(const std::string& mid, std::uint64_t off, std::uint64_t len) { return "prefetch:" + mid + ":" + std::to_string(off) + ":" + std::to_string(len); } Loading Loading @@ -119,6 +133,15 @@ struct PrefetchBuffer { int read(uint8_t* buf, int want) { if (pos >= total_size) return AVERROR_EOF; // Fast path: serve from bulk-prefetched data if (!bulk_data_.empty()) { std::uint64_t avail = bulk_data_.size() - pos; std::uint64_t n = std::min<std::uint64_t>(want, avail); std::memcpy(buf, bulk_data_.data() + pos, n); pos += n; return static_cast<int>(n); } kick_prefetch(); // drop blocks entirely before pos (after a seek) Loading Loading @@ -825,6 +848,7 @@ std::optional<PreviewResult> FFmpegPreviewer::render_streaming(MediaBackendApi& prefetch_buf.cache = &cache; prefetch_buf.media_id = media_id; prefetch_buf.total_size = media_size; prefetch_buf.bulk_prefetch(); // single round-trip for files ≤ 64 MB constexpr size_t avio_buf_size = 32768; auto* avio_buf = static_cast<uint8_t*>(av_malloc(avio_buf_size)); Loading