【流媒體】Nginx+nginx-http-flv-module流媒體+鑑權

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://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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章