Nginx安装及依赖
pcre
- wget https://sourceforge.net/projects/pcre/files/pcre/8.44/pcre-8.44.tar.gz
- tar -zxvf pcre-8.44.tar.gz
- cd pcre-8.44
- ./configure
- make
- sudo make install
zlib
- wget http://zlib.net/zlib-1.2.11.tar.gz
- tar -zxf zlib-1.2.11.tar.gz
- cd zlib-1.2.11
- ./configure
- make
- sudo make install
openssl
- wget http://www.openssl.org/source/openssl-1.1.1g.tar.gz
- tar -zxvf openssl-1.1.1g.tar.gz
- cd openssl-1.1.1g
- ./Configure LIST | grep -i linux
- ./Configure linux-x86_64 --prefix=/usr
- make
- sudo make install
nginx
nginx下载
- xwget https://nginx.org/download/nginx-1.18.0.tar.gz
- tar zxf nginx-1.18.0.tar.gz
nginx-http-flv-module下载
git clone https://github.com/winshining/nginx-http-flv-module.git
编译
cd nginx-1.18.0
- ./configure --add-module=../nginx-http-flv-module (nginx-http-flv-module的路径)
- make
- sudo make install
配置nginx
配置文件路径/usr/local/nginx/conf/nginx.conf
重点配置介绍
... http { include mime.types; default_type application/octet-stream; keepalive_timeout 65; server { listen 80; #http-flv的拉流端口 ... # http-flv的相关配置 location /live { flv_live on; #打开HTTP播放FLV直播流功能 chunked_transfer_encoding on; #支持'Transfer-Encoding: chunked'方式回复 add_header 'Access-Control-Allow-Origin' '*'; #添加额外的HTTP头 add_header 'Access-Control-Allow-Credentials' 'true'; #添加额外的HTTP头 } ... } } rtmp_auto_push on; rtmp_auto_push_reconnect 1s; rtmp_socket_dir /tmp; 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 1935; server_name www.test.*; #用于虚拟主机名后缀通配 #ffmpeg推流的application application myapp { live on; gop_cache on; #打开GOP缓存,减少首屏等待时间 on时第一帧加载快,off时第一帧加载慢 # @StringKai 在博客https://blog.csdn.net/string_kai/article/details/100598268提到on时延高,off时延低,不过我在测试时并没有感觉出时延的差别 } ... } ... }
完整配置文件
worker_processes 1; #should be 1 for Windows, for it doesn't support Unix domain socket #worker_processes auto; #from versions 1.3.8 and 1.2.5 #worker_cpu_affinity 0001 0010 0100 1000; #only available on FreeBSD and Linux #worker_cpu_affinity auto; #from version 1.9.10 error_log logs/error.log error; #if the module is compiled as a dynamic module and features relevant #to RTMP are needed, the command below MUST be specified and MUST be #located before events directive, otherwise the module won't be loaded #or will be loaded unsuccessfully when NGINX is started #load_module modules/ngx_http_flv_live_module.so; events { worker_connections 4096; } http { include mime.types; default_type application/octet-stream; keepalive_timeout 65; server { listen 80; location / { root /var/www; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location /live { flv_live on; #open flv live streaming (subscribe) chunked_transfer_encoding on; #open 'Transfer-Encoding: chunked' response add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header } location /hls { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /tmp; add_header 'Cache-Control' 'no-cache'; } location /dash { root /tmp; add_header 'Cache-Control' 'no-cache'; } location /stat { #configuration of streaming & recording statistics rtmp_stat all; rtmp_stat_stylesheet stat.xsl; } location /stat.xsl { root /var/www/rtmp; #specify in where stat.xsl located } #if JSON style stat needed, no need to specify #stat.xsl but a new directive rtmp_stat_format #location /stat { # rtmp_stat all; # rtmp_stat_format json; #} location /control { rtmp_control all; #configuration of control module of rtmp } } } rtmp_auto_push on; rtmp_auto_push_reconnect 1s; rtmp_socket_dir /tmp; rtmp { out_queue 4096; out_cork 8; max_streams 128; timeout 15s; drop_idle_publisher 15s; log_interval 5s; #interval used by log module to log in access.log, it is very useful for debug log_size 1m; #buffer size used by log module to log in access.log server { listen 1935; server_name www.test.*; #for suffix wildcard matching of virtual host name application myapp { live on; gop_cache on; #open GOP cache for reducing the wating time for the first picture of video } application hls { live on; hls on; hls_path /tmp/hls; } application dash { live on; dash on; dash_path /tmp/dash; } } server { listen 1935; server_name *.test.com; #for prefix wildcard matching of virtual host name application myapp { live on; gop_cache on; #open GOP cache for reducing the wating time for the first picture of video } } server { listen 1935; server_name www.test.com; #for completely matching of virtual host name application myapp { live on; gop_cache on; #open GOP cache for reducing the wating time for the first picture of video } } }
推拉流
推流
rtmp://ip:1935/myapp/mystream
1935与nginx配置listen 1935对应,myapp与nginx配置中application myapp对应,mystream为推流密码
ffmpeg
- ffmpeg -rtsp_transport tcp -i rtsp://user:password@ip:port/Streaming/channels/101 -c copy -f flv rtmp://127.0.0.1:1935/myapp/mystream
-
参数解析
-
-rtsp_transport tcp: 固定写法
-
user:用户名
-
password:密码
-
ip:摄像头或NVR的IP地址
-
port:摄像头或NVR的RTSP端口,默认是554,具体的RTSP取流规则可以百度
-
-c copy: 输出直接复制,不转换格式
-
-f flv:转成flv
-
rtmp://127.0.0.1:1935/myapp/mystream:
-
根据Nginx配置文件生成,端口号1935与nginx配置中的listen 1935对应;
-
myapp对应配置文件中的application myapp;
-
mystream名字不固定,会在后续用flvjs取流时用到
-
-
obs推流
拉流
vlc rtmp拉流
flv.js拉流
flvjs是哔哩哔哩开源的web播放器,使用方法可以参考官方demo。但是官方给出的demo代码比较多,如果只是想简单实现的话可以参考下方的完整代码,注意flvjs是通过bootcdn引入的。
关键代码说明
var flvPlayer = flvjs.createPlayer({ type: 'flv', enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js isLive: true, //直播模式 hasAudio: true, //开启音频 hasVideo: true, stashInitialSize: 128, enableStashBuffer: false, //播放flv时,设置是否启用播放缓存,只在直播起作用。 url: '' })
url格式 http://ip:80/live?port=1935&app=myapp&stream=mystream
ip:80中的80端口与nginx配置中listen 80对应,
后面的三个get参数port=1935&app=myapp&stream=mystream是固定格式,
1935与nginx配置listen 1935对应,
myapp与nginx配置中application myapp对应,
mystream与ffmpeg推流命令最后的rtmp://127.0.0.1:1935/myapp/mystream 对应
完整代码
<!DOCTYPE html> <html> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <title>flv.js demo</title> <style> .mainContainer { display: block; width: 1024px; margin-left: auto; margin-right: auto; } .urlInput { display: block; width: 100%; margin-left: auto; margin-right: auto; margin-top: 8px; margin-bottom: 8px; } .centeredVideo { display: block; width: 100%; height: 576px; margin-left: auto; margin-right: auto; margin-bottom: auto; } .controls { display: block; width: 100%; text-align: left; margin-left: auto; margin-right: auto; } </style> </head> <body> <div class="mainContainer"> <video id="videoElement" class="centeredVideo" controls width="1024" height="576">Your browser is too old which doesn't support HTML5 video.</video> </div> <br> <div class="controls"> <button onclick="flv_start()">开始</button> <button onclick="flv_pause()">暂停</button> <button onclick="flv_destroy()">停止</button> <input style="width:100px" type="text" name="seekpoint" /> <button onclick="flv_seekto()">跳转</button> </div> <script src="https://cdn.bootcdn.net/ajax/libs/flv.js/1.5.0/flv.js"></script> <script> var vElement = document.getElementById('videoElement'); if (flvjs.isSupported()) { var flvPlayer = flvjs.createPlayer({ type: 'flv', enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js isLive: true, //直播模式 hasAudio: true, //关闭音频 hasVideo: true, stashInitialSize: 128, enableStashBuffer: false, //播放flv时,设置是否启用播放缓存,只在直播起作用。 url: 'http://example.com/live?port=1935&app=myapp&stream=mystream' }); flvPlayer.attachMediaElement(vElement) flvPlayer.load() //加载 } setInterval(function () { vElement.playbackRate = 1 console.log("时延校正判断"); if (!vElement.buffered.length) { return } var end = vElement.buffered.end(0) var diff = end - vElement.currentTime console.log(diff) if (5 <= diff && diff <=60) { console.log("二倍速") vElement.playbackRate = 2 } if (diff > 60) { console.log("跳帧") vElement.currentTime = end } }, 2500) function flv_start() { flvPlayer.play() } function flv_pause() { flvPlayer.pause() } function flv_destroy() { flvPlayer.pause() flvPlayer.unload() flvPlayer.detachMediaElement() flvPlayer.destroy() flvPlayer = null } function flv_seekto() { player.currentTime = parseFloat(document.getElementsByName('seekpoint')[0].value) } </script> </body> </html>
其他问题
至此已经完成了整个实现过程,但是还有一些问题需要注意。
累积时延问题
由于网络波动或者网页切换到后台等原因,flvjs播放会有累积时延,这在摄像头监控画面中是无法忍受的,
主要参考了github issues以及【入门】无插件web直播解决方案,ffmpeg+nginx-http-flv-module+flv.js给出的解决方案:
video对象可以获取currentTime以及endTime,设置一个定时器比较一下二者时间差,时间差小于60s时倍速播放,大于60s时直接跳帧,
因为视频监控的用户更关心最近的画面,如下:
setInterval(function () { vElement.playbackRate = 1 console.log("时延校正判断"); if (!vElement.buffered.length) { return } var end = vElement.buffered.end(0) var diff = end - vElement.currentTime console.log(diff) if (5 <= diff && diff <=60) { console.log("二倍速") vElement.playbackRate = 2 } if (diff > 60) { console.log("跳帧") vElement.currentTime = end } }, 2500)
自动播放问题
有时候可能一个web页面需要展示好几个监控视频画面,让用户依次点击开始播放不太方便,
需要在video标签中加入autoplay 属性实现自动播放,但是一些浏览器比如chrome禁止音频内容的自动播放,可以在video标签中加入muted属性,如下:
<video id="videoElement" class="centeredVideo" muted autoplay width="1024" height="576">Your browser is too old which doesn't support HTML5 video.</video>
flvjs播放器要主动销毁
该问题是我在使用Vue进行前端开发时遇到的,在离开某个Vue页面后并没有主动销毁flvjs的播放器,后台仍然在接受数据,
导致再次回到该页面时无法重复创建flvjs播放器,加载不出监控画面。解决方法是在离开页面的回调函数中写入销毁的函数
function flv_destroy() { flvPlayer.pause() flvPlayer.unload() flvPlayer.detachMediaElement() flvPlayer.destroy() flvPlayer = null }
保存直播视频到服务器
server { listen 1935; server_name www.test.*; #用于虚拟主机名后缀通配 application myapp { live on; record video; #记录直播视频 record_path /tmp/rec; #视频保存路径 record_suffix -%d-%b-%y-%T.flv; #视频保存名:日期+.flv
回调鉴权
nginx-rtmp默认url过长截断问题
修改nginx-http-flv-module目录下的ngx_rtmp_cmd_module.h文件
flv.js访问url
http://127.0.0.1:81/live?port=1935&app=myapp&stream=mystream&key=***
@PostMapping("check") public void check(@RequestParam("key") String key){ System.out.println(key); }
nginx配置
# Many publishers, many subscribers # no checks, no recording application myapp { live on; # The following notifications receive all # the session variables as well as # particular call arguments in HTTP POST # request # Make HTTP request & use HTTP retcode # to decide whether to allow publishing # from this connection or not on_publish http://localhost:8080/publish; # Same with playing on_play http://localhost:8080/play; on_play_done http://localhost:8080/play_done; # Publish/play end (repeats on disconnect) on_done http://localhost:8080/done; # All above mentioned notifications receive # standard connect() arguments as well as # play/publish ones. If any arguments are sent # with GET-style syntax to play & publish # these are also included. # Example URL: # rtmp://localhost/myapp/mystream?a=b&c=d # record 10 video keyframes (no audio) every 2 minutes record keyframes; record_path /tmp/vc; record_max_frames 10; record_interval 2m; # Async notify about an flv recorded on_record_done http://localhost:8080/record_done; }
相关链接
https://blog.csdn.net/weixin_44387339/article/details/117374633?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.pc_relevant_default&spm=1001.2101.3001.4242.1&utm_relevant_index=3
https://*.com/questions/39024703/openssl-make-failure-error-x86-64-no-such-file-or-directory
https://blog.csdn.net/string_kai/article/details/100598268
https://blog.csdn.net/string_kai/article/details/101038941?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2~default~LandingCtr~default-3.queryctrv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~LandingCtr~default-3.queryctrv2&utm_relevant_index=6
https://www.cnblogs.com/goldenretriever/p/15585122.html
https://www.jianshu.com/p/74c2bba02990