使用nginx搭建rtmp+http_flv服务器实现直播推流
1.概述
项目中要实现安卓手机上的直播功能,因为涉及网络安全,不允许使用第三方的直播推流服务器,所以需要自建一台直播推流的视频服务器,将搭建过程记录如下。
2.服务器配置
现场服务器均为Windows Server版本,但nginx安装第三方模块需要在Linux系统下进行,在Windows下的安装非常麻烦,而且稳定性不好,所以考虑在Windows服务器上安装CentOS的虚拟机,在虚拟机上安装nginx,然后使用端口映射到主机端口上实现相关功能
2.1.安装vmware虚拟机软件
在服务器上安装了VMware Workstation 12 Pro
2.2.安装CentOS7操作系统
使用CentOS-7-x86_64-DVD-1908.iso安装操作系统,安装时虚拟机网络使用NAT地址转换
2.3.设置虚拟机为共享虚拟机
虚拟机必须设置为共享虚拟机才可以开机自启动,在左侧虚拟机列表中,右键虚拟机,然后选择“管理-共享”菜单,按提示一步一步设置为共享虚拟机,设置后即可开机自启动。
2.4.配置端口映射
在菜单:“编辑-虚拟网络编辑器”中打开虚拟网络编辑器,选择类型为“NAT模式”的行,在下面找到【NAT设置】按钮,点击后打开NAT设置窗口,点击添加即可添加端口映射
3.安装Nginx服务器软件
3.1.准备工作
打开命令行,安装编译相关软件
yum -y install unzipyum -y install gcc-c++ yum -y install pcre pcre-devel yum -y install zlib zlib-devel yum -y install openssl openssl-devel
3.2.下载Nginx源文件
从github下载nginx的源代码
/nginx/nginx
注:打不开网站的话,安装【Steam++】软件进行网络加速
下载后,拷贝nginx源文件到/home/nginx下并解压,
解压后目录为/home/nginx/nginx-1.23.1
3.3.下载nginx-http-flv-module模块
从github下载nginx-http-flv-module的源代码
/winshining/nginx-http-flv-module
拷贝nginx-http-flv-module源文件并解压到/usr/local/nginx下,nginx目录不存在时则创建之
解压后目录为/usr/local/nginx/nginx-http-flv-module
注:nginx-http-flv-module模块是基于 nginx-rtmp-module 的流媒体服务器,所以在编译时无需再引入nginx-rtmp-module,而且nginx-rtmp-module的各项配置也是兼容的。
3.4.编译安装Nginx软件
进入/home/nginx/nginx-1.23.1目录下执行以下命令
sudo ./configure --prefix=/usr/local/nginx --with-http_ssl_module --add-module=/usr/local/nginx/nginx-http-flv-module
sudo make
sudo make install
此时nginx已安装成功,可以执行/usr/local/nginx/sbin/nginx -V查看版本号,会输出以下内容:
nginx version: nginx/1.23.1built by gcc 4.8.5 0623 (Red Hat 4.8.5-44) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan TLS SNI support enabledconfigure arguments: --add-module=../nginx-http-flv-module
如果安装之后需要增加模块,可以使用configure命令添加配置,然后make,之后不要make install ,将编译的二进制文件手动拷贝到sbin目录下即可
cp ./objs/nginx /usr/local/nginx/sbin/
3.5.修改nginx配置文件
安装后的nginx配置文件中没有rtmp的相关配置,需要自行配置相关内容,以下是完整的配置文件内容
#user nobody;worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024;}rtmp {server {listen 1985;application live {live on;gop_cache on;publish_notify on;on_publish http://172.16.70.234:8080/swms/oa/index_notifyLiveStart.action;#on_update http://172.16.70.234:8080/swms/oa/index_notifyLiveStart.action;on_publish_done http://172.16.70.234:8080/swms/oa/index_notifyLiveStop.action;}}}http {include 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 logs/access.log main;sendfile on;#tcp_nopushon;#keepalive_timeout 0;keepalive_timeout 65;#gzip on;server {listen 8080;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}location /live {flv_live on;}location /flv {add_header 'Access-Control-Allow-Origin' '*';flv_live on;chunked_transfer_encoding on;}location /stat {rtmp_stat all;rtmp_stat_stylesheet stat.xsl;}location /stat.xsl {root nginx-http-flv-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;}}}
其中rtmp段和http->server->location中的/live, /flv, /stat, /stat.xsl为新增段落
3.6.启动nginx
现在就可以启动nginx了,使用以下命令控制nginx
#启动sudo /usr/local/nginx/sbin/nginx#停止sudo /usr/local/nginx/sbin/nginx -s stop#重新加载配置文件sudo /usr/local/nginx/sbin/nginx -s reload
3.7.安装nginx为服务
创建服务描述文件
vi /usr/lib/systemd/system/nginx.service
内容如下:
[Unit]Description=nginx - web serverAfter=network.target remote-fs.target nss-lookup.target[Service]Type=forkingPIDFile=/usr/local/nginx/logs/nginx.pidExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.confExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.confExecReload=/usr/local/nginx/sbin/nginx -s reloadExecStop=/usr/local/nginx/sbin/nginx -s stopExecQuit=/usr/local/nginx/sbin/nginx -s quitPrivateTmp=true[Install]WantedBy=multi-user.target
然后重新加载服务配置
systemctl daemon-reload
然后就可以使用服务命令控制nginx了
#停止Nginx服务sbin/nginx -s stop#开启Nginx服务systemctl start nginx#查看Nginx服务状态,出现【active (running)】表示服务正在运行systemctl status nginx#设置开机启动systemctl enable nginx.service
4.推流测试
使用ffmpeg进行测试
#视频文件推流:ffmpeg.exe -re -i c:\ffmpeg\inputfile.mp4 -vcodec libx264 -acodec aac -f flv rtmp://127.0.0.1:1985/live/mp4#桌面推流ffmpeg -f gdigrab -i desktop -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -f flv rtmp://127.0.0.1:1985/live/desk1#拉流测试ffplay.exe rtmp://localhost:1985/live/mp4
5.网页播放
因为rtmp协议在网页上播放时需要使用flash插件,但现在各大浏览器已经不再支持flash插件了,所以需要使用http_flv协议实现网页播放的功能,这也是选择了nginx-http-flv-module模块的原因,在网页上播放http_flv需要使用bilibili开源的flv.js组件
/bilibili/flv.js
可以下载源码进行编译,也可以直接下载编译好的Releases 版本的flv.js文件
http://bilibili.github.io/flv.js/dist/flv.js/bilibili/flv.js/releases/download/v1.6.2/flv.js
使用方法如下:
<script src="flv.js"></script><video id="videoElement" width="360" height="480"></video><script>var url = 'http://localhost:8080/flv?port=1985&app=live&stream=mp4';if (url && flvjs.isSupported()) {var videoElement = document.getElementById('videoElement');window.flvPlayer = flvjs.createPlayer({type: 'flv',url: url});flvPlayer.attachMediaElement(videoElement);flvPlayer.load();flvPlayer.play();}</script>
可以对FlvPlayer进行封装,监听相关事件,实现网络中断再次接通后重新加载播放功能
var liveOpt = {liveStat:0,//直播是否开始,0未开始 1已开始livePlayUrl:'http://....',//直播观看地址flvPlayer:null,startLive : function(){if (this.liveStat==1 && flvjs.isSupported()) {var videoElement = document.getElementById('videoElement');var flvPlayer = this.flvPlayer = flvjs.createPlayer({type: 'flv',isLive:true,url: this.livePlayUrl}, {isLive:true});flvPlayer.attachMediaElement(videoElement);flvPlayer.on(flvjs.Events.ERROR, ()=>{console.log('ERROR', arguments);this.restartHandle = clearTimeout(this.restartHandle);this.restartHandle = setTimeout(this.restartLive.bind(this), 3000);});this.metaArrived = false;flvPlayer.on(flvjs.Events.METADATA_ARRIVED, ()=>{console.log('METADATA_ARRIVED', arguments)if(this.metaArrived){//如果第二次发来Meta信息,说明是断线后重连了,此时重新初始化控件this.restartLive();}this.metaArrived = true;});flvPlayer.load();flvPlayer.play();}else{setTimeout(startLive, 600);}},stopLive : function (){if(this.flvPlayer){try{this.flvPlayer.destroy();}catch(e){}this.flvPlayer = null;}},restartLive:function(){console.log('try to restart live!')this.stopHandle && clearTimeout(this.stopHandle);this.startHandle && clearTimeout(this.startHandle);this.stopHandle = setTimeout(this.stopLive.bind(this), 1000);this.startHandle = setTimeout(this.startLive.bind(this), 2000);}};