Fix Cast teardown leaks, keyframe latch, and DLNA session lifecycle

Dropped frames never requested a keyframe, SessionEnded skipped ordered
stop (portal/TV/FFI leak, next start could abort), and a late end could
kill the following cast. Failed starts left PlatformClientPosix alive.
DLNA leaked its HTTP server and ignored portal EOS.

Also: start no longer blocks the daemon actor, IPC accept/request loops
stay up, HLS Range is clamped, LAN IP follows the renderer subnet, and
the picker closes before the portal dialog and handles Escape.
This commit is contained in:
Breadway 2026-08-16 14:15:56 +08:00
parent a80c49593d
commit 17abeed7ae
26 changed files with 861 additions and 222 deletions

View file

@ -6,7 +6,7 @@ use tokio::sync::mpsc;
use tracing::{debug, warn};
use super::device::DlnaDevice;
use super::AV_TRANSPORT;
use super::{AV_TRANSPORT, MEDIA_RENDERER};
/// How often to re-issue an SSDP search burst. Unlike mDNS (continuous
/// multicast browsing via `mdns-sd` in [`crate::discovery`]), SSDP has no
@ -69,7 +69,7 @@ impl DlnaDiscovery {
let mut known: HashMap<String, (DlnaDevice, u32)> = HashMap::new();
loop {
let search_target = SearchTarget::URN(AV_TRANSPORT);
let search_target = SearchTarget::URN(MEDIA_RENDERER);
match rupnp::discover(&search_target, SEARCH_TIMEOUT, None).await {
Ok(stream) => {
use futures_util::StreamExt;
@ -86,6 +86,10 @@ impl DlnaDiscovery {
};
let url = device.url().to_string();
if device.find_service(&AV_TRANSPORT).is_none() {
debug!(%url, "UPnP device has no AVTransport, skipping");
continue;
}
confirmed.insert(url.clone());
if let Some(entry) = known.get_mut(&url) {