基于 Nginx 配置 Web 视频流媒体服务器
Category(分类): Backend Status: 已读
原文:基于 Nginx 配置 Web 视频流媒体服务器 (音视频领域水深)(leefige 的博客,2019-03)。 本文为整理版本,保留原文全部步骤与配置,修正了原文配置块中缺失的花括号。
需求背景
室友提出一个需求:在作品集里放一个二维码,扫码进入网页播放自己的视频,且不能有广告——商业视频网站都不能用。手头刚好有一台 AWS 服务器,于是决定自建一个 Web 视频流媒体服务器。
Web 视频流媒体服务器主要应满足:
- 提供 HTTP 服务;
- 具备传输视频流的能力;
- 可以在浏览器中播放视频。
一般来说,会把视频传输和视频播放放在两台服务器上,因为视频的解码、传输本身就要消耗大量资源。但资源有限,先用一台服务器将就。
技术选型
查阅资料发现,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 访问非常流畅——所以是校园网的问题。室友很满意,承诺请吃饭。
参考资料
原文引用的核心资料:
- nginx搭建flv、mp4流媒体服务(安装支持 mp4 的 Nginx)
- 利用nginx搭建http和rtmp协议的流媒体服务器
- Nginx搭建flv视频点播服务器
- Linux nginx+rtmp服务器配置实现直播点播(最成熟的方案,作者最终未使用 rtmp)
- 理解RTMP、HttpFlv和HLS的正确姿势
- 开源网页播放器JWplayer使用
整理者注
至此 mp4 点播场景下,本分类四篇文章形成完整闭环:
- 怎么配:《Nginx 伺服 mp4 视频流的配置参考》——mp4 模块 + aio threads + slice 分片缓存;
- 为什么慢:《Nginx 伺服大体积 mp4 文件效率问题排查》——I/O 阻塞特征与线程池;
- 为什么 Safari 播不了:《Nginx 伺服 mp4 给 Safari 的正确方式》——Range/206 语义;
- 从零搭建(本篇)——编译、配置、播放器、二维码的全流程实践。
原文标题里的"音视频领域水深"一点不假:从协议(RTMP/HTTP-FLV/HLS)到封装(moov atom/faststart)到伺服(Range/slice/线程池),每一层都是独立的坑。