Unifeed UniFi Protect Camera → YouTube Live Streaming — Deployment Guide
This document covers every method for getting a live RTSP stream from UniFi Protect cameras onto YouTube Live — from the managed webcam.io cloud service to self-hosted Docker containers running FFmpeg.
Architecture
┌──────────────┐ RTSPS ┌─────────────────┐ RTMP ┌──────────────┐
│ UniFi │─────────────▶│ Relay Service │────────────▶│ YouTube │
│ Protect │ :7441 │ (webcam.io / │ :1935 │ Live │
│ Camera │ │ FFmpeg/Docker) │ │ │
└──────────────┘ └─────────────────┘ └──────────────┘
The camera outputs RTSPS. A relay transcodes/forwards it as RTMP to YouTube's ingest servers.
Prerequisites
- UniFi Protect controller with one or more cameras
- YouTube account with live streaming enabled (requires 24-hour verification after first enable)
- Network access from the relay machine to the Protect controller (same LAN or VPN)
- For webcam.io: port forwarding + DDNS on your router
- For self-hosted: Docker or FFmpeg installed on a machine with stable uptime
Step 1 — Enable RTSP on UniFi Protect
Enable the RTSP Stream
- Open the UniFi Protect web UI
- Go to Devices → select your camera
- Navigate to Settings → Advanced → RTSP
- Toggle RTSP On
- Choose quality: High, Medium, or Low
- Copy the RTSP URL displayed — you'll need this later
RTSP URL Formats
| Type | URL Pattern | Port |
|---|---|---|
| RTSPS (via Protect) | rtsps://<protect_ip>:7441/<camera_id> |
7441 |
| RTSP (direct to camera) | rtsp://<camera_ip>:7447/<camera_id> |
7447 |
Create a Dedicated RTSP User
Don't use your admin account. Create a restricted local user for streaming.
- Go to UniFi OS → Admins & Users
- Create a new local user
- Check "Restrict to local access only"
- Set a strong username/password
Authenticated RTSP URL:
rtsps://streamuser:yourpassword@192.168.1.1:7441/CAMERA_ID
Test the Stream
Open in VLC: Media → Open Network Stream and paste your RTSP URL. If it plays, you're good.
Step 2 — YouTube Live Setup
- Go to YouTube Studio
- Click Create → Go Live
-
Choose "Schedule stream" (not "Stream" tab)
Important: For 24/7 streaming, always use Scheduled Streams. The default "Stream Tab" will cause problems with continuous streams.
- Set your stream title, description, and visibility
- Copy your Stream Key — treat this like a password
YouTube RTMP Ingest URL
| Protocol | URL |
|---|---|
| RTMP | rtmp://a.rtmp.youtube.com/live2 |
| RTMPS (secure) | rtmps://a.rtmp.youtube.com:443/live2 |
Recommended Encoding Settings
| Resolution | FPS | Bitrate |
|---|---|---|
| 720p | 30 | 1,500 – 4,000 Kbps |
| 1080p | 30 | 4,500 – 9,000 Kbps |
| 4K | 30 | 13,000 – 34,000 Kbps |
Step 3 — webcam.io Setup Cloud
webcam.io is a cloud relay service that pulls your camera's RTSP stream and pushes it to YouTube. No local software required.
Pricing
| Plan | Price | Cameras | Features |
|---|---|---|---|
| Free | $0 | — | Time-lapse only |
| LIVE | ~$5/mo | Up to 25 | H.264 live streaming |
| LIVE+ | Higher | Up to 25 | H.265, MJPEG transcoding |
Each camera that streams live requires its own LIVE plan. 7-day free trial available (no CC).
Network Requirements
Because webcam.io connects to your camera from the cloud, you must expose the RTSP port to the internet.
Port Forwarding
- Reserve your Protect controller's LAN IP in your router's DHCP settings
- Forward an external port (e.g.
8554) to internal7441on the Protect IP - Verify the forward works with an online port-check tool
Dynamic DNS (DDNS)
Your ISP likely assigns a dynamic public IP. Set up DDNS (DuckDNS, No-IP, or your router's built-in client) so webcam.io always has a valid hostname.
Connect Camera in webcam.io
- Sign up at webcam.io
- Add a new camera
- Enter your RTSPS URL with DDNS hostname:
rtsps://user:pass@yourhost.ddns.net:8554/CAMERA_ID - Link your YouTube account and enter the stream key
- Select "Scheduled stream" mode
- Start streaming
Alternative: Docker + FFmpeg Recommended Self-Hosted
Run FFmpeg in a Docker container with auto-restart. No port forwarding needed — runs on your LAN.
Setup
-
Clone this repo:
git clone https://github.com/kjcornwell/Unifeed.git cd Unifeed -
Copy and fill in environment variables:
cp .env.example .env # Edit .env with your actual values -
Start the stream:
docker compose up -d
docker-compose.yml
version: '3.8'
services:
camera1:
image: jrottenberg/ffmpeg:latest
container_name: unifeed-${CAMERA_1_NAME:-camera1}
restart: unless-stopped
network_mode: host
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
command: >
-f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100
-thread_queue_size 256
-rtsp_transport tcp
-i rtsps://${RTSP_USER}:${RTSP_PASS}@${PROTECT_IP}:7441/${CAMERA_1_ID}
-shortest
-vf "fps=30"
-map 0:a:0 -c:a aac -b:a 128k
-map 1:v:0 -c:v libx264 -preset veryfast -crf 28 -g 60
-maxrate 4500k -bufsize 9000k
-f flv rtmp://a.rtmp.youtube.com/live2/${YT_STREAM_KEY_1}
.env File
RTSP_USER=streamuser
RTSP_PASS=changeme
PROTECT_IP=192.168.1.1
CAMERA_1_ID=your-camera-id-here
CAMERA_1_NAME=front-beach
YT_STREAM_KEY_1=xxxx-xxxx-xxxx-xxxx-xxxx
FFmpeg Flags Explained
| Flag | Purpose |
|---|---|
-f lavfi -i anullsrc=... | Generate silent audio (YouTube requires an audio track) |
-thread_queue_size 256 | Input buffer for smoother streaming |
-rtsp_transport tcp | Use TCP for RTSP (more reliable than UDP) |
-shortest | End when the shortest input ends |
-vf "fps=30" | Force 30 FPS output |
-c:v libx264 -preset veryfast | H.264 encoding, fast preset (low CPU) |
-crf 28 | Quality level (lower = better, 28 is decent for webcam) |
-g 60 | Keyframe every 60 frames (2 sec at 30fps) |
-maxrate 4500k -bufsize 9000k | Bitrate cap for consistent upload |
-f flv | FLV container (required for RTMP to YouTube) |
Management Commands
# View logs
docker logs -f unifeed-front-beach
# Restart
docker restart unifeed-front-beach
# Stop
docker compose down
# Start
docker compose up -d
Alternative: FFmpeg Direct Quick & Simple
Run FFmpeg straight from the command line. Good for testing, not ideal for 24/7.
Passthrough (no re-encode)
ffmpeg \
-rtsp_transport tcp \
-i "rtsps://user:pass@192.168.1.1:7441/CAMERA_ID" \
-c:v copy -c:a copy \
-f flv rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY
With Silent Audio + Re-encode
ffmpeg \
-f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 \
-thread_queue_size 256 -rtsp_transport tcp \
-i "rtsps://user:pass@192.168.1.1:7441/CAMERA_ID" \
-shortest \
-vf "fps=30" \
-map 0:a:0 -c:a aac -b:a 128k \
-map 1:v:0 -c:v libx264 -preset veryfast -crf 28 -g 60 \
-f flv rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY
With GPU Hardware Encoding
| GPU | Encoder Flag |
|---|---|
| NVIDIA | -c:v h264_nvenc |
| Intel | -c:v h264_qsv |
| AMD | -c:v h264_amf |
Alternative: OBS Studio GUI
- Open OBS → Sources → + → Media Source
- Uncheck "Local File" → paste RTSP URL
- Go to Settings → Stream → Service: YouTube
- Paste your stream key → Start Streaming
Alternative: MediaMTX Relay Advanced
MediaMTX acts as a local RTSP proxy. Useful if multiple consumers need the same camera stream.
mediamtx.yml
paths:
camera1:
source: rtsps://user:pass@192.168.1.1:7441/CAMERA_ID
sourceProtocol: rtsps
sourceOnDemand: yes
Then pipe through FFmpeg
ffmpeg \
-f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 \
-i rtsp://localhost:8554/camera1 \
-shortest \
-map 0:a:0 -c:a aac -b:a 128k \
-map 1:v:0 -c:v copy \
-f flv rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY
Alternative: Windows Service (NSSM) Windows
Run FFmpeg as a Windows service that starts at boot — no user login required.
Install
- Download NSSM and FFmpeg
-
Create
stream.bat:@echo off C:\Tools\ffmpeg\bin\ffmpeg.exe ^ -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 ^ -thread_queue_size 256 -rtsp_transport tcp ^ -i rtsps://user:pass@192.168.1.1:7441/CAMERA_ID ^ -shortest -vf "fps=30" ^ -map 0:a:0 -c:a aac -b:a 128k ^ -map 1:v:0 -c:v libx264 -preset veryfast -crf 28 -g 60 ^ -f flv rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY -
Register the service (Admin CMD):
nssm install UnifeedStream C:\path\to\stream.bat nssm start UnifeedStream
Manage
nssm start UnifeedStream
nssm stop UnifeedStream
nssm restart UnifeedStream
nssm remove UnifeedStream confirm
Multi-Camera Setup
Duplicate the service block in docker-compose.yml for each camera. Each needs its own YouTube stream key.
services:
front-beach:
image: jrottenberg/ffmpeg:latest
container_name: unifeed-front-beach
restart: unless-stopped
network_mode: host
command: >
-f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100
-thread_queue_size 256 -rtsp_transport tcp
-i rtsps://${RTSP_USER}:${RTSP_PASS}@${PROTECT_IP}:7441/${CAMERA_1_ID}
-shortest -vf "fps=30"
-map 0:a:0 -c:a aac -b:a 128k
-map 1:v:0 -c:v libx264 -preset veryfast -crf 28 -g 60
-maxrate 4500k -bufsize 9000k
-f flv rtmp://a.rtmp.youtube.com/live2/${YT_STREAM_KEY_1}
pool-deck:
image: jrottenberg/ffmpeg:latest
container_name: unifeed-pool-deck
restart: unless-stopped
network_mode: host
command: >
-f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100
-thread_queue_size 256 -rtsp_transport tcp
-i rtsps://${RTSP_USER}:${RTSP_PASS}@${PROTECT_IP}:7441/${CAMERA_2_ID}
-shortest -vf "fps=30"
-map 0:a:0 -c:a aac -b:a 128k
-map 1:v:0 -c:v libx264 -preset veryfast -crf 28 -g 60
-maxrate 4500k -bufsize 9000k
-f flv rtmp://a.rtmp.youtube.com/live2/${YT_STREAM_KEY_2}
Troubleshooting
Can't connect to RTSP stream
- Verify RTSP is enabled in Protect camera settings
- Test with VLC first
- Check camera ID is correct (copy from Protect UI)
- Try
-rtsp_transport udpinstead oftcp - Ensure ports 7441/7447 aren't blocked by firewall
Stream keeps disconnecting
- Use
-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5before the input URL - Increase buffer:
-thread_queue_size 512 - Use MediaMTX as a relay for better stability
- Check network between streaming machine and Protect controller
YouTube rejects the stream
- Add a silent audio track (
anullsrc) — YouTube requires audio - Ensure you're using H.264 video codec (not H.265)
- Verify stream key is correct and hasn't been rotated
- Use a scheduled stream, not the default stream tab
High CPU usage
- Use
-c:v copyto skip re-encoding (if camera outputs H.264) - Use GPU encoding (
h264_nvenc,h264_qsv,h264_amf) - Lower preset to
ultrafast - Reduce resolution at camera level instead of in FFmpeg
YouTube stream dies when no viewers
- Use Scheduled Streams instead of "Stream Tab"
- YouTube may stop processing after extended zero-viewer periods
Security
.env file is in .gitignore.
Keep stream keys and RTSP passwords out of version control.
- Create a dedicated RTSP user with minimal permissions — don't use your admin account
- Use RTSPS (encrypted) over RTSP whenever possible
- Keep cameras on an isolated VLAN
- If using webcam.io, use a non-standard external port and strong password
- Rotate your YouTube stream key if it's ever exposed
- Keep UniFi Protect firmware updated
Decision Matrix
| Method | Cost | Complexity | Reliability | Best For |
|---|---|---|---|---|
| webcam.io | $5+/mo | Low | High | Hands-off, non-technical |
| Docker + FFmpeg | Free | Medium | High | Production, multi-camera |
| FFmpeg Direct | Free | Medium | Medium | Testing, one-off streams |
| OBS Studio | Free | Low | Low | Preview, temporary use |
| Windows Service | Free | High | High | Windows-only servers |
| MediaMTX + FFmpeg | Free | High | Very High | Multi-consumer, advanced |
Quick Reference
Test RTSP in VLC
vlc rtsps://user:pass@192.168.1.1:7441/CAMERA_ID
One-liner: RTSP → YouTube
ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 \
-thread_queue_size 256 -rtsp_transport tcp \
-i "rtsps://user:pass@192.168.1.1:7441/CAMERA_ID" \
-shortest -vf "fps=30" \
-map 0:a:0 -c:a aac -b:a 128k \
-map 1:v:0 -c:v libx264 -preset veryfast -crf 28 -g 60 \
-f flv rtmp://a.rtmp.youtube.com/live2/STREAM_KEY
Docker Quick Start
git clone https://github.com/kjcornwell/Unifeed.git && cd Unifeed
cp .env.example .env # fill in your values
docker compose up -d # start streaming
docker logs -f unifeed-front-beach # watch logs
Key Ports
| Port | Protocol | Service |
|---|---|---|
| 7441 | RTSPS | UniFi Protect (encrypted) |
| 7447 | RTSP | UniFi Protect (direct) |
| 1935 | RTMP | YouTube ingest |
| 443 | RTMPS | YouTube ingest (encrypted) |
Unifeed — Last updated February 2026 — github.com/kjcornwell/Unifeed