RTSP Video Streaming over HTTPS
RTSPtoWeb converts RTSP video streams into formats that can be consumed in a web browser, such as MSE, WebRTC, or HLS. It does not require any additional software and can pull RTSP streams from a camera and publish them over HTTPS.
Why RTSPtoWeb?
I tested several open-source tools to publish video streams over HTTPS using an HIK-VISION IP camera. The results are as follows:
Tools | Latency (s) |
---|---|
Hikvisin portal | 0.5 |
FFPlay | 2.4 |
RTSPtoWeb | 1.2 |
SRS WHEP | 2.3 |
SRS HTTP-FLV | 4.7 |
In comparison, RTSPtoWeb has a lower latency than the other tools and is directly published over HTTPS behind a reverse proxy.
Note: Video source must be
h264
encoded for max compatibility. Set the camera to encode h264 at source so transcoding can be avoided later.
Deployment
- Docker compose file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Filename: /srv/livevideo.example.com/docker-compose.yaml
# Purpose: To publish RTSP video stream over HTTPS
name: livevideo-example-com
services:
rtsp-to-web:
image: ghcr.io/deepch/rtsptoweb:latest
container_name: livevideo-rtsp-to-web.example.com
restart: always
volumes:
- ./config.json:/config/config.json
livevideo:
image: nginx:latest
container_name: livevideo.example.com
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
labels:
- com.centurylinklabs.watchtower.enable=true
- traefik.enable=true
- traefik.http.routers.livevideo.rule=Host(`livevideo.example.com`)
- traefik.http.routers.livevideo.tls=true
- traefik.http.routers.livevideo.tls.certresolver=lets-encrypt
depends_on:
- rtsp-to-web
- RTSPtoWeb Configuration
Filename: /srv/livevideo.example.com/config.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
"channel_defaults": {
"on_demand": true
},
"server": {
"debug": true,
"http_debug": false,
"http_demo": true,
"http_dir": "",
"http_login": "api-username",
"http_password": "api-password",
"http_port": ":8083",
"https": false,
"https_auto_tls": false,
"https_auto_tls_name": "",
"https_cert": "",
"https_key": "",
"https_port": "",
"ice_credential": "",
"ice_servers": [],
"ice_username": "",
"log_level": "info",
"rtsp_port": ":5541",
"token": {
"backend": "",
"enable": false
},
"webrtc_port_max": 0,
"webrtc_port_min": 0
},
"streams": {
"mystream": {
"channels": {
"0": {
"name": "camera-1",
"url": "rtsp://username:password@ip"
},
"1": {
"name": "camera-2",
"url": "rtsp://username:password@ip"
}
},
"name": "Live Video Stream"
}
}
}
- Nginx Configuration
Filename: /srv/livevideo.example.com/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Filename: /etc/nginx/nginx.conf
events {
worker_connections 1024;
}
http {
server {
listen 80;
include /etc/nginx/mime.types;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri /index.html;
add_header 'Access-Control-Allow-Origin' '*';
}
location /stream/ {
proxy_pass http://rtsp-to-web:8083;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
- HTML Video Player
Filename: /srv/livevideo.example.com/html/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>RTSPtoWeb HLS-LL</title>
</head>
<body>
<h1>RTSPtoWeb HLS-LL</h1>
<div>
<p></p>
<video id="hlsll-video" autoplay muted playsinline controls style="width: 100%; height: 100%;"></video>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const videoEl = document.querySelector('#hlsll-video')
var path = window.location.pathname;
var parts = path.split('/'); // livevideo.example.com/mystream/channel/0
var url = window.location.protocol + '//' + window.location.hostname;
var stream_name = parts[parts.length - 3] + '/';
var channel = parts[parts.length - 2] + '/';
var channel_no = parts[parts.length - 1];
// https://livevideo.example.com/stream/{0}/{1}/{2}/hlsll/live/index.m3u8
var stream_url = url + '/stream/' + stream_name + channel + channel_no + '/hlsll/live/index.m3u8';
if (Hls.isSupported()) {
const hls = new Hls()
hls.loadSource(stream_url)
hls.attachMedia(videoEl)
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
videoEl.src = hlsllUrl
videoEl.addEventListener('loadedmetadata', function () {
videoEl.play();
});
}
})
</script>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</body>
</html>
Note: Here
Traefik
is used as reverse proxy. Traefik docker labels are attached to the nginx container to publish behind fully qualified domain name.
Now, the video stream of Camera 1
& Camera 2
is available in https://livevideo.example.com/mystream/channel/0
and https://livevideo.example.com/mystream/channel/1
Reference:
- GitHub Docs: https://github.com/deepch/RTSPtoWeb
- RTSPtoWeb API Docs: https://github.com/deepch/RTSPtoWeb/blob/master/docs/api.md