mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-04 00:47:00 +01:00
feat(app,stream): storage and hardware-decoding settings
- the streaming cache's budget (1-16 GiB) is chosen in Settings, shows what it holds, and can be emptied; a lower budget evicts at once - hardware decoding can be turned off for a driver that shows green frames, from the next file - both live in desktop.json rather than the playback prefs shared with Android, whose bridge record would reset them on every save. A file that will not parse is never overwritten
This commit is contained in:
1 parent
935a985589
commit
a432a93cf9
9 files changed
+315
-5
No files matched your search
@@ -70,7 +70,8 @@ pub struct DiskStats {
|
||||
|
||||
pub(crate) struct DiskCache {
|
||||
root: PathBuf,
|
||||
budget: u64,
|
||||
/// Atomic so it can be changed under a running cache, from settings.
|
||||
budget: std::sync::atomic::AtomicU64,
|
||||
index: Mutex<Index>,
|
||||
}
|
||||
|
||||
@@ -115,7 +116,7 @@ impl DiskCache {
|
||||
|
||||
let cache = Self {
|
||||
root: config.root,
|
||||
budget: config.budget_bytes,
|
||||
budget: std::sync::atomic::AtomicU64::new(config.budget_bytes),
|
||||
index: Mutex::new(Index {
|
||||
lru,
|
||||
bytes,
|
||||
@@ -169,7 +170,7 @@ impl DiskCache {
|
||||
/// written is a slower app, not a broken one.
|
||||
pub(crate) async fn put(&self, key: &BlockKey, block: &[u8]) {
|
||||
let len = block.len() as u64;
|
||||
if len > self.budget {
|
||||
if len > self.budget() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,6 +191,30 @@ impl DiskCache {
|
||||
self.evict_to_budget().await;
|
||||
}
|
||||
|
||||
fn budget(&self) -> u64 {
|
||||
self.budget.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Change the budget, shrinking the cache to it at once if it is now over.
|
||||
pub(crate) async fn set_budget(&self, bytes: u64) {
|
||||
self.budget
|
||||
.store(bytes, std::sync::atomic::Ordering::Relaxed);
|
||||
self.evict_to_budget().await;
|
||||
}
|
||||
|
||||
/// Delete every entry. The cache carries on, empty, under the same budget.
|
||||
pub(crate) async fn clear(&self) {
|
||||
let doomed: Vec<PathBuf> = {
|
||||
let mut index = self.lock();
|
||||
let doomed = std::iter::from_fn(|| index.lru.pop_lru().map(|(path, _)| path)).collect();
|
||||
index.bytes = 0;
|
||||
doomed
|
||||
};
|
||||
for path in doomed {
|
||||
self.discard(&path).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn stats(&self) -> DiskStats {
|
||||
let index = self.lock();
|
||||
DiskStats {
|
||||
@@ -204,7 +229,7 @@ impl DiskCache {
|
||||
loop {
|
||||
let doomed = {
|
||||
let mut index = self.lock();
|
||||
if index.bytes <= self.budget {
|
||||
if index.bytes <= self.budget() {
|
||||
return;
|
||||
}
|
||||
match index.lru.pop_lru() {
|
||||
@@ -511,6 +536,31 @@ mod tests {
|
||||
std::fs::remove_dir_all(&root).ok();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_cleared_cache_is_empty_on_disk_and_in_its_index() {
|
||||
let root = temp_root("clear");
|
||||
let cache = DiskCache::open(DiskCacheConfig::new(&root)).await.unwrap();
|
||||
for index in 0..3 {
|
||||
cache.put(&key("a", "rev1", index), &[7u8; 100]).await;
|
||||
}
|
||||
cache.clear().await;
|
||||
assert_eq!(cache.stats().stored_bytes, 0);
|
||||
assert!(cache.get(&key("a", "rev1", 0)).await.is_none());
|
||||
let reopened = DiskCache::open(DiskCacheConfig::new(&root)).await.unwrap();
|
||||
assert_eq!(reopened.stats().entries, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn lowering_the_budget_shrinks_a_running_cache() {
|
||||
let root = temp_root("lower");
|
||||
let cache = DiskCache::open(DiskCacheConfig::new(&root)).await.unwrap();
|
||||
for index in 0..5 {
|
||||
cache.put(&key("a", "rev1", index), &[7u8; 100]).await;
|
||||
}
|
||||
cache.set_budget(250).await;
|
||||
assert!(cache.stats().stored_bytes <= 250);
|
||||
}
|
||||
|
||||
/// Reopening with a smaller budget must shrink the cache, not sit over it
|
||||
/// until enough new blocks arrive to trigger eviction.
|
||||
#[tokio::test]
|
||||
|
||||
@@ -200,6 +200,21 @@ impl StreamSource {
|
||||
pub fn disk_stats(&self) -> Option<crate::disk::DiskStats> {
|
||||
self.inner.disk.as_ref().map(|disk| disk.stats())
|
||||
}
|
||||
|
||||
/// Change the disk cache's budget, evicting at once if it is now over.
|
||||
pub async fn set_disk_budget(&self, bytes: u64) {
|
||||
if let Some(disk) = &self.inner.disk {
|
||||
disk.set_budget(bytes).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Empty the disk cache. Streams already open carry on; what they had
|
||||
/// cached is fetched again if it is wanted.
|
||||
pub async fn clear_disk_cache(&self) {
|
||||
if let Some(disk) = &self.inner.disk {
|
||||
disk.clear().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
|
||||
Reference in new issue
Block a user