aboutsummaryrefslogtreecommitdiffstats
authorClayton Gentry <clayton@podstock.io>2026-08-14 00:28:38 -0400
committerClayton Gentry <clayton@podstock.io>2026-08-14 00:28:38 -0400
commit81ecd3c87a1a1b2dcc4dc11e0371d5f460164481 (patch)
tree0626e2308fd79f1d588a2a92cf2aa6ed2c1742fb
parent2a325b0cdfe29544dd4801d85fd9a160df595260 (diff)
downloadcribd-81ecd3c87a.tar.gz zip
Serve RTSP streams over WebRTC
Add an Apple container launcher for MediaMTX with environment-based camera configuration and loopback-only WebRTC ports. Document local setup and establish the dated changelog structure.
Diffstat
.env.example (new)+1-0
.gitignore (new)+1-0
CHANGELOG.md (new)+5-0
README.md+36-141
bin/cribd (new)+35-0
changelog/2026-08-13-rtsp-to-webrtc.md (new)+14-0
6 files changed, 92 insertions(+), 141 deletions(-)
diff --git a/.env.example b/.env.example
@@ -0,0 +1,1 @@
+CRIBD_RTSP_URL=rtsp://user:pass@camera/stream
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,1 @@
+.env
diff --git a/CHANGELOG.md b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## 2026
+
+- **AUG.13** — [RTSP to WebRTC](changelog/2026-08-13-rtsp-to-webrtc.md)
diff --git a/README.md b/README.md
@@ -1,175 +1,70 @@
# cribd
-`cribd` is a local, private baby-monitor server. Its core job is to take video and audio from an RTSP camera and make the stream available to an iPad or phone through browser-friendly WebRTC.
+`cribd` turns one RTSP camera stream into browser-friendly WebRTC for a local, private baby monitor. [MediaMTX](https://github.com/bluenviron/mediamtx) handles the media path; optional hardware adapters can provide camera-specific events without putting vendor logic in the server.
-[MediaMTX](https://github.com/bluenviron/mediamtx) provides the media-routing layer. Optional hardware adapters can add camera-specific events without putting vendor logic into the core server.
-
> [!WARNING]
-> This project is a convenience monitor, not a medical device or a substitute for safe-sleep practices, direct supervision, or any medically indicated monitor. It does not provide breathing, heart-rate, or infant-safety monitoring.
+> This project is not a medical device or a substitute for safe-sleep practices, direct supervision, or any medically indicated monitor.
## Status
-This project is in the design and prototyping stage. The first target is an Apple-silicon Mac with an iPhone acting as an RTSP camera. The production target is the same ARM64 container stack on a Raspberry Pi 5 with a dedicated IP camera.
+The current prototype runs on an Apple silicon Mac, serves one loopback-only stream, and uses MediaMTX's built-in viewer. Custom viewers, event adapters, remote access, persistence, and Raspberry Pi deployment come later.
-## Architecture
+## Run locally
-```mermaid
-flowchart LR
- Camera["IP camera"]
+Requires macOS 26 on Apple silicon, Apple's [`container`](https://github.com/apple/container) CLI 1.0 or later, and an RTSP camera.
- subgraph Server["cribd on the home server"]
- Media["MediaMTX"]
- Core["cribd core"]
- Adapter["Optional hardware adapter"]
- end
+```sh
+cp .env.example .env
+# Set CRIBD_RTSP_URL in .env.
+./bin/cribd
+```
- Viewer["Local viewer<br/>iPad or iPhone"]
- Remote["Remote viewer<br/>over Tailscale"]
+Open `http://localhost:8889/crib`. Use H.264 video with Opus, G722, PCMA, or PCMU audio for WebRTC playback.
- Camera -->|"RTSP video/audio"| Media
- Media -->|"WebRTC"| Viewer
- Media -->|"WebRTC"| Remote
- Core -->|"Viewer and status"| Viewer
- Core -->|"Viewer and status"| Remote
+The launcher runs MediaMTX 1.20.0 and binds its WebRTC ports to loopback. Docker and Docker Compose are not required; `docker.io` is only the OCI image registry.
- Camera -.->|"Vendor event API"| Adapter
- Adapter -.->|"Generic events"| Core
- Core -.->|"Optional event stream"| Viewer
- Core -.->|"Optional event stream"| Remote
-```
+## Architecture
-The required path is:
-
```text
camera -> RTSP -> cribd/MediaMTX -> WebRTC -> viewer
+camera -> vendor event API -> optional adapter -> generic events -> cribd
```
-The event path is optional. Live video and audio must work without an adapter and must continue working if an adapter fails.
+The media path is required and independent of adapters. Adapter failures must never interrupt video or audio.
-## Responsibilities
+### Core
-### cribd core
-
-- Configure the camera's RTSP source.
-- Use MediaMTX to route RTSP media to WebRTC.
-- Serve the viewer.
+- Configure the camera as MediaMTX path `/crib`.
+- Route RTSP media to WebRTC and reconnect after interruptions.
- Report camera, stream, and server health.
-- Reconnect after stream interruptions.
-- Accept and forward generic events when an adapter is configured.
+- Accept generic events when an adapter is configured.
-MediaMTX handles RTSP, RTP, WebRTC, and ICE. Depending on configuration, the media packets between the camera and server may travel over UDP or TCP.
-
### Optional hardware adapter
-An adapter owns everything specific to a camera or vendor:
+- Authenticate with the camera's event API.
+- Subscribe and reconnect independently of the media path.
+- Discover capabilities such as motion or crying.
+- Translate vendor data into generic events.
-- authentication and connection to the hardware event API;
-- event subscription and reconnection;
-- capability discovery, such as `motion` or `crying`; and
-- translation into a generic event format.
+The core does not know how Reolink or another vendor delivers events. Without an adapter, event features are unavailable.
-For example:
+## Constraints
-```json
-{
- "type": "crying",
- "active": true,
- "source": "reolink",
- "timestamp": "2026-08-13T23:00:00Z"
-}
-```
-
-The core does not know how Reolink or another vendor delivers events. With no adapter installed, event features are simply unavailable.
-
-### Viewer
-
-The viewer is a small web/PWA client intended for an always-on iPad, with iPhones as secondary local or remote viewers. It plays WebRTC video and audio, shows connection health, invalidates frozen frames, and displays events when an adapter provides them.
-
-## Interfaces
-
-| Path | Protocol | Purpose |
-| --- | --- | --- |
-| Camera to MediaMTX | RTSP, with RTP over UDP or TCP | Video and audio |
-| MediaMTX to viewer | WebRTC | Low-latency browser playback |
-| Camera to adapter | Vendor-specific | Optional hardware events |
-| Adapter to `cribd` | Internal event interface | Generic capabilities and events |
-| `cribd` to viewer | WebSocket or SSE | Optional event delivery |
-| Remote viewer to server | Tailscale | Private remote access |
-
-## Proposed API
-
-The core API is deliberately small:
-
-```text
-GET /api/status
-```
-
-When an event adapter is configured, `cribd` may also expose:
-
-```text
-WS /api/events
-```
-
-Adapter capabilities and health may appear in the status response only when an adapter is present.
-
-## Requirements
-
- Local viewing must work without public internet access.
-- Camera, stream, and server failures must become obvious within seconds.
-- Frozen frames must not continue looking live.
-- RTSP reconnection must use bounded backoff.
-- The stack must restart automatically after power loss or a process crash.
-- Adapter failures must be reported separately and must not interrupt media.
-- The camera, MediaMTX, and `cribd` must never be exposed directly to the public internet.
-- Remote access must use Tailscale identity and device authorization.
-- Camera credentials must remain on the home server and adapter credentials must remain isolated to their adapter.
+- Stream failures and frozen frames must become obvious within seconds.
+- The stack must recover after camera, process, or power interruptions.
+- Camera and adapter credentials remain on the home server.
+- Nothing is exposed directly to the public internet.
+- Remote access will use Tailscale identity and device authorization.
-## Build sequence
+## Roadmap
-1. Prove phone camera -> RTSP -> MediaMTX on Mac -> browser WebRTC.
-2. Add the `cribd` viewer, status, explicit offline states, and automatic reconnects.
-3. Swap in the production camera and test audio, night vision, and restart recovery.
-4. Move the same stack to a Raspberry Pi and configure automatic startup.
-5. Add Tailscale and test remote viewing over cellular.
-6. Define the generic event interface, then implement Reolink support as a separate adapter.
+1. Prove RTSP to MediaMTX to browser WebRTC on a Mac.
+2. Add the viewer, health reporting, and explicit offline states.
+3. Test a dedicated camera, night vision, audio, and restart recovery.
+4. Move the stack to a Raspberry Pi and add automatic startup.
+5. Add Tailscale, define the event interface, and build a Reolink adapter.
-## Acceptance criteria
-
-### Core
-
-- An iPad reaches live video and audio in one action.
-- End-to-end latency feels live.
-- An internet outage does not interrupt local viewing.
-- The system recovers automatically after the camera or server is power-cycled.
-- A dead camera, stream, or server is unmistakably visible within seconds.
-- An authorized iPhone can view through Tailscale without public port forwarding.
-- The system works with a plain RTSP camera that provides no event API.
-
-### Optional adapters
-
-- `cribd` reports the adapter's capabilities and health.
-- Generic events reach connected viewers with negligible application delay.
-- Disabling or crashing the adapter does not interrupt video or audio.
-- Vendor-specific protocols do not leak into the media core.
-
-## Initial target
-
-- **Camera:** Reolink E1 Pro or another compatible RTSP camera
-- **Development server:** Apple-silicon Mac
-- **Production server:** Raspberry Pi 5
-- **Viewer:** iPad and iPhones
-- **Media engine:** MediaMTX
-- **Remote network:** Tailscale
-- **First optional adapter:** Reolink
-
-## Open questions
-
-- Should adapters run in-process, as plugins, or as isolated sidecars?
-- What is the smallest useful generic event envelope?
-- How should adapters advertise capabilities and health?
-- Should live events use WebSocket or server-sent events?
-- Will background events eventually require a native iOS/iPadOS viewer?
-
## License
Licensed under the [MIT License](LICENSE).
diff --git a/bin/cribd b/bin/cribd
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+set +x
+set -eu
+
+error() {
+ printf '%s\n' "cribd: $1" >&2
+ exit 1
+}
+
+command -v container >/dev/null 2>&1 ||
+ error "Apple's container CLI is required. Install container 1.0 or later."
+
+project_dir=$(CDPATH= cd -- "${0%/*}/.." && pwd)
+
+if [ "${CRIBD_RTSP_URL+x}" != x ] && [ -f "$project_dir/.env" ]; then
+ . "$project_dir/.env"
+fi
+
+case "${CRIBD_RTSP_URL-}" in
+ rtsp://* | rtsps://*) rtsp_url=$CRIBD_RTSP_URL ;;
+ "") error "set CRIBD_RTSP_URL in .env or the environment." ;;
+ *) error "CRIBD_RTSP_URL must use rtsp:// or rtsps://." ;;
+esac
+
+unset CRIBD_RTSP_URL
+
+container system start
+
+exec container run --rm --name cribd-media \
+ --env "MTX_PATHS_CRIB_SOURCE=$rtsp_url" \
+ --env "MTX_WEBRTCADDITIONALHOSTS=127.0.0.1" \
+ --publish "127.0.0.1:8889:8889/tcp" \
+ --publish "127.0.0.1:8189:8189/udp" \
+ docker.io/bluenviron/mediamtx:1.20.0
diff --git a/changelog/2026-08-13-rtsp-to-webrtc.md b/changelog/2026-08-13-rtsp-to-webrtc.md
@@ -0,0 +1,14 @@
+# RTSP to WebRTC
+
+Date: **AUG.13.2026**
+
+`cribd` serves a single RTSP camera stream to browsers over WebRTC.
+
+```sh
+cp .env.example .env
+./bin/cribd
+```
+
+Open `http://localhost:8889/crib`.
+
+The launcher starts Apple's container service, maps the camera to `/crib`, and forwards MediaMTX's WebRTC HTTP and UDP ports to macOS. MediaMTX reconnects to the RTSP source after interruptions. This version supports one loopback-only stream on the development Mac; hardware events and remote access are not included.