Convert HLS streams to RTSP. Single binary, zero external dependencies (no FFmpeg, no MediaMTX).
Built with gohlslib and gortsplib.
- Multiple HLS sources → multiple RTSP paths
- RTSP Basic authentication
- H.264 passthrough (no transcoding)
- Auto-reconnect on HLS failures
- Built-in stream quality metrics (FPS, jitter, drift, smoothness)
- Single static binary, ~11MB
# Build
go build -o hls2rtsp ./cmd/hls2rtsp
# Create config
cp config.example.yaml config.yaml
# Edit config.yaml with your HLS URLs
# Run
./hls2rtsp --config config.yaml# Create config
cp config.example.yaml config.yaml
# Edit config.yaml
docker run -d --name hls2rtsp \
-v $(pwd)/config.yaml:/etc/hls2rtsp/config.yaml:ro \
-p 8554:8554 \
ghcr.io/overpod/hls2rtsp:latestservices:
hls2rtsp:
image: ghcr.io/overpod/hls2rtsp:latest
container_name: hls2rtsp
restart: unless-stopped
volumes:
- ./config.yaml:/etc/hls2rtsp/config.yaml:ro
ports:
- "8554:8554" # RTSP (TCP)
- "8000:8000/udp" # RTP (optional, for UDP transport)
- "8001:8001/udp" # RTCP (optional, for UDP transport)Note: UDP ports 8000/8001 are only needed if RTSP clients request UDP transport. Most clients (VLC, ffplay) use TCP interleaved by default — in that case only port 8554 is required.
server:
port: "8554"
auth:
enabled: true
username: admin
password: admin
streams:
camera1:
url: https://example.com/stream1/mono.m3u8
camera2:
url: https://example.com/stream2/mono.m3u8
metrics:
enabled: true
interval: 30sAfter starting, streams are available at:
rtsp://admin:admin@localhost:8554/camera1
rtsp://admin:admin@localhost:8554/camera2
Open in VLC: Media → Open Network Stream → paste the RTSP URL.
HLS source 1 ──► gohlslib.Client ──► Bridge 1 ──► /camera1 ──► RTSP clients
HLS source 2 ──► gohlslib.Client ──► Bridge 2 ──► /camera2 ──► RTSP clients
│
gortsplib.Server
:8554
- Each HLS source is handled by a separate bridge
gohlslibdownloads HLS segments, demuxes MPEG-TS, extracts H.264 NAL units with DTS-based pacing- NAL units are packetized into RTP (RFC 6184) by
gortspliband sent to RTSP clients - No transcoding — original H.264 quality is preserved
When metrics.enabled: true, the log shows periodic quality reports:
[camera1] frames=600 fps=20.0 jitter=0.08ms drift=0.0ms drift_max=4.9ms gaps=0 smooth=99/100
- fps — actual framerate from PTS timestamps
- jitter — standard deviation of frame intervals (lower = smoother)
- drift — difference between expected and actual delivery timing
- gaps — count of frame intervals > 2x average
- smooth — overall smoothness score (0–100)
MIT