Add your description here
Project description
P-Cast
Cast anything you can hear on your Linux desktop to any Chromecast‑compatible speaker, TV or Nest device — live, with ~3 s latency.
P‑Cast captures audio directly from PipeWire / PulseAudio, encodes it with FFmpeg and exposes the result as an HLS live stream that is played by Chromecast.
Casts local audio-device to chromecast compatible device.
Quick start
git clone https://github.com/GenessyX/p-cast
cd p-cast
uv run python run.py
Send audio to P‑Cast (set it as default sink or route an app explicitly).
Features
- Virtual sink - creates virtual null sink P-Cast on the fly.
- Automatic device discovery – finds the first Chromecast on your LAN via mDNS.
- On‑the‑fly transcoding – AAC @ 256 kbps by default (customisable via env vars).
- Live HLS – 0.5 s segments; Chromecast buffers 3 -> ~3 s end‑to‑end delay.
- Volume follow – changes to the PipeWire sink volume are mirrored to the Chromecast.
- Tiny footprint – single Python process + FFmpeg child; no browser or GUI required.
- Optional reconnection guard – a
p-cast-stream.confsnippet keeps the capture stream pinned to the chosen device.
How it works (TL;DR)
-
run.pystarts Granian and serves the Starlette app on :3000. -
app.py- discovers a Chromecast (
cast.find_chromecast), - creates a null sink named
P‑Cast(device.SinkController), - launches FFmpeg (
ffmpeg.create_ffmpeg_stream_command) to read fromP‑Cast.monitorand write HLS segments to a temp dir, - mounts that dir at /stream.
- discovers a Chromecast (
-
After 2 s the Chromecast receives
http://<host>:3000/stream/index.m3u8via the Media Controller and starts buffering. -
A background task listens for
pactlvolume‑change events and callsChromecast.set_volume(...).
Everything runs in a single Python process; FFmpeg is the only external binary.
Requirements
| Component | Purpose |
|---|---|
| Linux w/ PipeWire | audio capture |
| Python ≥ 3.12 | application runtime |
| FFmpeg ≥ 6.1 | encoding & HLS muxing |
| Chromecast | playback device on same LAN |
Dependencies are declared in pyproject.toml. The examples below use uv but regular pip works just as well.
uv run python run.py
Optional: keep PipeWire from re‑connecting to another device
Unplugging headphones or switching default sink can make PipeWire migrate all streams – including the monitor the server is recording – to a new sink, resulting in streaming input from switched device to the Chromecast. If that annoys you, add the supplied pipewire config:
mkdir -p ~/.config/pipewire/pipewire-pulse.conf.d
cp ./p-cast-stream.conf ~/.config/pipewire/pipewire-pulse.conf.d/p-cast-stream.conf
systemctl --user restart pipewire
The file sets:
node.dont-reconnect = true # stay on the chosen device
node.latency = "64/48000" # optional, lowers internal latency
🔊 Audio Delay: PipeWire to Chromecast
The minimum practical delay between audio captured from a PipeWire sink and audio output on a Chromecast device is approximately 3 seconds.
This delay is primarily due to how Chromecast handles HLS streaming, which includes:
-
Buffering: Chromecast typically buffers 3 full segments before beginning playback.
-
Playlist polling interval: The device refreshes the playlist based on the
#EXT-X-TARGETDURATIONvalue, which defines the expected segment duration and how frequently the.m3u8file is reloaded. -
Segment duration limitations: While the Google Chromecast documentation states:
#EXT-X-TARGETDURATION— How long in seconds each segment is. This also determines how often we download/refresh the playlist manifest for a live stream. The Web Receiver Player does not support durations shorter than 0.1 seconds.In practice, Chromecast cannot reliably handle
#EXT-X-TARGETDURATIONvalues below 1 second. Attempting to use smaller values (e.g., 0.25s) may result in playback stop. -
Comparison with VLC: Media players like VLC can handle much shorter segment durations (e.g., 0.25s) and respond to playlist updates more aggressively, leading to lower latency compared to Chromecast.
💡 Summary
| Player | Minimum stable segment duration | Behavior |
|---|---|---|
| Chromecast | ~1 second | Buffers 3 segments, ~3s delay |
| VLC / hls.js | 0.25–0.5 seconds | Can start playback much faster |
Known limitations
- Only the first Chromecast discovered is used. Open a PR to add a selector!
- Audio only. Support for dummy video tracks is stub‑bed out in
ffmpeg.py. - Tested on Manjaro Linux / PipeWire 1.4.1;
- 3+ seconds delay.
Roadmap
-
Configuration with envs:
Variable Default Description PCAST_BITRATE256kAAC bitrate fed to FFmpeg PCAST_SAMPLE_RATE48000Sampling rate (Hz) PCAST_HLS_FORMATmpegtsmpegtsor experimentalfmp4PCAST_SINK_NAMEP-CastName of the null sink See
config.pyfor details. -
Multiple chromecast devices support.
-
Package with
uvx(uv tool). -
Enhance repository structure.
-
Qt tray app to control chromecast device (pause/play).
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file p_cast-0.1.0.tar.gz.
File metadata
- Download URL: p_cast-0.1.0.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5e2a3153c1f51b726a5349f2f31fb96d79cd3fb4d05d3bd89e4cd10c044388a
|
|
| MD5 |
7eefaaaae2ff51f2819fcf483facb264
|
|
| BLAKE2b-256 |
7e538570665d080a22e927b465a6ec4f8c7d57efb74ee1c2bf5bbcbbf7f02a8b
|
File details
Details for the file p_cast-0.1.0-py3-none-any.whl.
File metadata
- Download URL: p_cast-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3fc030a1a823c1576611f03c0d840d4d346497842559fd5e284aa4dc75b9a8d
|
|
| MD5 |
15df025303f9fc4663fc49e33bc10ee0
|
|
| BLAKE2b-256 |
f53a09e942e74d9df11880fa2118715b42cc1736ca3cb453d77f512a751aedaf
|