No description
Find a file
Breadway 5b49955e33 Fix Cast Streaming teardown crash, bitrate collapse, and HLS latency
Three independent problems found by reading the mirroring paths end to end.

1. Segfault on Cast session teardown. CastStreamSender::SchedulePoll
   self-reschedules every 100ms with a raw `this` and was never cancelled,
   so the one task that can be scheduled to run *after* an already-queued
   teardown task would dereference the just-reset `environment` unique_ptr
   (Environment::task_runner() dereferences a member immediately) --- a hard
   null deref on openscreen's TaskRunner thread. TaskRunnerImpl's shutdown
   has an explicit flushing phase and PlatformClientPosix::ShutDown()'s quit
   task queues behind whatever is already pending, so this is a race the
   teardown path can lose. Latch a `shutting_down` atomic before posting
   teardown and check it in the poll and in every other posted task.

2. Encoder bitrate collapsing to the floor within seconds. The control loop
   set `target = 0.85 * estimate` once a second unconditionally. openscreen's
   BandwidthEstimator deliberately under-estimates capacity whenever the
   transmit rate is below it and documents the required TCP-like response;
   multiplying the target by <=0.85 every second instead walks 4000 kbps past
   1500 in ~6s and pins it at the floor on a healthy LAN. Replaced with
   proper AIMD (hold on a zero/unknown estimate, back off below it, probe up
   10%/s otherwise), clamped to 1000..8000 kbps, with unit tests.

3. DLNA/HLS latency. Segment length is max(target-duration, GOP), so
   target-duration=2 with a 2s GOP gave 2s segments, ~6s of renderer buffer,
   plus 3 segments of stale video waited for before handover. 1s segments
   (GOP halved to make that reachable), shorter playlist, and wait for 2
   segments instead of 3.

Also hardened two paths into openscreen's fatal OSP_CHECK on strictly
increasing RTP timestamps: pull_encoded_frame no longer substitutes 0 for a
missing PTS (it skips the buffer), and facade.cc drops non-monotonic capture
times at the FFI boundary. Either could previously abort the daemon outright.

CastMirrorSession now owns the Arc<CastStreamSender> instead of leaving its
lifetime to whichever detached pump thread dropped the last clone, so the
blocking FFI destroy happens at a defined point in stop() with the pump
joins ordered around it.
2026-08-15 22:34:21 +08:00
.forgejo/workflows ci: build on bread-ecosystem's shared Arch container 2026-08-05 19:16:28 +08:00
breadcast Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
breadcast-caststream-sys Fix Cast Streaming teardown crash, bitrate collapse, and HLS latency 2026-08-15 22:34:21 +08:00
breadcast-core Fix Cast Streaming teardown crash, bitrate collapse, and HLS latency 2026-08-15 22:34:21 +08:00
breadcastd Fix Cast Streaming teardown crash, bitrate collapse, and HLS latency 2026-08-15 22:34:21 +08:00
ci ci: build on bread-ecosystem's shared Arch container 2026-08-05 19:16:28 +08:00
contrib Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
graphify-out docs: describe the shipped bakery product and single-trunk releases 2026-08-15 21:38:38 +08:00
vendor/rust_cast-0.21.0 Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
.gitignore Track AGENTS.md 2026-08-15 22:03:30 +08:00
AGENTS.md Track AGENTS.md 2026-08-15 22:03:30 +08:00
bakery.toml docs: describe the shipped bakery product and single-trunk releases 2026-08-15 21:38:38 +08:00
Cargo.lock Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
Cargo.toml Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
CONTRIBUTING.md docs: describe the shipped bakery product and single-trunk releases 2026-08-15 21:38:38 +08:00
EVENTS.md Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
LICENSE Implement Cast Streaming mirroring, DLNA casting, daemon+GUI, and breadd integration 2026-08-03 09:07:21 +08:00
README.md docs: describe the shipped bakery product and single-trunk releases 2026-08-15 21:38:38 +08:00

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 of breadcastd: 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-portal ScreenCast (Hyprland is the primary target)
  • GTK 4.12+ and gtk4-layer-shell
  • GStreamer plus gst-plugin-pipewire, gst-plugins-bad, gst-plugin-va (vah264enc), and gst-plugin-hlssink3
  • jsoncpp and openssl (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.