当前位置: 首页 > news >正文

WebRTC获取GB28181监控摄像头实时音视频流的实现方法

我写了一个网关,核心是打通GB28181和WebRTC,通过浏览器获取监控摄像头的实时音视频流。完全用C/C++实现,依赖极少,效率极高。还封装了一个前端js类,只需要简单几条语句就能在web上拉取摄像头的流。

一、目标和分析

1、在浏览器上直接播放,不用安装任何插件

只能用WebRTC。

如果采用厂商(如海康)的SDK,意味着要安装插件,只能限制在windows下运行。 而WebRTC是现代浏览器的标准。

2、实时性,不能感觉到延时

这里的问题就变成,用什么方式获取摄像头的音视频流?

目前有三种常见方式:

a、厂家专用sdk 本质上私有协议,兼容性差。

b、RTSP协议 优点是接入简单,缺点是几乎没有浏览器可以显示实时 RTSP 协议

c、GB/T.28181即所谓的国标,本质上是SIP协议

这个最佳,媒体采用rtp传送,实时性强。

GB基于sip协议可以充分利用以前开发软交换的经验。 国标开放性也最好,国产视频监控摄像头强制必须支持该标准。

3、在任意操作系统上使用

在某些行业领域,强制安装国信麒麟等国产操作系统, 因此展示监控音视频流的客户端,要求能够在各种操作系统上运行,无论桌面操作系统windows、Linux(包括国信麒麟)、MacOS还是手机操作系统安卓或iOS。

支持WebRTC的现代浏览器,确实可以在任意操作系统上使用。 所以问题变成了,我们实现的网关可以在任意操作系统上部署。跨平台,对第三方库的依赖尽可能少。

4、支持H.264或H.265

最新版的浏览器 Chrome(版本136+以上)、Safari等已经直接支持H.265,Firefox还只能支持H.264。

网关收到呼叫时,如果摄像头的流是H.265,须判断前端浏览器是否支持,如果不支持,则可进行转码。 转码带来的问题是比较费cpu,而且通常会带来2、3秒的延时。

应该尽量避免转码。除了使用支持h.265的浏览器,也可以设置摄像头编码方式为h.264。

5、网关的效率

除了前面讨论的应尽量避免转码,还应支持流分发:

多个前端获取同一摄像头时,应该采用流分发,而非软交换sip通常习惯的发起多个呼叫。 这也是GB/T.28181对媒体服务器的要求。 特别是在转码的情况下,避免了相同流的重复解复用和转码,对资源的节约是很大的。

要能支持多个浏览器拉取多个摄像头的流,网关服务器的cpu占用不高,高效率并发。

二、实现

系统结构:

web浏览器 <--WebRTC--> 网关服务器 <--GB28181--> 监控管理平台(如海康) <--GB28181--> 摄像头

或直接挂摄像头:

web浏览器 <--WebRTC--> 网关(软交换)服务器 <--GB28181--> 摄像头

下面总结一些要点:

1、ps解包

和普通软交换不同,大部分监控摄像头使用GB28181只支持ps封包,ps可以将音频视频封在一起,因此要手写一个高效的解包模块。信令部分也要对摄像头的sdp和信令部分进行处理,以符合国标。

2、WebRTC的音视频流是分开的

采用不同rtp载荷,虽然端口可能复用。

3、发往WebRTC的流封装

对于H.264,须按rfc6184进行封装;

对于h.265,须按rfc7798进行封装(RTP Payload Format for High Efficiency Video Coding (HEVC))。

4、推到浏览器的流要注意时间戳

视频简单起见可以套用ps流的时间戳间隔。

要准确发送rtcp给浏览器。

传送视频帧的rtp,其mark位要准确,简单来说就是相同时间戳的最后一个rtp包mark必须置1,其它都置0。否则浏览器将无法正常播放。

音频的时间戳,应根据采样进行处理,通常都是8000采样率,所以每次加160即可。

5、浏览器信令选择:SIP

WebRTC没有规定控制信令,我采用wss承载的sip协议,sip是久经考验的标准,和GB相同类型。

支持wss-sip协议有开源的jssip,是js实现的完整sip协议栈,很成熟。

6、网关实现尽量减少依赖

网关的sip协议栈,媒体处理都是我自己实现的。

ws/wss协议栈、STUN协议栈、DTLS协议处理也全都自己实现,仅使用必要的库,这些库往往操作系统自带,如openssl、srtp。

如果需使用h.265到h.264的转码,我也避免使用极其臃肿的FFmpeg,而是直接使用短小精悍的libde265和libx264,这两个库很多操作系统也有预装。(实际上FFmpeg底层也会套娃般使用这两个库)

7、配置简单

只使用一个ini文件。

三、浏览器前端和效果

我对jssip进行了封装,呼叫摄像头就一条语句:

rtcMonitor.makeCall(摄像头id);

只有一个参数,即摄像头id,是一个符合 GB./T28181 标准的20位字符串,通常可从监控管理平台(如海康)获取。

下面是前端调用的demo说明:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title> WebRTC 获取 GB28181 摄像头音视频 </title><style>body { font-family: Arial, sans-serif; text-align: center; margin-top: 20px; background-color: #f4f4f4; }.container { width: 90%; max-width: 800px; margin: auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }input, button { padding: 10px; margin: 5px; width: 90%; box-sizing: border-box; }.status { margin-top: 10px; font-weight: bold; }.videos { display: flex; justify-content: space-around; margin-top: 20px; }.videos video { width: 45%; max-width: 350px; border: 1px solid #ccc; background-color: #000; }#localVideo { transform: scaleX(-1); } /* 镜像本地视频 */.video-container {width: 100%; /* 父容器宽度设为100% */}.video-container video {max-width: 100%; /* 视频宽度不超过父容器的最大宽度 */height: auto;   /* 高度自动,以保持原始宽高比 */}</style><script src="./RtcMonitor.js" type="module"></script>
</head>
<body><div class="container"><h1>WebRTC 获取 GB28181摄像头 音视频</h1><div class="status" id="status">未连接...</div><hr><h2>SIP 账户设置</h2><input type="text" id="sip_id" placeholder="5001" value="5001"><input type="password" id="password" placeholder="密码" value="password123"><button id="connectBtn">初始化并连接</button><hr><h2>呼叫摄像头</h2><input type="text" id="target_id" placeholder="摄像头id,20位国标编号" value="34020000001320000001"><button id="callBtn">呼叫</button><button id="hangupBtn">挂断</button><div class="video-container"><video id="remoteVideo" autoplay playsinline></video></div></div><script type="module">import RtcMonitor from './RtcMonitor.js'var rtcMonitor = null;const connectBtn = document.getElementById('connectBtn');const callBtn = document.getElementById('callBtn');const hangupBtn = document.getElementById('hangupBtn');// 按钮事件监听connectBtn.addEventListener('click', connect);callBtn.addEventListener('click', makeCall);hangupBtn.addEventListener('click', hangupCall);// 初始化callBtn.disabled = true;hangupBtn.disabled = true;// 1. 连接 SIP 服务器function connect() {const sipIdEl = document.getElementById('sip_id').value.toString();const passwordEl = document.getElementById('password').value.toString();var remoteVideoEl = document.getElementById('remoteVideo');var statusEl = document.getElementById("status");// 创建一个 RtcMonitor 类,5个参数:// sipId - 如果有多台或者多个窗口需显示监控画面,需不同的sipId// password - 对应sipId的密码。网关服务器校验// wss_uri - 网关服务器url,必须是合法的 wss 地址// remoteVideoEl - video 元素,用来显示监控流的音视频// statusEl - 用来显示状态的文字信息,可以为null,表示不显示状态rtcMonitor = new RtcMonitor(sipIdEl, passwordEl, 'wss://192.168.0.103:4536/wss', remoteVideoEl, statusEl);rtcMonitor.register();connectBtn.disabled = true;callBtn.disabled = false;}// 2. 发起呼叫function makeCall() {const targetIdEl = document.getElementById('target_id').value.toString();// 向网关服务器发起呼叫,请求摄像头的音频视频流// 只有一个参数,即摄像头id,是一个符合 GB./T28181 标准的 20位字符串,通常可从监控管理平台(如海康)获取rtcMonitor.makeCall(targetIdEl);hangupBtn.disabled = false;callBtn.disabled = true;}// 3. 挂断function hangupCall() {// 如果需要切换看其它摄像头,需调用本函数挂断当前会话,再调用 makeCall(另一个摄像头id)rtcMonitor.hangup();hangupBtn.disabled = true;callBtn.disabled = false;}</script>
</body>
</html>

安卓手机chrome浏览器跑上面demo的效果(使用自签名证书,所以地址栏显示感叹号):

http://www.dtcms.com/a/475095.html

相关文章:

  • 深入剖析:C++、C 和 C# 中的 static
  • 做电商要关注哪些网站临沂网站建设排名
  • 带条件的排名问题
  • 2025 诺贝尔物理学奖 | 从宏观量子隧穿到量子计算
  • 怎么做网站 有空间wordpress 弹性布局设计案例
  • LDPC 码基本概念
  • 在线考试响应式网站模板下载学校如何报销网站开发费用
  • 可以建微信网站的成考做那个网站的题比较好
  • 2025年推荐练习编程游戏和软件
  • 【HarmonyOS】手势处理
  • 学做缝纫的网站wordpress网站换主机
  • 葫芦岛做网站价格免费手机网站系统
  • PS2020使用教程|绘制深秋枫叶风景图
  • 济南建网站多少钱设计说明室内设计现代简约
  • Go语言 编写最简单的命令行工具
  • 【指针学习】
  • Go容器:双向链表和小根堆的源码解读
  • 深圳vi设计工作室搜索seo优化托管
  • 做的最好的理财网站地址一地址二在线发布页
  • 网站开发程序员招聘梅州建站怎么做
  • 普陀酒店网站建设有没有专门做中式的设计网站
  • 基于STM32与influxDB的电力监控系统-20
  • 购物网站开发多少钱免费连网络的软件有哪些
  • LeetCode算法日记 - Day 69: 第 N 个泰波那契数、三步问题
  • 【系统分析师】写作框架:项目风险管理及其应用
  • 容器编排大王Kubernetes——控制器的使用(3)
  • 建筑设计图保定seo网络推广
  • 上海网站设计费用温州鹿城区企业网站搭建
  • 网站建设方案功能哪个网站可以做微商
  • 牛客:大加法数