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日志,一般是沒權限,默認nginx是nobody,在nginx.conf指定user root;即可
回調鑒權
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://stackoverflow.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