使用Nginx搭建直播服务器(nginx-http-flv-module)

这里写自定义目录标题

1 简介

nginx-http-flv-module是在nginx-rtmp-module基础上开发的一个直播模块。
nginx-http-flv-module是基于nginx-rtmp-module 的流媒体服务器。它具备了所有nginx-rtmp-module的功能,并且新增多种新功能,功能对比如下。
感谢Arut创造了nginx-rtmp-module,它是Nginx的一个优秀的第三方模块,可以用来直播,支持RTMP,HLS和DASH方式直播,还支持调用第三方软件进行转码,录制视频等功能,由于依托Nginx,性能也比较高。但是美中不足的地方也不少,例如首屏时间长,不支持HTTP-FLV方式直播,不支持虚拟主机(vhost)功能,省略listen配置无法接受连接,有很多很明显的bug等问题。

功能简介
兼容nginx-rtmp-module的所有功能,详细说明参考nginx-rtmp-module的Wiki。
支持HTTP-FLV方式的直播
支持的播放器有VLC,OBS和flv.js等,只需要输入如下地址即可:
http://yourdomain[:httpport]/dir?[rtmpport=xxx&]app=yyy&stream=zzz
其中httpport是Nginx的配置文件中http块中监听的端口,如果这个端口是80,那么可以省略不写;rtmpport是Nginx的配置文件中rtmp块中监听的端口,如果这个端口是1935,那么可以省略不写。dir是Nginx的配置文件中http块中location后的路径。参数app和stream见下文的推流一节说明。
nginx-http-flv-module也支持HTTPS-FLV方式的直播。

支持虚拟主机功能
nginx-rtmp-module不支持虚拟主机功能。那什么是虚拟主机功能呢?假设有一台服务器,其IP地址是192.168.1.110,我配置了一个DNS服务器,将两个域名www.testa.com和www.testb.com都指向192.168.1.110,那么对于Web服务,我可以针对这两个域名配置不同的Web页面,通过不同的域名访问不同的Web页面,但其实这两个不同的服务都指向同一台服务器。这两个域名对应同一个IP地址就是虚拟主机功能。与http配置一样,nginx-http-flv-module通过server_name来指定域名,也支持前向通配,后向通配和完全匹配:
在这里插入图片描述
兼容性
NGINX 的版本应该大于或者等于 1.2.6,与其他版本的兼容性未知。

支持的系统
Linux(推荐)/ FreeBSD / MacOS / Windows(受限)。
支持的播放器
VLC (RTMP & HTTP-FLV) / OBS (RTMP & HTTP-FLV) / JW Player (RTMP) / flv.js (HTTP-FLV)。

2 安装

下载地址 https://github.com/winshining/nginx-http-flv-module

在这里插入图片描述
https://github.com/winshining/nginx-http-flv-module/tree/v1.2.8

 unzip nginx-http-flv-module-1.2.8.zip 
 mv nginx-http-flv-module-1.2.8 nginx-http-flv-module
./configure --add-module=/usr/local/nginx-http-flv-module  
或者 ./configure --add-dynamic-module=/path/to/nginx-http-flv-module

./configure --prefix=/usr/local/nginx-flv \
--with-http_ssl_module \
--with-debug \
--with-openssl=/usr/local/src/openssl-1.1.1 \
--with-zlib=/usr/local/src/zlib-1.2.12 \
--with-pcre=/usr/local/src/pcre-8.40 \
--add-module=/usr/local/src/nginx-http-flv-module

make -j2 && make install

在这里插入图片描述

3 配置

rtmp {
    server {
        listen 1936;
        chunk_size 4000;
        application hls {
            live on;
            hls on;
            hls_path /test;
            hls_fragment 1s;
        }
    }
}
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    sendfile        on;
    keepalive_timeout  65;
    # 支持跨域的配置
    add_header 'Access-Control-Allow-Origin' '*';
    # 请求允许发送cookie
	add_header 'Access-Control-Allow-Credentials' 'true';

 server {
        listen       8081;
        server_name  localhost;
        location /flv {
                flv_live on;
                chunked_transfer_encoding on;
        }

        location /hls {
           types{
             application/vnd.apple.mpegurl m3u8;
             video/mp2t ts;
           }
           alias /test;
           expires -1;
        }

        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }

        location /stat.xsl {
            root /usr/local/src/nginx-http-flv-module/;
        }

        location /control {
            rtmp_control all;
        }

        location /rtmp-publisher {
            root /usr/local/src/nginx-http-flv-module/test;
        }

        location / {
            root /usr/local/src/nginx-http-flv-module/test/www;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

主要配置为: rtmp标签下的server配置其监听1936端口,并配置location hls设置属性live on 表示开启直播。http标签下的server配置其监听8081端口,并配置location /flv设置属性 flv_live on。注意其中的 add_header ‘Access-Control-Allow-Origin’ ‘*’ 与 add_header ‘Access-Control-Allow-Credentials’ ‘true’ 很重要,主要解决了前端通过HTTP方式拉流是的跨域问题。
目前nginx-http-flv-module模块配置完成,并且nginx配置文件也已经配置完成 下面进行推流与拉流测试。

4 推流测试

用海康威视的网络摄像头推流
#ffmpeg -rtsp_transport tcp -i rtsp://admin:[email protected]:554/h264/ch0/main/av_stream -c:v libx264 -c:a aac -f flv -an rtmp://192.168.5.177:7933/myapp/room
在这里插入图片描述
用笔记本推流
ffmpeg -re -i 1.mp4 -vcodec libx264 -acodec aac -f flv rtmp://IP:1936/hls/1
在这里插入图片描述
在这里插入图片描述

推流成功。这里我说明一下 我使用的是海康威视的网络摄像头 rtsp://admin:[email protected]:554/h264/ch0/main/av_stream 这个地址是我的摄像头播放地址,请查看并使用你使用的摄像头播放地址。
下面通过VLC播放器进行拉流播放。
播放URL为http://IP:8081/flv?port=1936&app=hls&stream=1 其中IP换成你的服务器ip ,flv是nginx配置文件中配置的请求地址,可以查看nginx配置文件中的配置,port为nginx配置文件中rtmp标签下server监听的端口号(注意开放服务器防火墙),app=hls这个myapp与rtmp标签下 server下的location myapp所对应,1=room这个room与你推流地址所对应。

FLV:http://ip:8081/flv?port=1936&app=hls&stream=1

RTMP:rtmp://ip:1936/hls/1

HLS:http://ip:8081/hls/1.m3u8
在这里插入图片描述
在这里插入图片描述

3 flv.sj

https://github.com/Bilibili/flv.js
通过VUE播放实时视频流
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:[email protected]: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秒以后才会重新连接。

rtmp://180.76.243.46:1936/hls/1
http://180.76.243.46:8081/flv?port=1936&app=hls&stream=1
ffmpeg -re -i media_file -vcodec copy -acodec copy -f flv rtmp://yourdomain/app/stream

猜你喜欢

转载自blog.csdn.net/qq_37705525/article/details/125471133