fix(cast): stop hoarding full-track buffers in the DLNA media server
register_audio inserted every cast track's complete bytes into the entries map and NOTHING ever removed them — not stop, not disconnect; only app shutdown dropped the server. A DLNA session therefore grew by one full track per auto-advance (issue #550: 50 MB at launch to 4.5 GB, never released). Three changes: - register_audio/register_file evict every other entry — only the track being cast is servable anyway. - stop_if_cast and disconnect clear the entries outright, so stopping actually releases the buffers. - MediaSource::Data is Arc-backed: the per-request entry clone no longer copies the whole track (it did, on every GET/HEAD/range request), and an in-flight response keeps its own Arc so eviction can never truncate an ongoing range request. Fixes #550 cc
This commit is contained in:
2 files changed
+36
-3
No files matched your search
@@ -48,7 +48,10 @@ struct MediaEntry {
|
||||
|
||||
#[derive(Clone)]
|
||||
enum MediaSource {
|
||||
Data(Vec<u8>),
|
||||
// Arc so cloning an entry out of the map for an in-flight request never
|
||||
// copies the whole track, and so eviction can't invalidate a response
|
||||
// that is still being served (#550).
|
||||
Data(std::sync::Arc<Vec<u8>>),
|
||||
File(PathBuf),
|
||||
}
|
||||
|
||||
@@ -150,6 +153,14 @@ impl MediaServer {
|
||||
}
|
||||
|
||||
/// Get base URL (e.g., "http://192.168.1.100:8080")
|
||||
/// Drop every registered entry (releases the full-track buffers). Called
|
||||
/// on cast stop/disconnect so track bytes don't outlive the session (#550).
|
||||
pub fn clear_entries(&self) {
|
||||
if let Ok(mut entries) = self.entries.lock() {
|
||||
entries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_url(&self) -> String {
|
||||
self.base_url.clone()
|
||||
}
|
||||
@@ -164,10 +175,16 @@ impl MediaServer {
|
||||
let entry = MediaEntry {
|
||||
content_type: content_type.to_string(),
|
||||
size: data.len() as u64,
|
||||
source: MediaSource::Data(data),
|
||||
source: MediaSource::Data(std::sync::Arc::new(data)),
|
||||
};
|
||||
|
||||
if let Ok(mut entries) = self.entries.lock() {
|
||||
// Only the track being cast is servable — evict everything else.
|
||||
// Entries held full track bytes and NOTHING ever removed them, so
|
||||
// a DLNA session grew by one full track per auto-advance until app
|
||||
// exit (#550: 50 MB -> 4.5 GB). An in-flight response keeps its
|
||||
// own Arc, so eviction never truncates an ongoing range request.
|
||||
entries.retain(|k, _| *k == id);
|
||||
entries.insert(id, entry);
|
||||
}
|
||||
|
||||
@@ -209,6 +226,9 @@ impl MediaServer {
|
||||
};
|
||||
|
||||
if let Ok(mut entries) = self.entries.lock() {
|
||||
// Same eviction rule as register_audio (#550): switching to a
|
||||
// local file must also release the previous track's bytes.
|
||||
entries.retain(|k, _| *k == id);
|
||||
entries.insert(id, entry);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user