Raja's Exocortex

Live Streaming Video on the Web

ffmpeg and nginx can be used to live stream video from any RTSP source like a CCTV camera on the web using DASH. The video can be viewed in any modern web browser.

Notes

  1. Video source must be h264 encoded for max compatibility. Set the camera to encode h264 at source so transcoding can be avoided later.

Step 1: Setup nginx (native)

apt install -y nginx libnginx-mod-rtmp
# Nginx config for streaming RTSP/RTMP video feeds to DASH
# Filename: /etc/nginx/nginx.conf

# Comment the below if running natively via systemd
daemon off;

user www-data;
pid /run/nginx.pid;

load_module modules/ngx_rtmp_module.so;

events {}

rtmp {
  server {
    listen 1935;        # default RTMP port
    chunk_size 4096;    # 4k chunk size
    allow publish all;  # Accept streams from anywhere

    application live {
      live on;
      record off;       # Don't save streams to disk

      dash on;
      dash_path /var/www/html/dash;
    }
  }
}

http {
  server {
    listen 80 default_server;

    root /var/www/html;

    server_name _;

    location / {
      try_files $uri $uri/ =404;
      add_header Access-Control-Allow-Origin *;
    }
  }

  # Mime types for all the files served
  types {
    text/html               html;
    application/javascript  js;
    application/dash+xml    mpd;
    audio/x-m4a             m4a;
    video/x-m4v             m4v;
  }
}

Step 1.1 Custom Docker Container

Official nginx docker container does not include the RTMP module and a custom nginx container needs to be built from Alpine and nginx and nginx-mod-rtmp package.

Build custom docker container using alpine and below Dockerfile.

# Dockerfile for custom nginx image including RTMP module
FROM alpine:3.19
WORKDIR /root
RUN apk add --no-cache nginx nginx-mod-rtmp

EXPOSE 80/tcp
CMD ["nginx", "-g", "daemon off;"]

Step 1.2 Docker Compose

# docker compose to run nginx with rtmp streaming 
# Filename: docker-compose.yaml
name: livevideo-example-com

services:
  livevideo:
	build: .
    container_name: livevideo.example.com
    restart: always

    volumes:
      - ./www:/var/www/html: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

Step 2: Setup ffmpeg

ffmpeg used for pulling a camera feed over rtsp and forwarding to nginx with the correct streaming container format.

Input From File for Testing

ffmpeg -re -i test.mp4 -c copy -f flv rtmp://nginx-ip/live/stream

Notes

  1. -re: play back file in real time by reading input at native frame rate, eg 1 hour video will play back in 1 hour.
  2. -i test.mp4: sample input file for streaming to nginx.
  3. -c copy: don't transcode the audio/video stream, just copy to output.
  4. -f flv: suitable streaming format.
  5. rtmp://nginx-ip/live/stream: nginx rtmp server location for sending the output stream.

Input From CCTV Camera

HikVision CCTV cameras provide RTSP stream in the default tcp:554 port and no path is required. Username and password are the same as what is required to access the camera web portal. Ensure the camera is set for h264 encoding.

# nginx IP: a.a.a.a
# camera IP: c.c.c.c
ffmpeg -re -i rtsp://username:password@c.c.c.c -c copy -f flv rtmp://a.a.a.a/live/stream

Notes

  1. If ffmpeg is segfaulting, use the ubuntu supplied ffmpeg binary (apt install ffmpeg) and not the static builds from ffmpeg.org.

Step 3: Dash.js

The stream.mpd can be viewed in the browser using dash.js and the associated HTML player.

Place the following two flies into /var/www/html web root folder and open nginx base URL (eg http://nginx-ip) in your browser.

3.1 dash.all.min.js

wget http://cdn.dashjs.org/latest/dash.all.min.js

3.2. index.html

<html>
  <head>
      <title>Livestream</title>
      <style>
          video {
              width: 640px;
              height: 360px;
          }
      </style>
  </head>
  <body>
      <div>
          <video id="videoPlayer" controls></video>
      </div>
      <script src="dash.all.min.js"></script>
      <script>
          (function(){
              var url = "/dash/stream.mpd";
              var player = dashjs.MediaPlayer().create();
              player.initialize(document.querySelector("#videoPlayer"), url, true);
          })();
      </script>
  </body>
</html>

References

  1. Dash.js
  2. FFmpeg Live Streaming Guide