Repository navigation
Chromecast support (T-058): cast to TVs on the LAN, with discovery that works without mDNS - #11
Merged
Merged
Conversation
Cast Live TV, movies and episodes to a Chromecast or Google TV on the LAN. A Cast button in the player chrome and next to the sidebar logo picks the TV; what plays moves there (VOD at its position, live at "now"), and the player stays the remote (play/pause, seek, TV volume, zapping). Stopping continues a movie/episode here, paused, where the TV was. - src-tauri/src/cast/: Cast v2 over TLS (castv2.rs), session task and cast_* commands (mod.rs). Google's Default Media Receiver plays a URL from a LAN relay (relay.rs): provider URLs carry credentials and never reach the TV; each item gets a random token served only to that device. - remux.c: the embedded FFmpeg remuxes to fragmented MP4 (video copied; AAC/MP3 copied via aac_adtstoasc, other audio -> stereo AAC). Needs FFmpeg's aac encoder, now enabled in build-media.sh (one rebuild). - probe.rs: routers that drop multicast between Wi-Fi clients hide mDNS, so TVs are also found by re-checking remembered ones, sweeping the local /24 for port 8009 (identified via :8008/setup/eureka_info), or by IP. - One provider stream at a time: local play and the version probe end a cast session. - macOS: Info.plist local-network keys + network.server entitlement. Verified on a real Chromecast Ultra with mDNS blocked by the router.
Docs only: README and WORKLOG keep both the Chromecast (T-058) and Flatpak (T-059) entries. The Flatpak build runs build-media.sh, so it picks up the AAC encoder casting needs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds casting to a Chromecast or Google TV on the LAN. A Cast button in the player controls and next to the sidebar logo lists the TVs. Picking one moves what plays there: a movie or episode continues at its position, live TV starts at "now". With nothing playing, whatever is played next goes to the TV. The player stays the remote (play/pause, seek, TV volume/mute, zapping). Stopping the cast continues a movie or episode here, paused, where the TV was.
How it works (
src-tauri/src/cast/)castv2.rs: the Cast v2 protocol (protobuf frames over TLS to port 8009). Cast devices use Google's own CA, so like every sender only the handshake signature is checked, not the certificate chain.mod.rs: discovery, one session task per TV, and thecast_*commands. The TV runs Google's Default Media Receiver (CC1AD845, no developer registration).relay.rs: the TV never sees the provider URL, which carries the account's credentials. It getshttp://<lan-ip>:<port>/cast/<token>/<n>.mp4: the token is random per item and served only to that TV's address. Each seek is a new URL whose start position the relay remembers.remux.c, built bybuild.rswithccagainst the embedded FFmpeg: repackages the stream as fragmented MP4. Video is copied (H.264/HEVC/VP9/AV1). AAC/MP3 audio is copied throughaac_adtstoasc, which fMP4 doesn't insert by itself (theffmpegtool fails on TS input the same way). Anything else (AC-3, DTS, …) becomes stereo AAC. Live TV gets timestamp jumps re-based and dropped channels reloaded up to 3 times.probe.rs: many routers drop multicast between Wi-Fi clients (AP isolation, IGMP snooping), so mDNS never arrives even though the TV is reachable. TVs are also found by re-checking remembered ones (saved on every successful connect), sweeping the local /24 networks for port 8009 (about 1 s, at most once a minute) and identifying hits via:8008/setup/eureka_info. The menu also has "Add a TV by IP address". A TV found both ways shows up once.Reviewers should know
scripts/build-media.shnow enables FFmpeg'saacencoder, needed for the audio conversion. Any edit to the script rebuilds the whole engine once, here, in CI and in the Flatpak build (which runs the same script).publiczone allows only ssh/dhcpv6), both mDNS and that connection are blocked by default. The network sweep works around discovery; the stream connection needs a firewall rule. A fixed, configurable port would make that rule a single port. It's recorded in WORKLOG as open, not done here.player.audioLangsetting. The in-player audio/subtitle menus, picture-in-picture, recording and stats are hidden while casting.src-tauri/Info.plistaddsNSLocalNetworkUsageDescriptionandNSBonjourServices, plus thenetwork.serverentitlement. Not run on a Mac.mdns-sd,tokio-rustls(the same rustls/aws-lc as reqwest),if-addrs,getrandom,cc.THIRD_PARTY_NOTICES.mdis regenerated.Tested
ffprobe.scripts/check.shpasses after merging main: 87 Rust tests (6 new: protobuf framing, relay URL parsing, network sweep, address parsing, id matching), clippy, tsc, 46 vitest (3 new), notices.Merged main (7f5fa74): the Flatpak PR (#10) landed while this was open. Only README and WORKLOG conflicted; both sides are kept (Chromecast is T-058, Flatpak T-059).
scripts/flatpak.sh pinsstill passes. Earlier, three code conflicts with #8/#9 (probe.rs,Layout.tsx,Player.tsx) were resolved by keeping main's new subtitle-key logic and mute badge and adding the casting guards on top.