1. 前言
前面几章我们已经把基础环境都已经搭建完成,这一章我们通过使用 nginx-http-flv-module 搭建一个可以通过HTTP请求并且通过flv.js实现在HTML网页播放实时视频的应用场景。
2. 安装nginx-http-flv-module模块
2.1 简述
nginx-http-flv-module是基于nginx-rtmp-module 的流媒体服务器。它具备了所有nginx-rtmp-module的功能,并且新增多种新功能,功能对比如下。
功能 | nginx-http-flv-module | nginx-rtmp-module | 备注 |
HTTP-FLV (播放) | √ | x | 支持 HTTPS-FLV 和 chunked 回复 |
GOP 缓存 | √ | x | |
虚拟主机 | √ | x | |
省略 listen 配置 | √ | 见备注 | 配置中必须有一个 listen |
纯音频支持 | √ | 见备注 | wait_video 或 wait_key 开启后无法工作 |
reuseport 支持 | √ | x | |
定时打印访问记录 | √ | x | |
JSON 风格的 stat | √ | x | |
stat 中包含录制详情 | √ | x |
2.2 兼容性
NGINX 的版本应该大于或者等于 1.2.6,与其他版本的兼容性未知。
2.3 支持的系统
- Linux(推荐)/ FreeBSD / MacOS / Windows(受限)。
2.4 支持的播放器
- VLC (RTMP & HTTP-FLV) / OBS (RTMP & HTTP-FLV) / JW Player (RTMP) / flv.js (HTTP-FLV)。
2.5 其他
其余详见 nginx-http-flv-module 下的 README.CN.md。
2.6 下载
下载地址:https://github.com/winshining/nginx-http-flv-module 选择一个版本然后点击Code --> Download ZIP,这里最新版本为 v1.2.10,我选择的是 v1.2.8 版本。
然后通过xftp远程工具将文件上传至服务器。
2.7 编译安装
将已上传的nginx-http-flv-module解压。
[root@localhost download]# unzip nginx-http-flv-module-1.2.8.zip
对文件夹进行重命名(看个人习惯,不重命名也一样可以)。
[root@localhost download]# mv nginx-http-flv-module-1.2.8 nginx-http-flv-module
# 移动文件夹到 /usr/local/下
[root@localhost download]# mv ./nginx-http-flv-module /usr/local/
下面就可以重新编译nginx了,并将nginx-http-flv-module模块编译到nginx中。
进入最开始解压出来的nginx目录下。
[root@localhost download]# cd /opt/download/nginx-1.18.0/
# 执行编译命令
[root@localhost nginx-1.18.0]# ./configure --add-module=/usr/local/nginx-http-flv-module
[root@localhost nginx-1.18.0]# make && make install
接下来查看nginx是否安装成功nginx-http-flv-module模块,执行命令。
[root@localhost nginx-1.18.0]# nginx -V
这里就说明我们安装成功了。
3. 配置nginx配置文件文件
修改nginx配置文件。
[root@localhost nginx-1.18.0]# cd /usr/local/nginx/conf/
[root@localhost conf]# vim ./live.conf
配置文件如下:
#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 {
out_queue 4096;
out_cork 8;
max_streams 128;
timeout 15s;
drop_idle_publisher 15s;
log_interval 5s; #log 模块在 access.log 中记录日志的间隔时间,对调试非常有用
log_size 1m; #log 模块用来记录日志的缓冲区大小
server {
listen 7933;
application myapp {
live on;
}
application hls {
live on;
hls on;
hls_path /tmp/hls;
}
application dash {
live on;
dash on;
dash_path /tmp/dash;
}
}
}
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_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#access_log logs/host.access.log main;
location / {
add_header 'Access-Control-Allow-Origin' '*';
root html;
index index.html index.htm;
}
location /live {
flv_live on;
chunked_transfer_encoding on; #支持'Transfer-Encoding: chunked'方式回复
add_header 'Access-Control-Allow-Origin' '*'; #添加额外的 HTTP 头
add_header 'Access-Control-Allow-Credentials' 'true'; #添加额外的 HTTP 头
}
location /flv {
add_header 'Access-Control-Allow-Origin' '*';
flv_live on;
chunked_transfer_encoding on;
}
# control控制器
location /control {
rtmp_control all;
}
location /stat {
#推流播放和录制统计数据的配置
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /usr/local/nginx-http-flv-module/; #指定 stat.xsl 的位置
}
#error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
主要配置为: rtmp标签下的server配置其监听7933端口,并配置location myapp设置属性live on 表示开启直播。http标签下的server配置其监听80端口,并配置location /live设置属性 flv_live on。注意其中的 add_header ‘Access-Control-Allow-Origin’ ‘*’ 与 add_header ‘Access-Control-Allow-Credentials’ ‘true’ 很重要,主要解决了前端通过HTTP方式拉流是的跨域问题。
目前nginx-http-flv-module模块配置完成,并且nginx配置文件也已经配置完成 下面进行推流与拉流测试。
执行推流命令:
ffmpeg -rtsp_transport tcp -i rtsp://admin:Admin12345@192.168.1.65:554/h264/ch0/main/av_stream -c:v libx264 -c:a aac -f flv -an rtmp://192.168.5.177:7933/myapp/room
推流成功。这里我说明一下 我使用的是海康威视的网络摄像头 rtsp://admin:Admin12345@192.168.1.65:554/h264/ch0/main/av_stream 这个地址是我的摄像头播放地址,请查看并使用你使用的摄像头播放地址。
下面通过VLC播放器进行拉流播放。
播放URL为 http://192.168.5.177/live?port=7933&app=myapp&stream=room 其中192.168.5.177换成你的服务器ip ,live是nginx配置文件中配置的请求地址,可以查看nginx配置文件中的配置,port为nginx配置文件中rtmp标签下server监听的端口号(注意开放服务器防火墙),app=myapp这个myapp与rtmp标签下 server下的location myapp所对应,stream=room这个room与你推流地址所对应。
点击播放。
播放成功。
4. 通过VUE播放实时视频流
首先创建一个新的vue项目,并通过编辑器打开项目。
E:\vueCode>vue create live
# 执行命令运行项目
E:\vueCode\live> npm run serve
由于我们只需要使用一个页面那么我就直接在app.vue页面直接写了。
下面先安装flv.js,执行命令。
E:\vueCode\live> npm install --save flv.js
安装完成,下面进行编码。
<template>
<div id="app">
<video
id="videoLive"
crossorigin="anonymous"
controls
autoplay
width="100%"
height="100%"
style="object-fit: fill"
></video>
</div>
</template>
<script>
import flvjs from "flv.js";
export default {
name: "App",
data() {
return {
flvPlayer: null,
};
},
mounted() {
this.createVideo('http://192.168.5.177/live?port=7933&app=myapp&stream=room',"videoLive")
},
methods: {
createVideo(url, elementId) {
if (flvjs.isSupported()) {
let videoElement = document.getElementById(elementId);
this.flvPlayer = flvjs.createPlayer({
type: "flv",
enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js
isLive: true, //直播模式
hasAudio: false, //关闭音频
hasVideo: true,
stashInitialSize: 128,
enableStashBuffer: true, //播放flv时,设置是否启用播放缓存,只在直播起作用。
url: url,
});
this.flvPlayer.attachMediaElement(videoElement);
this.flvPlayer.load();
this.flvPlayer.play();
}
},
},
};
</script>
<style>
#app {
width: 640px;
height: 480px;
border: 1px red solid;
margin: 0 auto;
}
</style>
保存并查看页面。
此时页面就能够正常的播放摄像头的实时视频流了,不过这里两个问题,第一个问题是视频延时大概在6~7秒左右,这是延时是我们无法接受的,第二个问题是当我们在浏览器切换了一个标签后,我们发现视频默认暂停了,当我们再次回到页面的时候就需要手动调整进度条,这个是不行的不够友好。下面先来解决第一个延时问题。
这个延时问题主要是因为ffmpeg在对视频进行转码时的延迟,这时我们来优化ffmpeg转码参数,命令如下。
ffmpeg -rtsp_transport tcp -i rtsp://admin:Admin12345@192.168.1.65:554/h264/ch0/main/av_stream -c:v libx264 -c:a aac -threads 5 -preset ultrafast -max_delay 100000 -f flv -an rtmp://192.168.5.177:7933/myapp/room
对比第一条参数我们多加了 -threads 5 -preset ultrafast -max_delay 100000 参数,-threads 5的意思是 设置编解码等工作的线程数,线程数多了速度自然比一个线程处理的快 -preset ultrafast 用来指定视频的输出质量,它共有以下几个可以用的值:
- ultrafast 超快的
- superfast 超高速的
- veryfast 非常快
- faster 更快
- fast 快
- medium 中等
- slow 缓慢的
- slower 较慢的
- veryslow 非常慢
我们这里使用的是 ultrafast 超快的,-max_delay 100000 表示指定视频的最大延迟,这里设置100ms。调优参数就设置到这里,下面设置flv.js的参数。
createVideo(url, elementId) {
if (flvjs.isSupported()) {
let videoElement = document.getElementById(elementId);
this.flvPlayer = flvjs.createPlayer({
type: "flv",
enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js
isLive: true, //直播模式
hasAudio: false, //关闭音频
hasVideo: true,
stashInitialSize: 128,
enableStashBuffer: true, //播放flv时,设置是否启用播放缓存,只在直播起作用。
url: url,
}, {
enableStashBuffer: false, //禁用IO存储缓存区
});
this.flvPlayer.attachMediaElement(videoElement);
this.flvPlayer.load();
this.flvPlayer.play();
}
}
主要参数为:
{
enableStashBuffer: false, //禁用IO存储缓存区
}
下面重新执行命令,并刷新页面,查看延迟效果。
可以发现目前延迟大概在500ms左右,如果觉得还有有点慢的话可以调整转码的分辨率和视频大小等参数,可以自行百度查找。
下面解决第二个问题,切换标签页后页面自动暂停问题。
mounted() {
this.createVideo('http://192.168.5.177/live?port=7933&app=myapp&stream=room',"videoLive")
this.monitorVideo()
},
methods: {
createVideo(url, elementId) {
if (flvjs.isSupported()) {
let videoElement = document.getElementById(elementId);
this.flvPlayer = flvjs.createPlayer({
type: "flv",
enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js
isLive: true, //直播模式
hasAudio: false, //关闭音频
hasVideo: true,
stashInitialSize: 128,
enableStashBuffer: true, //播放flv时,设置是否启用播放缓存,只在直播起作用。
url: url,
}, {
enableStashBuffer: false, //禁用IO存储缓存区
});
this.flvPlayer.attachMediaElement(videoElement);
this.flvPlayer.load();
this.flvPlayer.play();
}
},
monitorVideo(){
let videoElement = document.getElementById("videoLive");
videoElement.onpause =function(){
//监听video标签是否暂停,如果暂停就让他继续播放
if (videoElement.paused){
videoElement.play()
}
}
}
},
主要参数如下:
其思想就是监听video标签是否暂停,如果暂停了就让他继续播放,这就相当于做了一个后台播放效果出来了。
5. 掉线重连
这时如果视频服务器突然掉线了怎么办,解决方法如下。
methods: {
createVideo(url, elementId) {
if (flvjs.isSupported()) {
let videoElement = document.getElementById(elementId);
this.flvPlayer = flvjs.createPlayer(
{
type: "flv",
enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js
isLive: true, //直播模式
hasAudio: false, //关闭音频
hasVideo: true,
stashInitialSize: 128,
enableStashBuffer: true, //播放flv时,设置是否启用播放缓存,只在直播起作用。
url: url,
},
{
enableStashBuffer: false, //禁用IO存储缓存区
}
);
this.flvPlayer.attachMediaElement(videoElement);
if (url != null && url !== "") {
this.flvPlayer.load();
this.flvPlayer.play();
}
let _then = this;
// 重连 监听flvPlayer异常
this.flvPlayer.on(flvjs.Events.ERROR, function (errType, errDetail) {
console.error("连接异常,正在尝试重新连接", errType, errDetail);
//如果flvPlayer不为空
if (_then.flvPlayer) {
_then.reloadVideo(_then.flvPlayer, elementId, url);
}
});
}
},
reloadVideo(flvPlayer, elementId, url) {
this.destoryVideo(flvPlayer);
// 重新执行createVideo方法
this.createVideo(url, elementId);
},
//销毁flvPlayer
destoryVideo(flvPlayer) {
flvPlayer.pause();
flvPlayer.unload();
flvPlayer.detachMediaElement();
flvPlayer.destroy();
flvPlayer = null;
},
monitorVideo() {
let videoElement = document.getElementById("videoLive");
videoElement.onpause = function () {
//监听video标签是否暂停,如果暂停就让他继续播放
if (videoElement.paused) {
videoElement.play();
}
};
},
}
主要代码如下:
其思想就是通过flvPlayer的on方法监听flvjs的异常,如果出现异常就先将当前的flvPlayer销毁,然后重新调用createVideo方法,不过on监听时间为15秒,也就是说断线15秒以后才会重新连接。