242 lines
6.4 KiB
Markdown
242 lines
6.4 KiB
Markdown
# 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
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||
</head>
|
||
<body>
|
||
<video id="video" controls style="width: 100%; max-width: 1280px;"></video>
|
||
<script>
|
||
const video = document.getElementById('video');
|
||
const src = 'https://videos.jeffemmett.com/live/my-stream/my-stream.m3u8';
|
||
|
||
if (Hls.isSupported()) {
|
||
const hls = new Hls();
|
||
hls.loadSource(src);
|
||
hls.attachMedia(video);
|
||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||
video.play();
|
||
});
|
||
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||
video.src = src;
|
||
}
|
||
</script>
|
||
</body>
|
||
</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`
|