Windows · Linux · Cast Streaming

Mirror your PC to a Chromecast or Google TV. No Chrome.

CastMirror finds Cast devices on your network, captures your whole screen or a single window with its sound, and streams H.264 and Opus over the same encrypted Cast RTP path Chrome uses. The Windows app is the main product; Linux has its own packages.

⏱️ ~200 ms Target Playout 🧪 230 Test Cases ⚡ GPU H.264 encode
CastMirror on Windows: a navigation rail, two Cast devices as cards, and a casting panel with Start casting and the casting options
Windows: pick a TV, choose the entire screen or one window, and press Start casting.

Download

Version 1.0.0

Windows and Linux ship as separate releases. Every file has a checksum in the same release.

🪟

Windows 10 2004 or later, 64-bit

The installer adds a Start menu entry and Windows Firewall rules for private and domain networks. A portable zip is also there. The binaries are not code-signed yet, so SmartScreen warns on first run: choose More info, then Run anyway.

Windows release

🐧

Linux, x86-64

A .deb for Debian and Ubuntu and an .rpm for Fedora, with the GTK 4 app and the command-line tool. X11, and Wayland through the desktop portal. The Windows app gets more testing than this one.

Linux release

Capabilities

What it does

CastMirror speaks the Cast protocol directly: TLS on port 8009 for control and encrypted RTP over UDP for the picture and sound. It does not run a browser.

📺

One-Click Cast & Discovery

Finds Chromecasts, Google TV devices and Cast TVs over mDNS. If your router blocks mDNS, an opt-in subnet scan or Add by IP still works. A TV that goes off the network drops out of the list.

🖥️

Screen & Window Sharing

Share a whole display or one window. On Windows, window sharing uses Windows.Graphics.Capture, so a window in front of the shared one stays out of the picture. You can also share the sound of one app instead of the whole PC. On Linux: X11, and the Wayland portal picker.

📈

Live stats

Frame rate, bitrate, round-trip time and packet loss update while you cast, with a plain-language hint when the Wi-Fi is dropping packets.

🎬

Freeze and mute

Freeze the picture or mute the TV without ending the cast. Global shortcuts (Ctrl+Alt+C, F and M) work from any app on Windows.

🔄

Adaptive bitrate

An eight-step ladder lowers resolution, frame rate and bitrate when packets are lost and climbs back when the link recovers. After a dropped connection, a sleep or a Wi-Fi change it reconnects for as long as you allow.

🛠️

Diagnostics and privacy

Copy a bug report from the Logs window with the last part of every IP address masked. A crash saves a memory dump on your PC and nothing is uploaded. There is no telemetry and no account.

Architecture

Native Capture to Cast UDP Pipeline

Cast keeps a playout buffer, so expect about 150 to 400 ms of delay depending on the preset (200 ms by default). Desktop pixels and system audio (WASAPI loopback on Windows, PulseAudio or PipeWire on Linux) are encoded, encrypted with a per-session AES-128-CTR key, and packetized into Cast RTP over UDP.

CastMirror Media Pipeline Diagram

Hardware Matrix

Cast video devices Chrome can mirror to

The limits CastMirror applies per device class, from its capability model. They are predictions, not measurements: the rows in the device matrix predate 1.0.0 and were not re-measured for it.

📺 Chromecast (3rd Gen) · 1080p60 ⚡ Chromecast Ultra · 4K30 / 1080p60 🚀 Google TV Streamer · 4K60 / 1080p60 🖥️ Cast TVs (Sony, Vizio, Philips) · 1080p60 📟 Google Nest Hub · 720p60

Documentation

Complete Technical Manual & Guides

All project documentation open on the website, migrated directly from the source repository.

1. Install and build

Guide

Windows

Install the app from the Windows release, or build it. Building needs MSYS2 (UCRT64) with GCC, CMake, Ninja, protobuf, OpenSSL, Opus, FFmpeg, nlohmann-json and libx264, plus the .NET 8 SDK for the WinUI app. The full package list is in docs/building.md.

build-all.bat        :: native core + WinUI app
package.ps1          :: self-contained publish, licence and package checks
scripts\run_ubsan.ps1   :: test suite under UBSan (needs clang)
scripts\soak_windows.ps1 -Minutes 10   :: memory and handle soak

Windows needs 10 version 2004 or later, 64-bit. Control traffic uses TCP 8009 and media uses UDP, so the installer adds Firewall rules for private and domain networks; scripts\setup_firewall.ps1 does the same for the portable zip.

Linux

Install the Linux release packages, or build from source. You need a C++20 compiler (GCC 11+ or Clang 14+), CMake 3.20+, Ninja, and developer libraries for GTK 4, Libadwaita, FFmpeg, and PulseAudio.

Prerequisites (Debian / Ubuntu)
sudo apt update
sudo apt install -y build-essential cmake ninja-build pkg-config protobuf-compiler libprotobuf-dev     libssl-dev libopus-dev libpulse-dev libx11-dev libxext-dev libxrandr-dev libxfixes-dev     libxcomposite-dev libxdamage-dev     libva-dev libdrm-dev libavcodec-dev libswscale-dev libavutil-dev nlohmann-json3-dev libgtest-dev     libgtk-4-dev libadwaita-1-dev
Optional Window Sharing Packages: libxcomposite-dev and libxdamage-dev enable occluded-window capture and damage tracking for X11 window sharing.
Configure and build
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"12"
Build artifacts
Binary PathComponent Role
build/app/castmirror-guiModern GTK 4 + Libadwaita desktop graphical application
build/app/castmirrorInteractive and non-interactive command-line interface
build/tests/castmirror_testsFull Google Test test suite (230 tests)
build/tools/poc-*Protocol, encoding, and transport benchmark tools
build/tools/fake-receiverAutomated simulated Cast receiver for local loopback verification
Running the GUI and CLI
# Launch the GTK 4 desktop app
./build/app/castmirror-gui

# Cast a full monitor via CLI
./build/app/castmirror --device 192.168.0.164 --display 0 --preset High

# List available application windows, then mirror one
./build/app/castmirror --list-windows
./build/app/castmirror --device 192.168.0.164 --window 0x2400004

# Cast display without mirroring system audio
./build/app/castmirror --device 192.168.0.164 --no-audio
Environment variables
  • CASTMIRROR_FORCE_SOFTWARE_ENCODE=1: Force FFmpeg libx264 software encoding even when VAAPI GPU acceleration is available.
  • CASTMIRROR_FORCE_X11=1: Force X11 display capture instead of the PipeWire portal when running under a Wayland compositor via XWayland.
Network and firewall (Linux)

Control traffic uses TCP 8009 (TLS) to the receiver. Media streams use UDP to the ephemeral port returned in the ANSWER. Both devices must be on the same local subnet. Helper script: scripts/setup_firewall.sh.

2. System Architecture & Design

Architecture

CastMirror is a C++20 engine (castcore) with a C API (ABI version 3) and three front ends: the WinUI 3 app (app/winui, .NET 8) on Windows, and a GTK 4 + Libadwaita app (app/gui) and CLI (app/cli) on Linux. The Windows app calls the engine through P/Invoke and checks the ABI version at start-up.

Performance & Latency Contract

  • Connect to First Frame: ≤ 8 seconds typical (including TLS, app launch, and OFFER/ANSWER negotiation).
  • Glass-to-Glass Latency: ~200 ms typical on a clean 5 GHz LAN at the default targetDelay: 200. The value stays in a 150–400 ms band (Game starts at 150 ms, Cinema at 400 ms) and the adaptive controller steps it 150→200→300→400 ms under loss/jitter and back down when clean. Chrome's own default is 400 ms.
  • Teardown Budget: Clean Stop within ≤ 500 ms (guaranteed halt of capture, encoders, and UDP sockets).
  • Idle Safety: Capture and encoders never run in the background when disconnected.

Media Pipeline

  • Video path (Windows): DXGI Desktop Duplication for a display, or Windows.Graphics.Capture for a window → conversion to BGRA (HDR and 10-bit surfaces tone-mapped to SDR) → frame pacer (steady 30 or 60 fps) → BT.709 limited-range conversion → Media Foundation H.264 (asynchronous hardware transforms supported, x264 fallback) → FrameCrypto → RtpPacketizer → paced UDP.
  • Video path (Linux): X11 XRandR / PipeWire portal → BGRA frame → GpuProcessor (letterbox scaling, direct YUV420P/NV12 plane write) → Hardware VAAPI / libx264 encoder (0 B-frames, zerolatency) → Annex-B NALUs → FrameCrypto (AES-128-CTR) → RtpPacketizer (Cast RTP) → Paced UDP socket.
  • Audio path (Windows): WASAPI loopback of the default speakers, or of one app through process loopback (Windows 10 2004+) → folded to 48 kHz stereo → Opus → FrameCrypto → RtpPacketizer → UDP. Capture follows the default device when it changes.
  • Audio path (Linux): PulseAudio / PipeWire default sink monitor → PCM 48 kHz stereo (10 ms chunks) → Opus encoder (192 kbps) → FrameCrypto → RtpPacketizer → UDP. Host audio sink is muted during streaming and restored on Stop.

State Machine

All subsystems coordinate strictly through an explicit thread-safe state machine:

Idle → Discovering → Ready → Connecting → Negotiating → Streaming ↔ Reconnecting → Stopping → Idle
(Failed state transitions gracefully back to Idle or Reconnecting)

Module Boundaries

ModuleResponsibilities
DeviceDiscoverymDNS _googlecast._tcp browsing + local subnet TCP port 8009 scan; drops devices after ten minutes of silence
CastChannelTLS 1.2/1.3 Cast V2 socket on port 8009, Protobuf packet framing, PING/PONG heartbeats
MirroringNegotiatorFirmware app launch (0F5096E8), JSON OFFER/ANSWER generation, AES key negotiation
IDisplayCaptureDXGI Desktop Duplication and Windows.Graphics.Capture on Windows; X11 (MIT-SHM, XComposite, XDamage, XFixes) and Wayland (PipeWire portal) on Linux
IAudioCaptureWASAPI loopback and per-app process loopback on Windows; PulseAudio monitor sink with host speaker muting on Linux
IVideoEncoderMedia Foundation on Windows and VAAPI (h264_vaapi) on Linux, each with automatic fallback to FFmpeg libx264
CastTransportUDP Cast RTP packetization, pacing token bucket, RTCP feedback parser, duplicate NACK suppression
AdaptiveController8-rung dynamic adaptation ladder tracking packet loss, RTT, and jitter

3. Device Compatibility & Adaptation Ladder

Matrix

CastMirror classifies receivers by model name: CapabilityModel::Evaluate lowercases the mDNS md value and substring-matches it. The mDNS ca capability bitmask is parsed into CastDevice::capabilities but no classifier consumes it.

Receiver Compatibility Matrix

Device family (as classified)Matched md substringH.264 LevelMax Resolution & FPSMax Bitrate
Nest Hubnest hub, google home hubLevel 3.11280x720 @ 605000 kbps
Chromecast UltraultraLevel 5.13840x2160 @ 6025000 kbps
Chromecast Gen 1/2h2g2-42, nc2-6a5 (not nc2-6a5-d)Level 4.11920x1080 @ 308000 kbps
Chromecast with Google TVgoogle tv, streamer, android tv, braviaLevel 5.13840x2160 @ 6025000 kbps
Chromecast Gen 3chromecastLevel 4.21920x1080 @ 6015000 kbps
Generic Cast TVanything elseLevel 4.21920x1080 @ 6012000 kbps

8-Rung Dynamic Adaptation Ladder

Defined once in AdaptiveController::BuildLadder() (core/src/adaptive_controller.cc) — each rung is only a resolution, a framerate and a target bitrate; there is no per-rung min/max bitrate and no RTT-threshold table.

RungResolutionFPSTarget Bitrate
0 (4K60)3840 × 21606025.0 Mbps
1 (4K30)3840 × 21603016.0 Mbps
2 (1440p60)2560 × 14406012.0 Mbps
3 (1080p60)1920 × 1080608.0 Mbps
4 (1080p30)1920 × 1080305.0 Mbps
5 (720p60)1280 × 720603.5 Mbps
6 (720p30)1280 × 720302.0 Mbps
7 (540p30)960 × 540301.2 Mbps

4. Cast Streaming Protocol Specification

Protocol

Google Cast screen mirroring uses a specialized real-time media protocol (not WebRTC ICE/DTLS and not HLS media playback).

1. Control Plane (Cast V2 TLS :8009)

  • Connection: OpenSSL TLS connection to device port 8009.
  • Protobuf Frame: 4-byte big-endian length prefix followed by serialized CastMessage.
  • Namespaces:
    • urn:x-cast:com.google.cast.tp.connection — Virtual sender/receiver session bindings.
    • urn:x-cast:com.google.cast.tp.heartbeat — PING / PONG keepalive packets (5-second cadence).
    • urn:x-cast:com.google.cast.receiver — LAUNCH, STOP, and GET_STATUS requests.
    • urn:x-cast:com.google.cast.webrtc — JSON OFFER / ANSWER session negotiation.
  • Firmware Mirroring App ID: 0F5096E8 (audio + video display mirroring) or 85CDB22F (audio-only).

2. Media Plane (Cast RTP / RTCP over UDP)

  • RTP Profile: rtpProfile: "cast" with a custom 7-byte Cast RTP header extension:
    Byte 0:    Flags (Keyframe marker, Reference frame present)
    Bytes 1-2: Frame ID (monotonically increasing 16-bit integer)
    Bytes 3-4: Packet ID (packet index within frame)
    Bytes 5-6: Max Packet ID (total packet count - 1)
  • AES-128-CTR Crypto: Fresh random 16-byte key and 16-byte IV mask per session. Per-frame IV mixes frame_id with the IV mask.
  • Compound RTCP: Sender Reports (SR) paired with receiver CAST feedback packets containing:
    • Checkpoint Frame IDs acknowledging complete display rendering.
    • Loss bitmasks (NACK / CST2) detailing dropped UDP packets for immediate retransmit.
    • Picture Loss Indicators (PLI) signaling decoder desync and requesting an immediate IDR keyframe.

5. Test & Verification Report

Verification

The suite covers unit tests, integration tests, network impairments and a simulated receiver that speaks TLS and Cast RTP over loopback. CI runs it on Windows and on Ubuntu (Release, ASan and UBSan, TSan) and Fedora.

Test Suite Status (230 cases; generated by scripts/count_tests.py)

Test sourceTest CountStatusCoverage
test_state_machine.cc6 testsPassedState transitions, callback notification, retry limits
test_config.cc3 testsPassedDefaults, save/load round-trip
test_capability_model.cc5 testsPassedModel classification, preset recommendations, capture-fps passthrough
test_offer_answer.cc5 testsPassedOFFER JSON, ANSWER parsing, custom audio bitrate, status query
test_crypto.cc2 testsPassedAES-128-CTR round-trip, per-frame ciphertext variance
test_rtp_rtcp.cc11 testsPassed7-byte Cast RTP header, MTU slicing, CAST/CST2 feedback, checkpoint expansion
test_adaptive.cc19 testsPassedLoss downshifts, RTT/jitter delay scaling, bitrate ramp-up, cooldown
test_encoders.cc19 testsPassedH.264 Annex-B output, Reconfigure, multi-slice, intra-refresh, Opus cadence
test_cast_e2e.cc13 testsPassedFull TLS + OFFER/ANSWER + media streaming + Stop against the simulated receiver
test_device_selection.cc2 testsPassedLast-device resolution by id then IP, display index
test_logger.cc2 testsPassedSeverity filter, callback delivery
test_display_capture.cc13 testsPassedSynthetic/X11 enumerate and start, DMA-BUF metadata, WGC pacing (`_WIN32` only)
test_frame_pacer.cc14 testsPassedFrame pacing cadence
test_session_recovery.cc3 testsPassedRecovery state and the 30 s timeout ceiling
test_cast_transport.cc10 testsPassedRTCP source filter, rolling FPS, duplicate-NACK suppression, pacing
test_capture_source.cc9 testsPassedCaptureSource kind round-trip, config v2 to v3 migration, legacy Start forwarding
test_portal_source.cc3 testsPassedPortal source-type mapping (Linux only)
test_source_selection.cc5 testsPassedWindow support reporting, geometry, config persistence
test_device_auth.cc7 testsPassedDevice-auth challenge/response
test_c_api.cc10 testsPassedC ABI structs, string truncation and NUL-termination contract
test_device_discovery.cc18 testsPassedmDNS TXT parsing, device list, subnet probe
test_net_platform.cc7 testsPassedInterface enumeration, socket init idempotence, error classification
test_gpu_processor.cc5 testsPassedLetterbox geometry, YUV/NV12 conversion correctness, invalid args
test_pixel_convert.cc8 testsPassed
test_parser_robustness.cc4 testsPassed
test_audio_sessions.cc5 testsPassed
test_window_capture.cc5 testsPassed
test_audio_capture.cc12 testsPassed10 ms cadence, monotonic timestamps, join-or-detach worker cleanup
test_wgc_window_source.cc5 testsPassed
Total230Passed29 files registered in tests/CMakeLists.txt

Budgets the tests check

MetricBudgetHow it is checked
Stop latency≤ 500 msThe end-to-end test stops a live stream against the simulated receiver and asserts the budget.
Memory and handles over timeWorking set within 64 MB and handles within 40 of the baselineThe soak test streams a 1080p desktop for 10 minutes on Windows (scripts/soak_windows.ps1). The last run stayed within 12 MB and 6 handles.
Undefined behaviourNoneThe suite runs under UBSan in trap mode on Windows, and the RTCP and mDNS parsers are mutation-fuzzed.

These are simulated-receiver and single-machine results. They are not measurements on real TVs; see the device matrix.

6. Physical Device Matrix

Hardware

Firmware, resolution and frame-rate ceilings, codecs and playout delay per receiver generation live in the source document. Its rows predate 1.0.0 and were not re-measured for it, so treat them as unverified: docs/DEVICE_MATRIX.md.

Playout delay bounds

  • Minimum target playout delay the sender will request: 150 ms.
  • Default target delay: 200 ms (Auto / High / Balanced / Smooth).
  • Maximum: 400 ms — the Cinema preset starts there and Game starts at 150 ms; the controller clamps every value to the 150–400 ms range.
  • The delay is adapted at runtime: AdaptiveController::StepUpPlayoutDelay walks 150→200→300→400 ms when jitter exceeds 30 ms or loss exceeds 3%, and StepDownPlayoutDelay walks back after 15 consecutive clean intervals.

Keyframe cadence & crypto

  • 2000 ms periodic intra-refresh, or an IDR on packet-loss bursts.
  • OpenSSL AES-128-CTR, hardware-accelerated via AES-NI / ARMv8 Crypto Extensions.