Skip to main content

LAN screen broadcasting with adaptive UDP streaming and diff-frame encoding

Project description

mirror-space

LAN screen broadcasting with adaptive UDP streaming and diff-frame encoding.

mirror-space lets you share your screen to any machine on the same local network with minimal latency. It uses block-based differential compression — sending only the pixels that changed — to keep bandwidth low even at 30 fps, and automatically adapts quality when the network gets congested.


Features

  • Diff-frame encoding — only changed screen regions are transmitted each frame
  • Adaptive streaming — FPS, resolution, and JPEG quality adjust in real time based on receiver feedback
  • Zero-config LAN discovery — mDNS (Zeroconf), UDP beacon, and subnet-scan discovery run in parallel so receivers find broadcasters automatically
  • HWND-based window capture (Windows) — lock capture to a specific window handle so tab switches never affect the stream
  • Secure session handshake — six-character access ID prevents accidental cross-session connections
  • Cross-platform receiver — the receiver runs on Windows, Linux, and macOS

Requirements

Component Minimum version
Python 3.10
OpenCV 4.8.0
NumPy 1.24.0
mss 9.0.1
Pillow 10.0.0
zeroconf 0.130.0

Windows users who want HWND-based window capture (recommended) also need:

pygetwindow>=0.0.9
pywin32>=306

Installation

From PyPI

pip install mirror-space

With Windows extras (HWND capture + window enumeration)

pip install "mirror-space[windows]"

From source (development)

git clone https://github.com/your-username/mirror-space.git
cd mirror-space
pip install -e ".[dev,windows]"

Quick Start

Step 1 — Start the broadcaster (on the machine you want to share)

mirror-broadcast

The broadcaster will:

  1. Prompt you to choose a capture region (full screen, specific window, or custom area)
  2. Print a Session Access ID (e.g. A1B2C3)
  3. Wait for a receiver to connect

Step 2 — Start the receiver (on the other machine)

mirror-receive

The receiver will discover broadcasters on the LAN automatically. Select the broadcaster by number and enter the Session Access ID when prompted.

Manual IP (if auto-discovery fails)

# broadcaster side — already running, noting its IP
mirror-broadcast

# receiver side — pass broadcaster IP directly
mirror-receive 192.168.1.42

Custom port

mirror-broadcast 192.168.1.255 8888
mirror-receive 8888 192.168.1.42

CLI Reference

mirror-broadcast [target_ip [port]]

Argument Default Description
target_ip 255.255.255.255 Receiver IP or broadcast address
port 9999 UDP stream port (feedback on port + 1)

mirror-receive [port [broadcaster_ip]]

Argument Default Description
port 9999 UDP listen port
broadcaster_ip (auto) Skip discovery and connect directly

Receiver keyboard shortcuts (inside the OpenCV display window):

Key Action
19 Select discovered stream
q / ESC Quit

Library API

You can use the encoder and decoder directly in your own code:

import cv2
import numpy as np
from mirror_space import DiffFrameEncoder, DiffFrameDecoder

encoder = DiffFrameEncoder(
    block_size=8,
    threshold=1,
    max_changed_block_ratio=0.45,
    jpeg_quality=95,
)

decoder = DiffFrameDecoder()

# Encode a frame captured by any means
frame = cv2.imread("screenshot.png")
payload = encoder.encode(frame, frame_number=0)

# Decode on the receiver side
reconstructed = decoder.decode(payload)

DiffFrameEncoder parameters

Parameter Default Description
block_size 32 Pixel block size for dirty-region detection
threshold 10 Per-pixel diff threshold to count a block as changed
max_changed_block_ratio 0.30 Changed-area ratio above which a key frame is sent
max_diff_payload_ratio 0.12 Payload-size ratio above which a key frame is sent
jpeg_quality 75 JPEG quality for full/key frames (30–95)
enable_motion_detection True Enable Lucas-Kanade optical flow motion frames

How It Works

Broadcaster                          Receiver
──────────                           ────────
ScreenCapture                        UDPReceiver
    │                                    │
    ▼                                    │
DiffFrameEncoder ──UDP fragments──► Reassemble
    │                                    │
    │   KEY_FRAME  (PNG, full res)       ▼
    │   DIFF_FRAME (PNG patches)    DiffFrameDecoder
    │   MOTION_FRAME (residuals)         │
    │                                    ▼
    ◄──STREAM_STATS / KEYFRAME_REQ──  cv2.imshow
AdaptiveController
  • KEY_FRAME / FULL_FRAME — complete PNG-compressed frame; sent on startup, resolution change, or high-motion scenes
  • DIFF_FRAME — only the changed rectangular regions, each individually PNG-compressed
  • MOTION_FRAME — Lucas-Kanade optical flow vectors + residual blocks (experimental)
  • Fragmentation — frames larger than 1400 bytes are split into MTU-safe UDP fragments with a 20-byte header (total | index | frame_number | send_time_ns)

Project Structure

mirror-space/
├── src/
│   └── mirror_space/
│       ├── __init__.py          # Public API
│       ├── broadcaster.py       # CLI entry point + stream logic
│       ├── receiver.py          # CLI entry point + display loop
│       ├── diff_encoder.py      # DiffFrameEncoder / DiffFrameDecoder
│       └── region_selector.py   # Capture region / window picker
├── docs/
├── pyproject.toml
├── setup.py
├── README.md
├── LICENSE
└── .gitignore

Building and Publishing

See PUBLISHING.md for full step-by-step instructions.


License

MIT — see LICENSE for details.

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

mirror_space-0.1.0.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mirror_space-0.1.0-py3-none-any.whl (38.1 kB view details)

Uploaded Python 3

File details

Details for the file mirror_space-0.1.0.tar.gz.

File metadata

  • Download URL: mirror_space-0.1.0.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.8

File hashes

Hashes for mirror_space-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3768fb769b9d686caccd04a6a9cf09909fc021a7af4c5623fbf836f4fd275bfc
MD5 cb779ae97f12aa415fddd0f29c647b9c
BLAKE2b-256 3e79951be0914945bf763fe4d9d04695e197a0b299c2fe1aef714e5de646e5d9

See more details on using hashes here.

File details

Details for the file mirror_space-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mirror_space-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 38.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.8

File hashes

Hashes for mirror_space-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1941e3a9af32feafb4c7ab804057eb1eef9ccd3e57eb4d2a7baae0761580f762
MD5 9c99c0dffa6ffe64e2e77235f38d39f3
BLAKE2b-256 25afc925abfc5cf52215a6b8db8972c76660768f2d309282f8282dff139aee02

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page