• 【流媒体】Nginx+nginxhttpflvmodule流媒体+鉴权


    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="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

  • 相关阅读:
    oracle游标
    PLSQL
    git移除target目录下已经提交的内容案例
    git全局配置
    [github]Windows系统下将本地仓库的项目上传到远程仓库(含仅单独上传更新文件情况)
    小试Python交互模式(Python Shell)与读文件操作报错(OSError)的解决办法
    C盘内存“侵略者”——Unity开发环境中GI Cache默认目录的修改
    Hibernate学习——Hibernate Tools for Eclipse Plugins的下载与安装
    博客开篇——应用Markdown编辑器呈现样式和内容
    pt-table-checksum使用dsn方式连接检测从库
  • 原文地址:https://www.cnblogs.com/jxd283465/p/15801959.html
Copyright © 2020-2023  润新知