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

本来在一心折腾基于Hexo的自建博客,恰好室友提出这样一个需求:在自己的作品集里放一个二维码,扫一下就可以转到一个网页播放自己的视频,但是又要求不能有广告,因此商业性质的视频网站都不能使用。手头刚好有一个AWS的服务器,于是决心尝试一下这类还没碰过的技术。

Web视频流媒体服务器主要应该满足以下几个功能:

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

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

技术选型

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

  • Nginx: nginx-1.15.9
  • mp4: nginx_mod_h264_streaming-2.2.7

另外,在尝试使用Nginx自带播放器播放,发现加载速度极慢难以忍受后,选择使用JW Player

安装nginx

需要手动编译,以包含一些需要的附加功能(如mp4等)

nginx_mod_h264_streaming的下载地址: http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-Nginx-Version2

nginx-rtmp-module下载地址: https://github.com/arut/nginx-rtmp-module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

$ sudo apt update
$ sudo apt install libpcre3 libpcre3-dev # nginx需要
$ sudo apt install libssl-dev
$ sudo apt-get install zlib1g-dev

# 下载 nginx
$ wget http://nginx.org/download/nginx-1.15.9.tar.gz

# 下载 h264 mod
$ wget http://h264.code-shop.com/download/ngi7.tar.gz

# 下载 rtmp
$ 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会报错:

1
2
3
4
5
6
7
8
9
10
11
12
13

In file included from ../nginx_mod_h264_streaming-2.2.7/src/ngx_http_h:
../nginx_mod_h264_streaming-2.2.7/src/ngx_http_streaming_module.c: In ndler’:
../nginx_mod_h264_streaming-2.2.7/src/ngx_http_streaming_module.c:158:st_t {aka struct ngx_http_request_s}’ has no member named ‘zero_in_uriri’?
if (r->zero_in_uri)
^~~~~~~~~~~
plus_in_uri
objs/Makefile:1262: recipe for target 'objs/addon/src/ngx_http_h264_st
make[1]: *** [objs/addon/src/ngx_http_h264_streaming_module.o] Error 1
make[1]: Leaving directory '/home/username/downloads/nginx-1.15.9'
Makefile:11: recipe for target 'install' failed
make: *** [install] Error 2

编辑objs/Makefile,去掉编译选项的-Werror

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

此时通过浏览器访问服务器,若看到默认欢迎页面,说明nginx安装成功。

配置nginx

在若干次尝试中借鉴了多篇文章,最终的配置如下:

/usr/local/nginx/conf/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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#user  nobody;
worker_processes 4;

error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

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;
#tcp_nopush on;

#keepalive_timeout 0;
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 404 /404.html;

# redirect server error pages to the static page /50x.html
#
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;
}

# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

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

本来试图通过rtmp推流,但不知道为什么,ffmpeg可以正常读取原视频,写流时也未报错,但无法正常推流,最终作罢。

通过这样的设置,重启nginx:

1
$ sudo nginx -s reload

访问http://x.x.x.x:8081/xxx.mp4即可播放mp4格式的视频。但由于使用了nginx自带的播放器,所以卡顿非常明显,体验不好,考虑使用专用播放器。

播放

播放器选择了JW Player,似乎以前是开源的,但现在变为免费试用六个月。只需要在官网注册,即可获得专用代码,复制到html中即可。

编辑/home/username/web_service/html/index.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
<!DOCTYPE heml>
<html>
<head>
<!-- 在这里替换自己的JW Player id -->
<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,
// image: "data/myposter.jpg",
title: file_name,
});
</script>
</body>
</html>

方便起见,编写了一个js函数用来获取GET请求参数,从而指定播放文件

getParam.js
1
2
3
4
5
6
7
8
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表示非常流畅……所以还是校园网太垃圾了。室友很满意,承诺要请我吃饭😀

References

核心部分

利用该文安装了支持mp4的Nginx:

nginx搭建flv、mp4流媒体服务

利用这两篇文章完成了Nginx配置和JW Player的使用:

利用nginx搭建http和rtmp协议的流媒体服务器 Nginx搭建flv视频点播服务器 (以前文为基础)

Nginx

Ubuntu18.04安装Nginx Nginx的启动、停止与重启 Nginx Open File Cache

视频流

Linux nginx+rtmp服务器配置实现直播点播 (应该是最成熟的方案,但我最终没有使用rtmp) 理解RTMP、HttpFlv和HLS的正确姿势

页面实现

PHP如何与js交互数据 (但没有用到) 开源网页播放器JWplayer使用 js获取url传递参数,js获取url?号后面的参数 JS-获取URL请求参数