使用WebRTC播放rtsp监控及coturn服务器搭建(原创)

2,432 阅读9分钟

目录

前言

image.png

作为“全干工程师”,前段时间刚开发完小游戏,最近又当起了音视频开发工程师,最近因为公司里的项目,甲方需求需要支持播放rtsp矿山监控视频,但是因为rtsp不能支持在浏览器直接播放。接下来有两种方案,第一个是在服务端用ffmpeg转码成flv再在前端使用flv.js进行播放,也就是我们之前使用的方案,但是延迟大,容易卡顿不稳定。第二种方案是用webRTC方案,延迟低,性能好,实时性高。就选择了webRTC,但是大多数网上的教程大多数有头无尾,都只是介绍了怎么搭建webRTC,其实都只能在内网播放,最重要的coturn服务器搭建都没有说,实际有很多坑,今天这篇文章就教大家怎么从入门到应用WebRTC,先会跟大家介绍怎么搭建webRTC,接下来会介绍怎么搭建路由器内网映射,最后会给大家介绍怎么在windows上搭建coturn服务器(虽然官网说只支持Linux但还是有办法)。

WebRTC-Streamer

首先我的服务器系统环境是windows,在webRTC技术选型上我选择了WebRTC-Streamer,比较方便好用。这个是github地址,然后去这里下载最新版release包,下载完成以后,使用powershell,进入命令行,进入webrtc-streamer安装目录下,简单执行一下./webrtc-streamer -C config.json -o测试,此时就可以在浏览器的http://127.0.0.1:8000看到一些视频了,这里介绍一下webRTC-streamer的所有启动参数

./webrtc-streamer -V
    -v[v[v]]           : verbosity
    -V                 : 打印版本
    -H [hostname:]port : HTTPServer绑定 (default 0.0.0.0:8000)
	-w webroot         : 获取文件的路径
	-c sslkeycert      : HTTPS的私钥和证书路径
	-N nbthreads       : HTTP服务器的线程数
	-A passwd          : 用于HTTP服务器访问的密码文件
	-D authDomain      : HTTP服务器访问的身份验证域(默认值:mydomain.com)
	-S[stun_address]                   : 使用嵌入的STUN服务器绑定到地址(默认值为0.0.0.0:3478)
	-s[stun_address]                   : 使用外部的STUN服务器绑定到地址(默认值为0.0.0.0:3478)
	-t[username:password@]turn_address : 使用外部TURN中继服务器(默认:禁用)
	-T[username:password@]turn_address : 使用嵌入式TURN中继服务器(默认:禁用)
	-a[audio layer]                    : 指定要使用的音频捕获层(默认值:0)	
	-q[filter]                         : 指定发布筛选器(默认值:.*)
	-o                                 : 使用空编解码器(保持帧编码)
	-C config.json                     : 从JSON配置文件加载URL
	-R [Udp port range min:max]        : 设置webrtc-udp端口范围(默认值为0:65535)
	-n name -u videourl -U audiourl    : 注册视频url和音频url的名称
	[url]                              : 要在源列表中注册的url

细节注意

这里最好带上 -o 否则cpu会受到影响,如果需要改成其他端口号可以用 -H 参数进行指定,毕竟8000端口比较常用。后面还有-S-T参数是后面会用到的,可以接入使用STUN服务器和TURN服务器,后面会讲。

端口映射

经过前面那些步骤,本地已经可以播放视频了,但是别人看不到,这个时候就需要进行端口映射,利用路由器的虚拟服务器功能,我这里是用的TP-LINK的路由器,在传输控制-NAT设置-虚拟服务器下,新增一条记录webrtc,输入外部端口和内部端口,内部服务器IP是你的服务器下ipconfig里的ipv4地址。这里我们再新增一个turn服务器的端口映射后面用得到,端口都选3478,服务器协议一定要选ALL因为要同时支持UDP和TCP,截图如下:

  • webrtc端口映射配置:

image.png

  • turn服务器端口映射配置:

image.png

内网测试播放

前面就已经搭建好你的webrtc服务器了,此时,你应该可以在内网同一网段下看到公网ip:公网端口(你的公网ip可以用这个工具查看)下的webrtc-streamer首页了,这里有一些config文件里的示例的rtsp,多选择一些,总有一些可以播放的,可以用来判断服务是否正常。

image.png

前端代码

如果你可以在浏览器里成功看到webrtc的视频,那么可以继续写前端代码,这里我用的是vue框架,废话不多说,show me the code.

<template>
  <a-modal
    title="webRTC视频监控(H.264专线)"
    :width="1100"
    :visible="visible"
    cancelText="关闭"
    @cancel="handleCancel"
    :confirmLoading="confirmLoading"
    @ok="handleCancel"
  >
    <div class="wrap">
      <div v-for="(_value, _key, index) in lines" :key="index">
        <video ref="player" muted autoplay controls width="1000"></video>
      </div>
    </div>
  </a-modal>
</template>

<script>
import { MONITOR_WEBRTC_SERVER_URL, VIDEO_TYPE_ENUM } from '@/constants'
import '@/utils/webrtc/adapter.min.js'
import WebRtcStreamer from '@/utils/webrtc/webrtcstreamer'
import webrtcConfig from '@/utils/webrtc/webrtcconfig'
import isSupportsWebRTC from '@/utils/webrtc/isSupportsWebRTC'

export default {
  name: 'VideoMonitor',
  data() {
    return {
      players: [],
      lines: [],
      webRtcServerList: [],
    }
  },
  props: {
    // 控制显隐
    visible: {
      type: Boolean,
      default: false,
      required: true,
    },
    // 行数据
    resData: {
      type: Object,
      default: () => {},
      required: false,
    },
    // url
    url: {
      type: String,
      default: '',
      required: false,
    },
    customOptions: {
      type: Object,
      default: () => {},
      required: false,
    },
  },
  created() {
    this.handleCreateVideo()
  },
  watch: {
    visible(val) {
      if (val) {
        this.handleCreateVideo()
      } else {
        // 页面销毁前解绑 webRtcServer
        this.handleReset()
      }
    },
  },
  beforeDestroy() {
    this.handleReset()
  },
  methods: {
    handleReset() {
      for (let i = 0; i < this.webRtcServerList.length; i++) {
        const webRtcServer = this.webRtcServerList[i]
        if (!webRtcServer) return
        webRtcServer.disconnect()
      }
      this.webRtcServerList = []
      this.lines = []
    },
    handleCancel() {
      this.$emit('cancel')
      this.handleReset()
    },
    handleCreateVideo() {
      let options = webrtcConfig.options
      // 判断浏览器是否支持 webRTC 直播视频
      if (!isSupportsWebRTC()) {
        this.$message.error('当前浏览器不支持 WebRTC 无法播放直播视频!')
        this.handleCancel()
        return
      }
      const { videoType, title } = this.customOptions
      const lines = title.split(',')
      const serverUrl = MONITOR_WEBRTC_SERVER_URL
      // 过滤重复的 createVideo
      if (this.lines.length >= lines.length) {
        return
      }
      for (let i = 0; i < lines.length; i++) {
        const channel = lines[i]
        let url = this.url
        switch (videoType) {
          case VIDEO_TYPE_ENUM.默认:
          // 默认大华的监控地址
            url = this.url.replace('?', `?channel=${channel}&`)
            break
          // 海康视频可以直接连线路101,201,01是主码流
          case VIDEO_TYPE_ENUM.海康:
            url = this.url.endsWith('/') ? this.url + channel + '01' : this.url + `/${channel}01`
            break
          // 天翼云 url 不需要处理
          default:
            break
        }

        this.lines.push(url)

        setTimeout(() => {
          var videoElement = this.$refs.player[i]
          const webRtcServer = new WebRtcStreamer(videoElement, serverUrl)
          webRtcServer.connect(url, '', options)
          this.webRtcServerList.push(webRtcServer)
        })
      }
    },
  },
}
</script>

<style lang="css" scoped>
.wrap {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}
.wrap .video {
  width: 1000px;
  max-height: 800px;
}
</style>
// isSupportsWebRTC.js
// 用于判断浏览器是否支持 WebRTC
export default () => {
  try {
    let testRTCPeerConnection =
      window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCIceGatherer
    if (testRTCPeerConnection) {
      let serverConfig = {
        iceServers: [
          {
            urls: ['stun:stun.l.google.com:19302'], // 这里的stun地址可以换成自己的
          },
        ],
      }
      new RTCPeerConnection(serverConfig)
      return true
    } else {
      throw '当前浏览器不支持 WebRTC 无法播放直播视频!'
    }
  } catch (error) {
    console.error('当前浏览器不支持 WebRTC 无法播放直播视频!')
    return false
  }
}

这里我的 customOptions 是由外部传参传入的,videoType是视频监控类型,title是所有线路的字符串,用逗号连接,比如1,2,3就是表示监控线路1,2,3。其他的代码需要根据自己需要调整。包括不同的厂商提供的摄像机rtsp地址WebRtcStreamer.js,adapter.min.js,webrtcconfig.js文件都是从webRTC-streamer的Release文件包下html目录下获取。这里后面还有个坑,后面会说。

Coturn服务器

Cygwin64安装

这个时候如果你只是内网访问就已经没有问题了,但是我们需要外网也可以访问,webRTC-streamer这东西,主要是他有个问题,所有的中间NAT映射都会让他迷路,所以需要用stun服务器去拿到公网ip,turn服务器进行穿透中继,之前部署到k8s集群看不到视频就是因为没有部署这个,不过我们现在是windows,还需要一个所以我们需要再部署一个coturn服务器来中继到外网,但是coturn服务器在windows系统上需要借助Cygwin64工具才可以运行,主要提供linux里的那些库函数和方法。在这里Cygwin官网安装地址安装好cygwin64,安装教程如下:

1.打开安装软件setup.exe 28cfa9d6a3a1717df5ff4dd554ea9a46.png

2.选择下载源,从互联网安装

8ae3bece52e8520b62408a81521c0952.png

3.选择安装目录

52d4cda1d9552f72b9894c72fe1e4e41.png

4.选择本地软件包目录,不用动就可以

18867bf742e6e432eba20ff7772ca9a1.png

5.选择互联网连接,我这里选择直连,如果有梯子可以选系统代理设置

3e0b147cd6f50c351a90cb3a60cc3ec6.png

6.选择下载站点地址,我这里选择163的,也可以选择淘宝的

136bccc4114b7536877001358f2a7233.png

7.选择需要的安装包,这里我已经下载过了,下面这些是必要的包,可以搜索安装上,主要是这几个gambas3-gb-openssl,gcc-core,gcc-g++,libevent-devel,libssl-devel,make

4d671c359e817563afaf08b3e9a1e5d1.png

8.下一页进行安装,安装完成选择添加快捷方式到桌面

接下来就可以点击cygwin快捷方式打开,在这里就可以执行gcc,make那些linux命令了。

运行coturn

然后下载coturn源码,下载完成解压,在cygwin命令行里cd到coturn目录,执行:

1.生成Makefile文件:

CC=gcc ./configure

如果出现 Makefile created: success. 就没有问题

2.编译make:

make

3.安装依赖:

make install

安装完成,如果前面是成功的,进入bin目录下就可以看到 turnserver.exe 还有其他可执行exe文件,但是我们还少了一些文件,签名证书和turnserver.config.

4.签名证书

签名证书可以自己在cygwin命令行里生成一下,刚刚下载了openssl就可以用上了

openssl req -x509 -newkey rsa:2048 -keyout ./turn_server_pkey.pem -out ./turn_server_cert.pem -days 99999 -nodes

这样就会在当前目录下生成两个pem文件,可以选择把他们新建个pem文件夹拖进去

5.配置 turnserver.config

新建一个turnserver.config文件,里面输入以下config内容,记得verbose不要大写,否则就会一直报错timeout,参数具体解释在这里

listening-ip=192.168.1.113
listening-port=3478
tls-listening-port=5349
relay-ip=192.168.1.113
external-ip=xxx(公网ip)
relay-threads=50
lt-cred-mech
cert=pem/turn_server_cert.pem
pkey=pem/turn_server_pkey.pem
pidfile=turnserver.pid
min-port=49152
max-port=65535
user=admin:123456
cli-password=123456
realm=my-domain-name
verbose
fingerprint
syslog

6.执行以下代码,-r后面可以随意接其他字符,-o是后台运行,因为是webRTC所以需要接-a参数

./turnserver -a -f -user=admin:123456 -c ./turnserver.conf -r hangzhou -o

7.这个时候可以执行以下指令看下是否正在运行,3478是上面设定的turn/stun服务监听端口也是默认的端口

netstat -ano|findstr 3478

8.如果需要关掉的可以执行以下指令,PID是上个命令里的线程id

taskkill /f /t /PID 22432

如果没有问题的话到这里就外网就应该可以接收到webRTC视频了。但是可能还有个注意点,就是客户端的代码里没有申明turn和stun服务器的ip和账号密码导致请求都没有到我们的turn服务器。具体的解决方法可以看我的issue