# Live Streaming Setup Guide This guide will help you set up hybrid live streaming with HLS + automatic VOD archiving to R2. ## Architecture ``` OBS → RTMP → nginx-rtmp → HLS chunks → Real-time uploader → R2 → Cloudflare Worker → Viewers ↓ VOD Archive ``` ## Quick Start ### 1. Start the Streaming Stack ```bash cd streaming ./start-streaming.sh ``` This will: - Start nginx-rtmp server (Docker) - Start HLS chunk uploader to R2 - Display connection info ### 2. Configure OBS #### Settings → Stream 1. **Service**: Custom 2. **Server**: `rtmp://localhost/live` 3. **Stream Key**: `my-stream` (or choose your own name like `gaming`, `podcast`, etc.) #### Settings → Output **Recommended Settings for 1080p60:** - **Output Mode**: Advanced - **Encoder**: x264 or Hardware (NVENC/AMD/QuickSync) - **Rate Control**: CBR - **Bitrate**: 6000 Kbps (adjust based on upload speed) - **Keyframe Interval**: 2 seconds - **CPU Usage Preset**: veryfast (or faster for lower-end CPUs) - **Profile**: high - **Tune**: zerolatency **For 720p60:** - **Bitrate**: 4500 Kbps **For 1080p30:** - **Bitrate**: 4500 Kbps #### Settings → Video - **Base Resolution**: 1920x1080 (or your monitor resolution) - **Output Resolution**: 1920x1080 or 1280x720 - **FPS**: 60 or 30 #### Settings → Advanced - **Process Priority**: High (optional, for smoother streaming) ### 3. Start Streaming 1. Click "Start Streaming" in OBS 2. Wait 5-10 seconds for HLS chunks to be created 3. Access your stream at: - Local (testing): `http://localhost:8081/hls/my-stream.m3u8` - Public: `https://videos.jeffemmett.com/live/my-stream/my-stream.m3u8` ### 4. Watch Your Stream #### In a browser: - Use the HLS.js player - Or use VLC: Media → Open Network Stream → Enter URL #### Test Player HTML: ```html
``` ### 5. Stop Streaming 1. Click "Stop Streaming" in OBS 2. (Optional) Stop the streaming stack: ```bash ./stop-streaming.sh ``` ## How It Works ### Real-Time Streaming 1. **OBS** sends RTMP stream to `rtmp://localhost/live/my-stream` 2. **nginx-rtmp** receives the stream and segments it into HLS chunks: - Creates `.m3u8` playlist (updated every 2 seconds) - Creates `.ts` video chunks (2-second segments) 3. **HLS Uploader** watches the `hls/` directory: - Detects new chunks as they're created - Immediately uploads to R2 at `live/my-stream/` 4. **Cloudflare Worker** serves the chunks: - Viewers request `.m3u8` playlist - Player loads `.ts` chunks as they become available - ~5-10 second latency end-to-end ### Automatic VOD Archiving - All HLS chunks remain in R2 after stream ends - Can be concatenated into full VOD recording - Or left as is for HLS replay ## Troubleshooting ### Stream won't connect - Check if nginx-rtmp is running: `docker ps | grep obs-streaming-server` - Check nginx logs: `docker logs obs-streaming-server` - Verify RTMP URL: `rtmp://localhost/live` (not `rtmp://localhost:1935/live`) ### Stream is choppy/buffering - Lower bitrate in OBS Output settings - Check CPU usage - try faster encoder preset - Check upload bandwidth - run speed test ### Can't see stream on public URL - Wait 5-10 seconds after starting stream - Check HLS uploader logs: `tail -f streaming/hls-uploader.log` - Verify chunks are being uploaded to R2 ### High latency (>15 seconds) - Normal for HLS - typical latency is 6-15 seconds - Lower `hls_fragment` in nginx.conf to 1s (will increase bandwidth) - Consider WebRTC for sub-second latency (different setup) ## Advanced Configuration ### Multiple Quality Levels Edit `streaming/nginx/nginx.conf`: ```nginx hls_variant _360p BANDWIDTH=800000; hls_variant _720p BANDWIDTH=2800000; hls_variant _1080p BANDWIDTH=5000000; ``` ### Change Stream Key Use any stream key you want - it becomes the folder name in R2: - Stream key: `my-stream` → R2 path: `live/my-stream/` (default) - Stream key: `gaming` → R2 path: `live/gaming/` - Stream key: `podcast-ep-5` → R2 path: `live/podcast-ep-5/` ### Custom Domain If using a different domain, update in `.env`: ``` PUBLIC_DOMAIN=streams.yourdomain.com ``` ## Performance Notes ### Bandwidth Usage - **720p60 @ 4500kbps**: ~2.0 GB/hour - **1080p60 @ 6000kbps**: ~2.7 GB/hour - **1080p30 @ 4500kbps**: ~2.0 GB/hour ### R2 Costs - **Storage**: $0.015/GB/month - **Class A Operations** (uploads): $4.50/million - **Class B Operations** (downloads): $0.36/million - **Egress**: Free to Cloudflare network Example cost for 1 hour stream at 1080p60: - Storage: ~2.7GB × $0.015 = $0.04/month - Uploads: ~1800 chunks × $0.0000045 = $0.008 - **Total: ~$0.05** per hour streamed ### Viewer Bandwidth Viewers consume: - **720p**: ~4.5 Mbps - **1080p**: ~6 Mbps ## File Structure ``` streaming/ ├── docker-compose.yml # nginx-rtmp container config ├── nginx/ │ └── nginx.conf # nginx RTMP + HLS config ├── scripts/ │ ├── on_publish.sh # Called when stream starts │ └── on_publish_done.sh # Called when stream ends ├── hls/ # HLS output directory (auto-created) │ ├── my-stream.m3u8 # Playlist │ └── my-stream-*.ts # Video chunks ├── start-streaming.sh # Start everything ├── stop-streaming.sh # Stop everything └── STREAMING-SETUP.md # This file ``` ## Next Steps - Create a custom player page - Set up stream notifications (when stream goes live) - Add stream recording/VOD conversion - Implement chat/comments - Add stream analytics ## Support For issues, check: 1. Docker logs: `docker logs obs-streaming-server` 2. HLS uploader logs: `tail -f streaming/hls-uploader.log` 3. Nginx stats: `http://localhost:8081/stat`