cam-proxy
An ultra-lightweight, zero-stress edge gateway for IP security cameras.
Eliminate camera freezes, RTSP socket exhaustion, and heavy CPU decoding. Sub-100ms HTTP snapshots, single-flight coalescing, PTZ control, MJPEG streaming, and ONVIF-to-MQTT events.
Have you ever tried displaying snapshots from your IP cameras on a Home Assistant wall tablet, MagicMirror, or web dashboardโonly to have the camera freeze, disconnect, or crash under the connection load? Or spent hours wrestling with complex ONVIF SOAP protocols and RTSP credentials just to pan a camera or receive a motion alert?
cam-proxy solves this. It acts as an intelligent, featherweight (< 20MB RAM) buffer between your cameras and your smart home or custom applications. Instead of running heavy 24/7 video decoders or exposing fragile camera hardware to multiple concurrent clients, cam-proxy gives you simple HTTP snapshot URLs, clean REST PTZ controls, and automatic MQTT motion alertsโwithout burning CPU or overloading your cameras.
Current Release
0.4.0 StableDownload pre-compiled static zero-dependency binaries for Linux, macOS, and Raspberry Pi:
๐ก What Problems Does cam-proxy Solve?
Addressing the architectural limitations of low-cost IP cameras and smart home setups.
Budget IP cameras (Tapo, Reolink, Hikvision, Dahua, Amcrest) have limited microcontrollers. When a dashboard refreshes, multiple tablets load simultaneously, or an automation requests snapshots, the camera's RTSP connection crashes. cam-proxy uses single-flight request coalescing and an in-memory TTL frame cache: 20 simultaneous snapshot requests hit the camera as a single connection.
Running a full NVR (like Frigate or Blue Iris) requires gigabytes of RAM and constant CPU/GPU decoding just to grab periodic pictures. cam-proxy idles at < 15MB RAM and 0% CPU, connecting to the camera only when a snapshot or PTZ action is requested.
Controlling a PTZ camera or pulling an ONVIF snapshot normally requires crafting complex XML SOAP envelopes with WS-Security timestamps and HTTP Digest handshakes. cam-proxy translates all of this into standard, browser-friendly JSON REST endpoints: GET /snapshot and POST /ptz.
Subscribes directly to local camera event queues (motion detection, tampering, line crossing) and publishes them to MQTT with Home Assistant MQTT Auto-Discoveryโno cloud subscriptions, vendor apps, or flaky webhooks required.
Browsers cannot connect directly to camera RTSP streams or digest-authenticated HTTP endpoints due to CORS restrictions. cam-proxy provides configurable CORS middleware and standard security headers (X-Content-Type-Options: nosniff) out of the box, ready for embedding directly in wall tablets, MagicMirrors, and web applications.
Why cam-proxy? (Comparison with Alternatives)
If you already use tools like Frigate, go2rtc, or ffmpeg scripts, where does cam-proxy fit?
| Feature / Capability | cam-proxy | Full NVRs (Frigate, Blue Iris, Shinobi) | Streaming Servers (go2rtc, MediaMTX) | DIY Scripts (ffmpeg / curl cron) |
|---|---|---|---|---|
| Primary Focus | Lightweight Snapshots, PTZ & MQTT Events | 24/7 Video Recording & AI Detection | Low-Latency Live Video Streaming | Ad-hoc Frame Grabbing |
| Idle Memory (RAM) | < 15โ20 MB | 500 MB โ 4+ GB | 50 MB โ 200 MB | Transient (spikes per execution) |
| Idle CPU Usage | ~0% (no continuous decoding) | High (constant decoding/AI) | LowโModerate | 0% until script triggers |
| Hardware Strain Protection | Yes (Single-flight coalescing + cache) | None (pulls continuous 24/7 stream) | None (passes connections through) | No (concurrent runs crash camera) |
| ONVIF PTZ REST API | Yes (Simple JSON POST /ptz) | Rare / Complex integration | Limited / None | Manual SOAP XML scripting |
| Camera Event Bridge | Yes (ONVIF PullPoint โ MQTT) | Via internal events | RTSP metadata only | Not supported |
| Home Assistant Discovery | Zero-Config (MQTT Auto-Discovery) | Via custom integration | Via go2rtc integration | Manual YAML configuration |
| CORS & Web Browser Ready | Yes (Built-in CORS middleware) | Usually behind reverse proxy | WebRTC / WS focused | Requires reverse proxy |
| Dependency Footprint | Zero (Single static binary, CGO-free) | Heavy (Python, OpenCV, TensorRT) | Moderate | Requires ffmpeg, bash, curl |
Decision Guide
Selecting the right tool for your camera architecture.
When should you use cam-proxy?
- โ Snapshots on dashboards & smart displays: Wall tablets, Home Assistant Lovelace cards, MagicMirrors, e-ink picture frames, or smartwatch notifications.
- โ Camera events without cloud lock-in: Instant motion, tamper, and line-crossing alerts pushed to Home Assistant or Node-RED via MQTT.
- โ Low-power edge hardware: Raspberry Pi Zero, 3, 4, 5, thin clients, NAS devices, or edge routers where every megabyte of RAM matters.
- โ Eliminating camera crashes: Your camera drops offline when multiple clients or automations request snapshots simultaneously.
When should you use something else?
-
โ
Continuous 24/7 video recording: If you need to store weeks of continuous video footage on hard drives, use a dedicated NVR like Frigate, Scrypted, or Blue Iris (you can still run
cam-proxyalongside them for lightweight dashboard snapshots!). -
โ
Low-latency live video streaming: If you want to stream 30fps live video in a browser over WebRTC, tools like
go2rtcorMediaMTXare purpose-built for video re-streaming.
Engineered for Hardware Resilience
Low-cost IP cameras have fragile web servers that crash under concurrent dashboard requests. cam-proxy protects them.
Single-Flight Coalescing
Concurrently arriving snapshot requests are coalesced into a single outbound request using singleflight.Group. 50 dashboard clients trigger exactly 1 camera fetch.
Sub-100ms HTTP Snapshots
Extracts JPEG snapshots natively from the camera's ONVIF HTTP endpoint in under 100ms. Configurable in-memory TTL caching prevents camera hardware flooding.
Multipart MJPEG Streaming
Continuous multipart/x-mixed-replace live streams at /mjpeg with automatic PTZ cache invalidation and zero browser buffering issues.
PTZ Proxy & Eviction
Continuous move, stop, and preset positioning via ONVIF SOAP. Any pan, tilt, or zoom immediately evicts stale frame caches so dashboards stay in sync.
ONVIF PullPoint to MQTT
Subscribes to camera motion, tamper, and line-crossing event streams, publishing normalized JSON states to MQTT with Home Assistant auto-discovery.
Raspberry Pi & Edge Optimized
Pure zero-CGO Go binary. Runs as an unprivileged Systemd service or scratch Docker container, idling at less than 15MB RAM and 0% CPU.
Home Assistant Integration
Eliminate dashboard lag and camera crashes. Serve sub-100ms snapshots, PTZ D-pad controls, and automatic MQTT motion sensors directly into Lovelace.
Official Add-on Ready
Install directly from your Home Assistant Add-on Store with auto-configuration for the Mosquitto MQTT broker.
Sub-100ms Snapshots
Feed Lovelace picture-glance cards via native Generic Camera without freezing low-cost camera SOCs.
PTZ REST Controls
Pan, tilt, zoom, and recall presets right from your dashboard buttons with clean HTTP POST commands.
Zero-Config MQTT Discovery
ONVIF PullPoint events automatically appear as binary_sensor entities in Home Assistant.
Add the cam-proxy repository in Settings โ Add-ons โ Add-on Store โ Repositories:
https://github.com/smford/cam-proxy
When using the official Mosquitto broker add-on, cam-proxy auto-discovers broker credentials and provisions MQTT entities automatically.
Add your camera via Settings โ Devices & Services โ Generic Camera:
# Still Image URL (sub-100ms with hardware coalescing)
http://localhost:8080/api/v1/cameras/front-door/snapshot
# Stream Source (RTSP or MJPEG)
rtsp://192.168.1.50:554/live/ch0
# or: http://localhost:8080/api/v1/cameras/front-door/stream.mjpg
# Framerate: 2 fps (keeps Lovelace tablets fast and cool)
Define REST commands in configuration.yaml to drive camera movements:
rest_command:
cam_ptz_move:
url: "http://localhost:8080/api/v1/cameras/{{ camera_id }}/ptz"
method: POST
headers:
Content-Type: "application/json"
payload: '{"action":"move","pan":{{ pan | default(0) }},"tilt":{{ tilt | default(0) }},"zoom":{{ zoom | default(0) }}}'
cam_ptz_stop:
url: "http://localhost:8080/api/v1/cameras/{{ camera_id }}/ptz"
method: POST
headers:
Content-Type: "application/json"
payload: '{"action":"stop"}'
Display camera snapshots with auto-discovered motion sensor status:
type: picture-glance
title: Front Door
camera_image: camera.front_door
entities:
- binary_sensor.front_door_motion
- binary_sensor.front_door_tamper
View Complete Home Assistant Guide (with D-Pad & Automations) โ
Quick Installation
Install with Homebrew, Home Assistant Add-on, Docker, or standalone binary in seconds.
Install directly on macOS or Linux via the official Homebrew tap:
brew tap smford/tap
brew install cam-proxy
# Start the gateway with your configuration
cam-proxy -config cam-proxy.yaml
Install the official add-on in Home Assistant OS / Supervised:
# 1. In Home Assistant: Settings -> Add-ons -> Add-on Store -> Repositories
# 2. Add repository:
https://github.com/smford/cam-proxy
# 3. Locate 'cam-proxy', click 'Install', configure cameras, and click 'Start'
Run the minimal multi-arch container with host networking for ONVIF discovery:
docker run -d \
--name cam-proxy \
--restart unless-stopped \
--network host \
-v $(pwd)/cam-proxy.yaml:/etc/cam-proxy/cam-proxy.yaml:ro \
ghcr.io/smford/cam-proxy:latest
Install as an isolated, sandboxed Systemd daemon with DynamicUser security:
# Download latest Linux ARM64 binary for Raspberry Pi
curl -sL https://github.com/smford/cam-proxy/releases/download/v0.4.0/cam-proxy_0.4.0_linux_arm64.tar.gz | tar -xz
sudo mv cam-proxy /usr/local/bin/
# Install systemd unit and start service
sudo cp systemd/cam-proxy.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now cam-proxy
Discover cameras and generate a complete YAML configuration interactively:
# Probe local subnet for ONVIF cameras via WS-Discovery
cam-proxy --scan
# Launch the interactive terminal setup wizard
cam-proxy --tui
HTTP REST API
Clean, low-latency endpoints ready for Home Assistant, Scrypted, Frigate, or web dashboards.
| Method | Endpoint | Description | Headers / Type |
|---|---|---|---|
| GET | /api/v1/cameras/{id}/snapshot |
Coalesced, TTL-cached JPEG snapshot (< 100ms) | image/jpeg |
| GET | /api/v1/cameras/{id}/mjpeg |
Continuous multipart MJPEG video stream | multipart/x-mixed-replace |
| POST | /api/v1/cameras/{id}/ptz |
Pan/Tilt/Zoom continuous move, stop, or preset recall | application/json |
| GET | /api/v1/status |
Gateway uptime, configured cameras, and operational health | application/json |
| GET | /metrics |
Prometheus metrics (snapshots, latencies, cache hits) | text/plain |
| GET | /openapi.yaml |
Complete OpenAPI 3.1.0 specification | application/yaml |