Every "fix" for the mirroring freezes so far has been reasoned from code rather than measured, because the one counter that could have falsified any of them was blind by construction: `enqueue_frame` returns as soon as a frame is *posted* to openscreen's TaskRunner, long before `Sender::EnqueueFrame` decides whether to accept it. The frame pump's `enqueued_fps` therefore read a healthy 30fps through every freeze. Add `BreadcastEnqueueStats` (new FFI accessor, no behaviour change): per-second counts of OK / MAX_DURATION_IN_FLIGHT / REACHED_ID_SPAN_LIMIT / PAYLOAD_TOO_LARGE, plus the in-flight window gauges and RTT sampled at the enqueue attempt, all surfaced on the existing "frame pump rate" line as `accepted_fps` / `rejected_*`. Measured against the real Chromecast, that settles it: 12.2% of frames were being rejected with MAX_DURATION_IN_FLIGHT, in 85% of all seconds -- steady, not just during visible freezes. Since breadcast enqueues already-encoded frames, each rejection silently breaks the H.264 reference chain rather than merely dropping a frame. The measurement also corrects the diagnosis. The send window is clamp(2*RTT, kMinSenderInFlight, target_playout_delay/3); the assumption was that a LAN pins it to the 66ms floor. It does not -- RTT to this receiver runs 42-189ms, so 2*RTT is 84-378ms and the window was pinned at the *ceiling*, 133ms at a 400ms playout delay. The ceiling was the binding constraint, so raising the floor alone would have changed nothing. So raise both, ceiling first: target playout delay 400ms -> 1200ms (ceiling 133ms -> 400ms) and kMinSenderInFlight 66ms -> 200ms for RTT dips. Measured over a matched 65s steady-state window, rejections fall 12.2% -> 4.3% and seconds containing a broken reference chain 85% -> 40%. Costs ~800ms of added latency, which is unnoticeable for mirroring to a TV. This is an improvement, not a cure. The residual rejections are bursts (in-flight seen at 433ms against a 200ms window, RTT spiking to 221ms), and no static window survives those. The real fix is the backpressure contract sender.h documents and this facade still doesn't implement: consult GetInFlightMediaDuration()/GetMaxInFlightMediaDuration() and throttle *before* encoding, so a skipped frame never leaves a dangling reference behind. |
||
|---|---|---|
| .forgejo/workflows | ||
| breadcast | ||
| breadcast-caststream-sys | ||
| breadcast-core | ||
| breadcastd | ||
| ci | ||
| contrib | ||
| graphify-out | ||
| vendor/rust_cast-0.21.0 | ||
| .gitignore | ||
| AGENTS.md | ||
| bakery.toml | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CONTRIBUTING.md | ||
| EVENTS.md | ||
| LICENSE | ||
| README.md | ||
breadcast
Cast your screen to a Chromecast, Google TV, or DLNA renderer on the LAN. Two binaries:
breadcastd— background daemon. Discovers Cast (mDNS) and DLNA/UPnP (SSDP) devices, owns the portal screen-capture / encode pipeline, and runs the live session: Cast Streaming (vendored openscreen) or DLNA/AVTransport. One active session at a time.breadcast— GTK4 Layer Shell popup. Thin IPC client ofbreadcastd: pick a device, start or stop a cast. Closing the popup does not interrupt an active session.
This is a bakery product. It is not shipped on the BOS ISO and is not part of the default desktop — install it yourself if you want it.
Install
bakery install breadcast
That puts breadcast and breadcastd on $PATH (usually ~/.local/bin),
installs contrib/breadcastd.service as a systemd user unit, enables it,
and starts the daemon. bakery doctor breadcast checks the system
packages listed in bakery.toml first.
Requirements
- A Wayland compositor with Layer Shell and
xdg-desktop-portalScreenCast (Hyprland is the primary target) - GTK 4.12+ and
gtk4-layer-shell - GStreamer plus
gst-plugin-pipewire,gst-plugins-bad,gst-plugin-va(vah264enc), andgst-plugin-hlssink3 jsoncppandopenssl(runtime deps of the vendored Cast Streaming code)
From source you also need a Rust toolchain (edition 2021).
Build from source
git clone https://git.breadway.dev/Breadway/breadcast
cd breadcast
cargo build --release
Binaries land at target/release/breadcast and target/release/breadcastd.
cp target/release/breadcast target/release/breadcastd ~/.local/bin/
cp contrib/breadcastd.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now breadcastd
Usage
Start the daemon (or let the systemd unit handle it):
breadcastd
Open the device picker:
breadcast
Running breadcast again while it is open closes it (toggle). Click a
device to start mirroring — the portal picker asks which screen to share.
Stop mirroring ends the session. Status is Idle or Casting.
Discovery examples (optional, for debugging the LAN):
cargo run -p breadcast-core --example discover
cargo run -p breadcast-core --example dlna_discover
Hyprland keybind
On stock Hyprland, add the contents of contrib/hyprland.conf to your
hyprland.conf if you want Super+C and the frosted-glass blur:
layerrule = blur, breadcast
layerrule = ignorezero, breadcast
bind = $mainMod, C, exec, breadcast
BOS does not ship this app or a default keybind for it. Add one yourself if you install breadcast on a BOS machine.
bread event integration
breadcastd works the same with or without breadd. When breadd is
running, it publishes bread.cast.* and honors bread.command.cast.*.
See EVENTS.md for the bus contract. bread is not a bakery
dependency.
Theming
breadcast inherits its colour palette from bread-theme.