使用nginx,在一个容器中实现RTMPS和HLS的播放(nginx + stream + rtmp)

由于某种原因,我们似乎需要建立一个使用nginx-rtmp-module自己搭建的RTMP视频传输平台。这篇文章是关于这个过程中的笔记。

实现的内容

这篇文章的结构如下所示。

nginx-rtmp.drawio (2).png

正如标题所示,我们通过一个容器来实现视频传输和视频分发。

首先是视频传输的发送端。
我们使用OBS或者Microsoft Teams将视频传输到443端口。同时,使用RTMPS来防止窃听和篡改。首先,视频会通过nginx的stream模块接收,并且使用SSL通信进行终止。接着,通过SNI选择转发目标的后端,并最终被发送到nginx的RTMP模块,并保存为HLS文件(*.m3u8, *.ts)。

在接收端,我们使用网络浏览器通过HTTPS访问443端口。我们使用nginx的stream模块来终止SSL通信,并通过SNI将数据传输至HTTP的后端。通过加载HLS文件和HTML播放器,我们可以播放视频。

通过这种配置,可以使用具有RTMP视频传输功能的客户端,例如OBS或Microsoft Teams,将视频传送到远程地点。

关于stunnel的信息

如果使用nginx和RTMPS关键词进行搜索,可以发现一种使用stunnel终止来自OBS或Microsoft Teams的SSL的配置。但是,使用nginx的流模块可以实现相同的SSL终止,并且可以享受nginx访问控制的好处,并且可以集中配置。因此,最终不再使用stunnel。

nginx的Dockerfile

只需一种选择,以下是中文的原生改写版本:

Dockerfile对tiangolo/nginx-rtmp镜像进行了一些修改。由于这个镜像没有包含nginx的stream模块,因此我添加了–with-stream和–with-stream_ssl_module选项来配置它。

FROM buildpack-deps:bullseye

# Versions of nginx and nginx-rtmp-module to use
ENV NGINX_VERSION nginx-1.23.2
ENV NGINX_RTMP_MODULE_VERSION 1.2.2

# Install dependencies
RUN apt-get update && \
    apt-get install -y ca-certificates openssl libssl-dev && \
    rm -rf /var/lib/apt/lists/*

# Download and decompress nginx
RUN mkdir -p /tmp/build/nginx && \
    cd /tmp/build/nginx && \
    wget -O ${NGINX_VERSION}.tar.gz https://nginx.org/download/${NGINX_VERSION}.tar.gz && \
    tar -zxf ${NGINX_VERSION}.tar.gz

# Download and decompress RTMP module
RUN mkdir -p /tmp/build/nginx-rtmp-module && \
    cd /tmp/build/nginx-rtmp-module && \
    wget -O nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION}.tar.gz https://github.com/arut/nginx-rtmp-module/archive/v${NGINX_RTMP_MODULE_VERSION}.tar.gz && \
    tar -zxf nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION}.tar.gz && \
    cd nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION}

# Build and install nginx
# The default puts everything under /usr/local/nginx, so it's needed to change
# it explicitly. Not just for order but to have it in the PATH
RUN cd /tmp/build/nginx/${NGINX_VERSION} && \
    ./configure \
        --sbin-path=/usr/local/sbin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --pid-path=/var/run/nginx/nginx.pid \
        --lock-path=/var/lock/nginx/nginx.lock \
        --http-log-path=/var/log/nginx/access.log \
        --http-client-body-temp-path=/tmp/nginx-client-body \
        --with-http_ssl_module \
        --with-threads \
        --with-ipv6 \
        --with-stream \
        --with-stream_ssl_module \
        --add-module=/tmp/build/nginx-rtmp-module/nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION} --with-debug && \
    make -j $(getconf _NPROCESSORS_ONLN) && \
    make install && \
    mkdir /var/lock/nginx && \
    rm -rf /tmp/build

# Forward logs to Docker
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log

# Set up config file
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 1935
CMD ["nginx", "-g", "daemon off;"]

获得证书

这次我们从Let’s Encrypt获取了证书。
虽然不详细说明步骤,但为了备忘,我会附上获取证书时使用的docker-compose.yml。

version: "3"
services:
  certbot:
    image: certbot/certbot:latest
    volumes:
      - type: bind
        source: ./data/certbot/etc/letsencrypt
        target: /etc/letsencrypt
      - type: bind
        source: ./data/certbot/var/lib/letsencrypt
        target: /var/lib/letsencrypt
    command: ["certonly", "--manual", "--agree-tos", "--no-eff-email", "--preferred-challenges", "dns"]

执行命令如下。

docker compose run -f docker-compose-certbot.yml --rm certbot

本次我们设置了 –preferred-challenges 为 dns,以获得WildCard证书。
此外,由于在本地进行了测试,所以名称指向将是内部IP地址。
以下是要在公共DNS上注册的记录示例。注意,example.com部分是虚构的,需要根据获得的域名进行更改。

名前タイプ値*.local.live.example.comA127.0.0.1_acme-challenge.local.live.example.comTXTcertbotから指示される文字列

建構

在此,我将解释一下构建步骤。由于我们没有在GitHub或其他地方公开文件,因此本文中写的内容将涵盖可展示的全部内容。

使用的背景/环境

    • Windows 10

 

    • Docker Desktop for Windows

 

    • WSL2

 

    OBS 29.1.2

文件组织

在运行Docker的终端内创建以下类似的文件结构。

- data/
  - nginx/
    - hls/  # HLSファイル保存場所
- deployments/
  - nginx/
    - certs/
      - certificate.crt  # Let's Encrypt 証明書
      - private.key  # Let's Encrypt 秘密鍵
  - etc/
    - nginx/
      - nginx.conf
  - www/
    - index.html  # ビデオプレイヤー
- images/
  - nginx/
    - Dockerfile
    - nginx.conf  # イメージビルド用nginx設定
- docker-compose-certbot.yml
- docker-compose.yml

docker-compose.yml 的释义是 “Docker 组合文件”。

这次考试使用的 docker-compose.yml 文件在这里。

version: "3"
services:
  nginx:
    build:
      context: ./images/nginx
    volumes:
      - type: bind
        source: ./deployments/nginx/certs
        target: /certs
      - type: bind
        source: ./deployments/nginx/etc/nginx/nginx.conf
        target: /etc/nginx/nginx.conf
      - type: bind
        source: ./deployments/nginx/www
        target: /www
      - type: bind
        source: ./data/nginx/hls
        target: /hls
    ports:
      - "80:80"
      - "443:443"
      - "1935:1935"

正在公開端口80和1935,但由於僅用於測試,所以在正式部署時是不必要的。

nginx的配置文件

这次使用的nginx配置文件在这里。

worker_processes  auto;

error_log  /var/log/nginx/error.log  info;
pid        /var/run/nginx.pid;

rtmp_auto_push  on;

events {
    worker_connections  1024;
}

rtmp {
    server {
        listen  1935;
        listen  [::]:1935  ipv6only=on;

        application live {
            live    on;
            record  off;

            hls           on;
            hls_path      /hls;
            hls_fragment  10s;
        }
    }
}

stream {
    map  $ssl_server_name  $backend {
        www.local.live.example.com   127.0.0.1:80;
        rtmp.local.live.example.com  127.0.0.1:1935;
    }

    server {
        listen  443  ssl;
        listen  [::]:443  ssl  ipv6only=on;

        ssl_prefer_server_ciphers  on;
        ssl_certificate            /certs/certificate.crt;
        ssl_certificate_key        /certs/private.key;
        ssl_protocols              TLSv1.2 TLSv1.3;
        ssl_ciphers                ECDHE:HIGH:!aNULL:!MD5;

        proxy_pass  $backend;
    }
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile    on;
    tcp_nopush  on;

    gzip  off;

    keepalive_timeout  65;

    server {
        listen  80;

        location /hls/ {
            alias /hls/;
        }

        location / {
            alias /www/;
        }
    }
}

正如往常一样,example.com 是虚拟的,您需要根据自己拥有的域名进行更改。

总结这项设置的整体情况如下。

    • stream

443 ポートで待ち受け
SSLを終端
SNIの名前に基づいて 80 か 1935 ポートに振り分ける

rtmp

1935 ポートで待ち受け
映像を受信するとHLSファイルを保存

http

80 ポートで待ち受け
HLSファイルとビデオプレイヤーを公開

HTML视频播放器

以下是播放HLS视频的HTML视频播放器。此次使用了Video.js。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Live</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="https://vjs.zencdn.net/8.3.0/video-js.css" rel="stylesheet" />
    <style>
        body {
            margin: 0;
            padding: 0;
        }
    </style>
  </head>
  <body>
    <div style="height: 100vh">
      <video
        autoplay
        id="player"
        class="video-js"
        style="width: 100%; height: 100%;"
        controls
        muted
        preload="auto"
        width="640"
        height="360"
        data-setup="{}">
        <source src="/hls/hogehoge.m3u8" type="application/vnd.apple.mpegurl" />
      </video>
    </div>
    <script src="https://vjs.zencdn.net/8.3.0/video.min.js"></script>
  </body>
</html>

在这个文件名中写着“hogehoge.m3u8”,其中的“hogehoge”部分对应于输入到OBS或Microsoft Teams的流密钥。

执行 (shí

完成构建后,执行服务器。

$ docker compose up
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: using the "epoll" event method
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: nginx/1.23.2
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6) 
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2   
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576      
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker processes
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker process 7
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker process 8
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker process 9
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker process 10
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker process 11
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start worker process 12
rtmp-server-nginx-1  | 2023/07/27 04:58:21 [notice] 1#1: start cache manager process 13

然后,进行OBS的流媒体设置。

image.png
名前値サービスカスタムサーバーrtmps://rtmp.local.live.example.com/liveストリームキーhogehoge

一旦在OBS上开始直播后,只要能够从https://www.local.live.example.com/显示视频播放器,并且视频能够正常播放,就可以了。

总结

好像没有对nginx + stream + rtmp 组合进行说明的文章呢,所以这次我来总结一下。
使用nginx-rtmp-module,似乎可以轻松地实现“只有在Microsoft 365的认证基础上认证的用户才能进行流媒体的发布和观看”的功能,也许以后我会尝试一下。

广告
将在 10 秒后关闭
bannerAds