A multi-channel RTSP streaming desktop client built with PySide6 + MVC architecture on Windows.
- 🎥 6 video source types: local video files, cameras, RTSP pull-to-push, screen capture, window capture, Hikvision industrial cameras
- 📡 Multi-channel streaming with independent start/stop control per channel
- 🔐 v2 authenticated streaming: supports window-to-web v2 username + API key authentication, three-level stream path
username/machine/channel - 🎨 Catppuccin Mocha dark theme
- 🔧 Configurable encoding: codec (h264/h265/NVENC), resolution, framerate, bitrate
- 💾 Auto-persistent configuration (JSON)
- 👁️ Live preview via ffplay
- 🖥️ System tray minimize support
- 🔒 Server lock to prevent accidental RTSP address changes
- 🔄 Loop playback for local video files
- 🖱️ Editable channel names with click-to-edit titles
- 🔑 Auto-detect machine name using motherboard UUID
- ✅ Input validation for username, machine name, and stream names (ASCII-safe characters only)
- 🔄 Auto-reconnect with configurable interval for RTSP sources and Hikvision industrial cameras
- 💾 Auto-save on successful connection test
- 🚀 Launch at startup (Windows): when enabled, BeaverPush starts with the OS and auto-resumes the streams that were running last time
Download the latest installer from GitHub Releases. The installer bundles FFmpeg — no additional setup required.
- Python ≥ 3.12
- FFmpeg / ffprobe / ffplay in
PATH(or place them in affmpeg/subdirectory) - uv package manager
# Install dependencies
uv sync
# Run the application
uv run beaverpush
# or
uv run python -m beaverpush.mainuv run pytestBuild a standalone executable and Windows installer:
# Build executable + installer (requires Inno Setup 6)
.\build.ps1 -Version "1.0.0"The build script uses Nuitka to compile a standalone executable (dist/main.dist/BeaverPush.exe) and Inno Setup to create the installer (dist/BeaverPushSetup.exe).
For local installer packaging, also prepare a shared FFmpeg build in the project ffmpeg/ directory (ffmpeg.exe, ffplay.exe, ffprobe.exe, and the required *.dll files). CI downloads and stages these files automatically before invoking build.ps1.
- Enter the RTSP server address (e.g.
rtsp://192.168.1.100:8554) - Enter your Username (your account on window-to-web) and Auth Secret (API key generated on the window-to-web web UI)
- Set a Machine Name to identify this streaming device (auto-detected from motherboard UUID if left empty)
- Click Add Channel to create a streaming channel
- Select a video source type and configure parameters
- Click Start to begin streaming (stream names default to
stream1,stream2, etc. if left empty)
The stream path follows a three-level structure: {username}/{machine}/{channel}, e.g. alice/pc1/stream1.
| Source | Description |
|---|---|
| Local Video | Stream a video file (supports loop playback) |
| Camera | Stream from a DirectShow camera device |
| RTSP | Pull from an RTSP source and re-push |
| Screen | Capture a display/monitor region |
| Window | Capture a specific application window |
| Hikvision Industrial Camera | Capture from a Hikvision industrial camera by serial number |
Toggle Advanced mode on a channel card to configure:
- Codec: libx264, h264_nvenc, hevc_nvenc, copy
- Resolution: Width × Height (auto-adjusted to even numbers)
- Framerate and Bitrate (fixed
M; leave bitrate empty for no bitrate limit)
src/beaverpush/
├── main.py # Application entry point
├── models/
│ ├── config.py # JSON config persistence (AppConfig, StreamConfig)
│ ├── stream_model.py # StreamState enum
│ └── source_type_plugin.py # SourceTypePlugin Protocol + 6 built-in plugins
├── views/
│ ├── theme.py # Catppuccin Mocha theme + QSS
│ ├── stream_card.py # Stream channel card widget
│ └── main_window.py # Main window (toolbar + scrollable card list)
├── controllers/
│ ├── _utils.py # Shared utility functions
│ ├── app_controller.py # App lifecycle, config, device enumeration
│ ├── stream_controller.py # Single channel FFmpeg lifecycle
│ └── reconnect_policy.py # Reconnection state machine
└── services/
├── device_service.py # Device enumeration (cameras/screens/windows)
├── ffmpeg_service.py # Backward-compatible wrapper (imports from sub-modules)
├── ffmpeg_command.py # FFmpeg command building + error mapping
├── ffmpeg_worker.py # FFmpegWorker QThread process management
├── rtsp_url.py # RTSP URL construction + authentication
├── codec_registry.py # Codec availability registry (singleton)
├── ffmpeg_path.py # FFmpeg executable path resolution
├── log_service.py # Loguru-based logging
└── window_capture.py # Win32 window/screen capture (PrintWindow/BitBlt)
┌───────────────────────────────────────────────┐
│ Views │
│ MainWindow ◄──── StreamCardView (×N) │
│ (Qt signals) (Qt signals) │
└──────┬──────────────────┬──────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│AppController │ │StreamController │ ← Controllers
│ (global) │ │ (per channel) │
└──────┬───────┘ └────────┬────────┘
│ │
▼ ▼
┌───────────────────────────────────────────────┐
│ Models + Services │
│ config · stream_model · source_type_plugin │
│ ffmpeg_command · ffmpeg_worker · rtsp_url │
│ codec_registry · device_service │
└───────────────────────────────────────────────┘
- Views — UI rendering only; emit Qt signals for user actions
- Controllers — Connect signals, call services, update views via
set_*methods - Services — Pure business logic (FFmpeg process, device enumeration, window capture)
- Models — Data structures, persistence, and plugin interfaces
Automated builds are triggered by pushing a version tag (e.g. v1.0.0). The GitHub Actions workflow:
- Sets up Python 3.12 + uv
- Downloads the pinned FFmpeg n8.1 shared binaries used for packaging
- Installs Inno Setup 6
- Compiles with Nuitka and packages the installer
- Runs a silent install verification test