Screen Cast: Chromecast

Hi everyone.
Recently I bought a Tablet and i put on it Fedora with KDE, I came from Galaxy Tab so one of the functions that i miss is the ability to cast the screen from the tablet to a Chromecast device, the problem is that there is not and easy way to cast my screen using wayland on KDE, i found a workaround to cast videos or images using a Brave or VLC, but sometimes i want to cast an application so i need to cast my screen or at least the application window.

I’m a Software Engineer but mostly focused on web development and backend, however I decided to work on this feature with my few knowledge about wayand and kde development, my idea is first, create a service to interact with chromecast protocol(find, devices, stream media, etc..) and second, create a KDE plasmoid for interacting with this service with he ability to cast my screen or a window .

I’m using python right now because it’s the easier way to interact with chromecast that i found but i’m open to alternatives.

here’s my progress:

I wrote a simple script with Python, my idea was use ffmpeg for creating a stream of my screen and cast to an specific chromecast device, it’s not working because the ffmpeg show errors when i try to cast the screen on wayland but the chromecast functions are working: find devices and cast media to those devices.
Here’s my script, it’s only depends on pychromecast pip package:

import pychromecast
import time
import subprocess
import os
from pychromecast.controllers.media import MediaController

def find_chromecasts():
    print("Searching Chromecast devices...")
    chromecasts, browser = pychromecast.get_chromecasts()
    if not chromecasts:
        print("Not found.")
        return None

    print("Chromecast devices availble:")
    for i, cc in enumerate(chromecasts):
        print(f"{i}: {cc.cast_info.friendly_name}")

    selection = int(input("Select Chromecast device: "))
    cast = chromecasts[selection]
    print(f"Connected to {cast.cast_info.friendly_name}")
    cast.wait()
    return cast

def get_displays():
    displays = []
    xrandr_output = subprocess.check_output(["xrandr", "--listmonitors"]).decode()
    for line in xrandr_output.split('\n')[1:]:  # Skip the first line
        if line.strip():
            displays.append(line.split()[-1])
    return displays

def select_display():
    displays = get_displays()
    print("Displays disponibles:")
    for i, display in enumerate(displays):
        print(f"{i}: {display}")
    selection = int(input("Select display: "))
    return displays[selection]

def get_display_resolution(display):
    xrandr_output = subprocess.check_output(["xrandr", "--query"]).decode()
    for line in xrandr_output.split('\n'):
        if display in line and "connected" in line:
            resolution = line.split()[2].split('+')[0]
            return resolution
    return "1920x1080"  # Default resolution if not found

def start_ffmpeg_stream(cast, display):
    ip = cast.cast_info.host
    port = cast.cast_info.port
    resolution = get_display_resolution(display)
    print("Casting to: ", ip)
    ffmpeg_command = [
        "ffmpeg",
        "-f", "x11grab",
        "-r", "60",
        "-s", resolution,
        "-i", f"{os.environ['DISPLAY']}.0+{display}",
        "-vcodec", "libx264",
        "-preset", "ultrafast",
        "-tune", "zerolatency",
        "-maxrate", "5000k",
        "-bufsize", "10000k",
        "-f", "matroska",
        f"tcp://{ip}:{port}"
    ]

    print("starting transmission with ffmpeg...")
    return subprocess.Popen(ffmpeg_command)

def stream_to_chromecast(cast, display):
    mc = cast.media_controller

    print(f"Setting transmission to {cast.cast_info.friendly_name}")
    stream_url = f"tcp://{cast.cast_info.host}:8009"

    ffmpeg_process = start_ffmpeg_stream(cast, display)

    print("Starting Chromecast transmission...")
    mc.play_media(stream_url, 'video/mp4')
    mc.block_until_active()

    print("Play started. Press Ctrl+C To stop.")
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("Stoping transmission...")
        mc.stop()
        cast.quit_app()
        ffmpeg_process.terminate()
        ffmpeg_process.wait()

if __name__ == "__main__":
    cast = find_chromecasts()
    if cast:
        display = select_display()
        stream_to_chromecast(cast, display)

Currently I’m looking for a better way to cast the screen on wayland, so any help is welcome, also i have no idea how to develop the cast widget, I’m focused right now on the functionality so any advice is welcome to.

Also here’s mockup of the plasmoid:

Any help or advice is welcome, thanks for reading.

1 Like

Hey, I ran into exactly this problem building KCast (KDE Plasma 6 widget, currently does file/YouTube casting to Chromecast via catt). Desktop mirroring is a step further, but I’ve been prototyping it and hit the same Wayland capture wall you’re describing — might save you some time.

Two paths I tried:

HLS via the Chromecast Default Media Receiver (GStreamer/PipeWire/xdg-desktop-portal → HLS segments) — got it down to 5-8s delay at 1080p60 with reduced bitrate, but hit a hard floor: the Default Media Receiver won’t go below playlist-length: 3, or Chromecast just hangs. That’s structural, not a tuning problem — the receiver is built for buffered VOD/live-TV, not mirroring. If your delay is stuck too, that’s probably why.

Sunshine (host) + Moonlight (client) — this is what actually gets sub-1s delay. It skips the Cast protocol entirely and uses the GameStream/UDP family instead (same idea as Steam Link). Caveat: your receiver needs to actually run Android/Google TV with app support — plain “Chromecast built-in” without an app store won’t cut it.

One more Wayland-specific gotcha either way: on KWin, capture=portal (xdg-desktop-portal + PipeWire) is the only capture path that works — KMS capture and wlr-export-dmabuf aren’t supported, so if your pipeline assumes either of those, that’s your bug.

We’re currently polling on which of these to build into KCast properly: Which desktop-cast solution should KCast get first? · Agundur-KDE/KCast · Discussion #22 · GitHub. Given you’ve already been fighting the same ffmpeg/Wayland pipeline, your take on the tradeoff (quick-and-laggy vs. setup-but-fast) would be genuinely useful there.

https://flathub.org/apps/org.gnome.NetworkDisplays should work too. But I agree that a plasmoid or even an integrated feature would be nice.

1 Like

I haven’t tried this myself but since you are using Python this might be helpful

Thanks for the pointer! We actually tried GNOME Network Displays — works, but it’s Miracast/WFD-based, so it needs a WiFi-Direct-capable adapter on both ends. No WiFi on our test box, so that path is closed for us here. Still on the lookout for something that works over plain LAN/Ethernet with low delay — will report back if we find it.

Thanks, appreciate the pointer! We already capture via pipewiresrc in a GStreamer pipeline (portal ScreenCast + PipeWire), so we’re not actually blocked on the capture step — that part’s fast either way. pipewire-capture gives you raw BGRA frames as numpy arrays, but doesn’t include encoding or network transport, so it wouldn’t change our actual bottleneck: the ~5-8s delay comes from Google Cast’s Default Media Receiver, which needs a minimum 3-segment HLS playlist buffered before it’ll play — that floor sits downstream of capture, no matter how the frames are grabbed.

The only thing that got us under 1s so far is bypassing the Cast Media Receiver entirely (Sunshine/Moonlight, GameStream-style UDP streaming) — but that needs pairing an app on the receiver, not a drop-in fix. Still looking for something that keeps Chromecast’s simple “just cast” model with lower latency.

1 Like

Ah, cool!

In any case this was mostly intended for @QuinsZouls as they seemed be stuck on this.

As an unrelated third party I am wondering if you could collaborate on the service part somehow. This could make your applet the primary UI and allow you both to concentrate on the service and its challenges

@krake Good suggestion, thanks for flagging that!

@QuinsZouls happy to explore that — KCast already has the applet/UI side (drag&drop, catt integration) fairly solid, so if you’re deeper into the service/backend challenges that could split nicely. Feel free to send me a PM here (or open an issue on the repo) and I’ll add you as a collaborator on GitHub so we can figure out how the pieces fit together.

> i have no idea how to develop the cast widget

You can also put a python script into the Plasma applet and call it from your QML/JavaScript. The advantage, beside using python, could be that maybe no C/C++ is needed what enables to distribute the Plasma applet through our Plasma Extension store. For an example see e.g. Sebastian Sauer / Plasmakonsoleframe · GitLab