技术知识文章集合TECHNICAL ARCHIVE · 807 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTBE

基于 Nginx 配置 Web 视频流媒体服务器

所属分类
Backend
文件格式
Markdown
原始路径
Backend/基于 Nginx 配置 Web 视频流媒体服务器
本文目录9 个章节
  1. 需求背景
  2. 技术选型
  3. 安装 Nginx
  4. 配置 Nginx
  5. 播放页面(JW Player)
  6. 生成二维码
  7. 结果
  8. 参考资料
  9. 整理者注

基于 Nginx 配置 Web 视频流媒体服务器

Category(分类): Backend Status: 已读

原文:基于 Nginx 配置 Web 视频流媒体服务器 (音视频领域水深)(leefige 的博客,2019-03)。 本文为整理版本,保留原文全部步骤与配置,修正了原文配置块中缺失的花括号。

需求背景

室友提出一个需求:在作品集里放一个二维码,扫码进入网页播放自己的视频,且不能有广告——商业视频网站都不能用。手头刚好有一台 AWS 服务器,于是决定自建一个 Web 视频流媒体服务器。

Web 视频流媒体服务器主要应满足:

  1. 提供 HTTP 服务;
  2. 具备传输视频流的能力;
  3. 可以在浏览器中播放视频。

一般来说,会把视频传输和视频播放放在两台服务器上,因为视频的解码、传输本身就要消耗大量资源。但资源有限,先用一台服务器将就。

技术选型

查阅资料发现,Nginx 本身就提供了视频流传输和播放能力,对于 mp4 格式,借用现成插件即可:

  • Nginx:nginx-1.15.9
  • mp4 流式支持:nginx_mod_h264_streaming-2.2.7
  • 播放器:JW Player(尝试 nginx 自带播放器后发现加载速度极慢难以忍受,故改用专用播放器)

安装 Nginx

需要手动编译,以包含 mp4 等附加功能:

$ sudo apt update
$ sudo apt install libpcre3 libpcre3-dev
$ sudo apt install libssl-dev
$ sudo apt install zlib1g-dev

$ wget http://nginx.org/download/nginx-1.15.9.tar.gz
$ wget http://h264.code-shop.com/download/ngi7.tar.gz
$ git clone https://github.com/arut/nginx-rtmp-module

$ tar -zxvf nginx-1.15.9.tar.gz
$ tar -zxvf nginx_mod_h264_streaming-2.2.7.tar.gz

$ cd nginx-1.15.9/
$ ./configure --prefix=/usr/local/nginx \
  --add-module=../nginx_mod_h264_streaming-2.2.7 \
  --add-module=../nginx-rtmp-module \
  --with-http_flv_module --with-http_mp4_module \
  --with-http_gzip_static_module --with-http_stub_status_module

直接 make 会报错——h264 streaming 模块的代码引用了 r->zero_in_uri,新版 nginx 结构体里没有这个成员:

../nginx_mod_h264_streaming-2.2.7/src/ngx_http_streaming_module.c:158:
  ‘ngx_http_request_t {aka struct ngx_http_request_s}’ has no member named ‘zero_in_uri’

解决办法:编辑 objs/Makefile,去掉编译选项里的 -Werror(把错误降级为警告即可编过):

$ sudo make install
$ sudo ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx
$ sudo nginx

浏览器访问服务器,看到默认欢迎页说明安装成功。

整理者注:现代 nginx(≥1.5)已内置 ngx_http_mp4_module(编译参数 --with-http_mp4_module 即本文所用),第三方 h264 streaming 模块如今基本不再需要;zero_in_uri 报错正是该模块年久失修的标志。

配置 Nginx

/usr/local/nginx/conf/nginx.conf:

#user  nobody;
worker_processes  4;

error_log  logs/error.log;
pid        logs/nginx.pid;

events {
    use epoll;
    worker_connections  65535;
}

rtmp {
    server {
        listen 1935;
        application vod {
            play /home/username/web_service/rtmp;
        }
    }
}

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

    sendfile           on;
    keepalive_timeout  65;
    server_names_hash_bucket_size 128;
    client_header_buffer_size   32k;
    large_client_header_buffers 4 32k;

    gzip             on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_comp_level  2;
    gzip_types       text/plain application/x-javascript text/css application/xml;
    gzip_vary        on;

    output_buffers     1 32k;
    postpone_output    1460;
    client_header_timeout 3m;
    client_body_timeout   3m;
    send_timeout          3m;
    tcp_nopush            on;
    tcp_nodelay           on;

    open_file_cache          max=64 inactive=30d;
    open_file_cache_min_uses 1;
    open_file_cache_valid    3m;

    server {
        listen      8081;
        server_name x.x.x.x;
        charset     utf-8;

        location ~ \.mp4$ {
            root /home/username/web_service/mp4/;
        }

        location ~ \.m4v$ {
            root /home/username/web_service/m4v/;
        }

        location ~ \.flv$ {
            root /home/username/web_service/flv/;
        }

        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }

        location /stat.xsl {
            root /home/username/downloads/nginx-rtmp-module;
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }

    server {
        listen      80;
        server_name x.x.x.x;
        charset     utf-8;

        location / {
            root  /home/username/web_service/html/;
            index index.html index.htm;
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
}

主要思路:8081 端口提供视频文件的 HTTP 服务,80 端口提供视频播放页面。

本来试图通过 rtmp 推流,但 ffmpeg 可以正常读取原视频、写流也不报错,却始终无法正常推流,最终作罢(rtmp 配置块保留未用)。

重启 nginx 生效:

$ sudo nginx -s reload

此时访问 http://x.x.x.x:8081/xxx.mp4 即可播放 mp4 视频,但用的是 nginx 自带的播放方式,卡顿非常明显,体验不好,于是引入专用播放器。

播放页面(JW Player)

JW Player 官网注册后可获得专用代码,复制到 html 中。编辑 /home/username/web_service/html/index.html:

<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript" src="https://cdn.jwplayer.com/libraries/xxxx.js"></script>
        <script type="text/javascript" src="getParam.js"></script>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    </head>

    <body bgcolor="#000000">
        <div id="myElement">Loading the page...</div>
        <script type="text/javascript">
            var file_name = getParam('id');
            console.log(file_name);
            jwplayer("myElement").setup({
                file: "http://x.x.x.x:8081/" + file_name,
                title: file_name,
            });
        </script>
    </body>
</html>

为了方便指定播放文件,写了一个 js 函数获取 GET 参数 getParam.js:

function getParam(paramName) {
    paramValue = "", isFound = !1;
    if (this.location.search.indexOf("?") == 0 && this.location.search.indexOf("=") > 1) {
        arrSource = unescape(this.location.search).substring(1, this.location.search.length).split("&"), i = 0;
        while (i < arrSource.length && !isFound)
            arrSource[i].indexOf("=") > 0
                && arrSource[i].split("=")[0].toLowerCase() == paramName.toLowerCase()
                && (paramValue = arrSource[i].split("=")[1], isFound = !0), i++;
    }
    return paramValue == "" && (paramValue = null), paramValue;
}

于是通过 http://x.x.x.x/?id=xxx.mp4 就可以直接用 JW Player 播放目标视频。

生成二维码

最简单的一步:直接使用 Chrome 插件 "Quick QR Code Generator!",打开目标页面即可生成二维码。

结果

一开始在校园网测试还是有卡顿,但室友用 4G 访问非常流畅——所以是校园网的问题。室友很满意,承诺请吃饭。

参考资料

原文引用的核心资料:

整理者注

至此 mp4 点播场景下,本分类四篇文章形成完整闭环:

  1. 怎么配:《Nginx 伺服 mp4 视频流的配置参考》——mp4 模块 + aio threads + slice 分片缓存;
  2. 为什么慢:《Nginx 伺服大体积 mp4 文件效率问题排查》——I/O 阻塞特征与线程池;
  3. 为什么 Safari 播不了:《Nginx 伺服 mp4 给 Safari 的正确方式》——Range/206 语义;
  4. 从零搭建(本篇)——编译、配置、播放器、二维码的全流程实践。

原文标题里的"音视频领域水深"一点不假:从协议(RTMP/HTTP-FLV/HLS)到封装(moov atom/faststart)到伺服(Range/slice/线程池),每一层都是独立的坑。

807 DOCUMENTS · 12 COLLECTIONS
ARCHIVE SEARCH807 篇文章