node.js实现国标GB28181流媒体点播(即实时预览)服务解决方案
背景
28181协议全称为GB/T28181《安全防范视频监控联网系统信息传输、交换、控制技术要求》,是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/TC100)归口,公安部一所等多家单位共同起草的一部国家标准(以下简称28181)。
28181协议在全国平安城市、交通、道路等监控中广泛采用,若想做统一的大监控平台,则支持28181协议接入是必不可少的。如今很多客户都是想在之前使用的28181平台的基础上进行拓展。
EasyDSS云平台目标支持市面上绝大多数监控设备接入,所以支持28181协议也是必然的要求;结合EasyDSS云平台的标准设备接入能解决市面上绝大多数设备以及平台的接入。
说明
GB28181流媒体服务器是EasyDSS云平台提供的流转发服务器,负责将GB28181设备/平台推送的PS流转成ES流,然后推送给EasyDSS流媒体服务器进行分发。
同时,GB28181流媒体服务器对外提供HTTP API接口,通过接口可以获知流媒体转发服务的运行状态信息,转发会话信息,服务器配置和版本信息等;
GB28181流媒体服务器提供以下功能:
1. 接受和处理GB28181接入服务器的推流请求(如有推流权限验证则调用验证服务器接口);
2. 接受和处理GB28181设备的推流;
3. 实时流媒体处理,PS(TS)转ES;
4. 推送ES流到EasyDSS流媒体服务器;
5. 接受和处理GB28181接入服务器的断开推流请求;
6. 对外提供服务器获取状态、信息,控制等http API接口;
GB28181流媒体服务器对接EasyDSS云平台整体框架
流媒体点播详细流程
1 接入服务器发送Invite请求
接入服务器向流媒体服务器发送Invite请求,请求流媒体服务返回携带SDP 消息体,消息体中
描述了媒体服务器接收媒体流的IP、端口、媒体格式等内容;
Invite请求代码如下:
const options = {
serialServer: serialServer,
serialDevice: code,
method: common.SIP_INVITE,
contentType: common.CONTENT_NONE,
content: sdp,
host: hostip,
port: hostport,
rtpovertcp: (parseInt(rtpovertcp)===0?'UDP':'TCP')
};
console.log('inviteMediaServer......sendRequest' + JSON.stringify(options));
uas.sendRequest(options);
2 流媒体服务接受Invite请求处理并ACK应答
流媒体服务接受Invite请求,并在回调函数中处理请求,js代码如下:
uas.on('invite', async ctx => {
const request = ctx.request;
const content = JSON.parse(request.content);
const status = 200;
const serial = sip.parseUri(request.uri).user;
const host = config.server.serverHost;
let ssid = serial.substring(16,20);// PrefixInteger(sessionid,4);
let sirialid = serial.substring(3,8);
const ssrc = "0"+sirialid+ssid;
console.log("ssrc = "+ssrc);
let sdp = '';
//如果已存在
let bHas = this.session_.has(serial);
console.log(bHas);
if (bHas) {
console.log('this.session_ has exist serial: '+serial);
sdp = '';
}
else{
let port = config.server.udpPort;//流媒体接收TCP端口
let transport = 'RTP/AVP';
let a = "a=recvonly\r\n";
if(content.rtpovertcp === 'TCP' )
{
port = config.server.tcpPort;//流媒体接收TCP端口
transport = 'TCP/RTP/AVP';
a = "a=recvonly\r\na=setup:passive\r\n";
}
sdp = "v=0\r\n" +
`o=${serial} 0 0 IN IP4 ${host}\r\n` +
"s=Play\r\n" +
`c=IN IP4 ${host}\r\n` +
"t=0 0\r\n" +
`m=video ${port} ${transport} 96 98 97\r\n` +
"a=rtpmap:96 PS/90000\r\n" +
"a=rtpmap:98 H264/90000\r\n" +
"a=rtpmap:97 MPEG4/90000\r\n" +
`${a}`+
//`a=connection:new\r\n` +
`y=${ssrc}\r\n`;
// A new channel is coming, delete the old
rtpserver.deleteChannels(parseInt(ssrc));
// Create a new stram,and add to redis
this.registerStream(parseInt(ssrc),uuidv4(),true);
}
let response = sip.makeResponse(request, status, common.messages[status]);
uas.sendAckEx(response, sdp);
});
如上代码所示,我们在SDP消息体中提供了两种流传输方式,分别是TCP和UDP,通过Invite请求所带的 “rtpovertcp ”参数来控制,TCP方式因为其不丢包的传输方式在GB28181设备推流到公网服务器的方案中得以广泛应用,然而,目前市面上的多数支持国标的设备都不支持tcp模式推流,udp仍然是主流的推流方式,不过,经测试udp推流方式在公网应用中效果比较差,需要进一步优化或者改进。
3 接入服务器接收ACK应答并Invite请求设备开始推流
回调函数中ack应答处理js代码如下:
uas.once('ack', async ctx => {
const request = ctx.request;
const callId = request.headers['call-id'];
if (request.content.length > 0 )
{
const serial = serialDevice;//sip.parseUri(request.headers.from.uri).user;
let response ;
if(!this.session_.has(callId))
{
response = await this.inviteDevice(serial, code, callId, request.content);
//Invite Device is complete
if(response != undefined)
{
if(response.content)
{
const transform = require('sdp');
const res = transform.parse(response.content);
console.log(res.media[0].protocol);
if((res.media[0].protocol === 'RTP/AVP'&&parseInt(rtpovertcp)===0) ||
(res.media[0].protocol === 'TCP/RTP/AVP'&&parseInt(rtpovertcp)===1) ){
if (response.status === 200 )
{
//send ack to stream server
this.ackMediaServer(response.status,request,request.content);
this.session_.set(callId, response);
}
}
else{
response.status = 700;
}
}
console.log('inviteMediaServer ack is coming.......response='+JSON.stringify(response));
}
resolve(response);
}
else{
console.log('inviteMediaServer this.session_.has: '+callId);
}
}
});
如上代码所示,在InviteDevice请求完成后,我们在返回Response处理过程中做过一次特殊处理,即:如果TCP拉流时发现设备拉流应答中返回其推流模式依然是’RTP/AVP’的UDP模式,我们认为其设备不支持TCP模式,从而向上层返回700,不支持的流媒体传输方式。
4 Invite设备正常返回200应答并传递给流媒体服务器
代码在第3点中有所体现。
5 流媒体服务接受拉流请求成功应答
uas.on('ack', async ctx => {
const request = ctx.request;
if (request.content.length === 0) {
return;
}
const serial = sip.parseUri(request.headers.from.uri).user;
this.session_.set(serial, request);
const ssrc = serialTossrc(serial);
// resole a new stram,and refresh to redis
const info = JSON.parse(await redis.get(`stream:${parseInt(ssrc)}`));
this.registerStream(parseInt(ssrc),info.uuId,false);
});
至此,整个拉流过程已经完成,这时,不出意外的话设备端将会通过我们指定的传输方式将流推送到我们指定的UDP/TCP服务器上,并转发到我们指定的EasyDSS流媒体服务器。
获取更多信息
邮件:support@easydarwin.org
WEB:www.EasyDarwin.org
流媒体技术交流QQ群:538316953
Copyright © EasyDarwin.org 2012-2018
node.js实现国标GB28181流媒体点播(即实时预览)服务解决方案的更多相关文章
- node.js实现国标GB28181设备接入的sip服务器解决方案
方案背景 在介绍GB28181接入服务器的方案前,咱们先大概给大家介绍一下为什么我们选择了用nodejs开发国标GB28181的服务,我大概给很多人介绍过这个方案,大部分都为之虎躯一震,nodejs在 ...
- RunJS推荐用于个人使用(使用方便JS、css实时预览、编辑、管理等功能)
RunJS,在线编写.展示html.js.css代码,拥有实时预览.分享.Fork.代码高亮.自己主动完毕等多项特性,提供文件上传.多种登录方式. 地址:http://runjs.cn/ waterm ...
- node.js中使用socket.io + express进行实时消息推送
socket.io是一个websocket库,包含客户端的js和服务端的node.js,可以在不同浏览器和移动设备上构建实时应用. 一.安装 socket.io npm install socket. ...
- js实现本地的图片压缩上传预览
js在设计时考虑到安全的原因是不允许读写本地文件的,随着html5的出现提供了fileReader AP从而可以I实现本地图片的读取预览功能, 另外在移动端有的限制图片大小的需求,主要是考虑图片过大会 ...
- Node.js中读取文件后用Json.parse方法报错解决方案
今天,在调试一个node项目时,发现了一个很大的坑,在此分享给大家! 大家都知道,Json.parse()方法对格式要求是很严格的,格式不对极其容易报错,但是有时候格式看似是正确的也会报错. 比如这一 ...
- 用node.js做cluster,监听异常的邮件提醒服务
__ __ __ _ __ ____ ____ ____/ /__ _____/ /_ _______/ /____ _____ ___ ____ ___ ____ _(_) / / __ \/ __ ...
- node.js 在使用child_process 模块时候,调试端口占用的问题解决方案(EADDRINUSE)
在fork的时候,带参数{ execArgv: ['--debug=' + (process.debugPort + 1)] }
- html + js 实现图片上传,压缩,预览及图片压缩后得到Blob对象继续上传问题
先上效果 上传图片后(设置了最多上传3张图片,三张后上传按钮消失) 点击图片放大,可以使用删除和旋转按钮 (旋转功能主要是因为ios手机拍照后上传会有写图片被自动旋转,通过旋转功能可以调正) html ...
- [js] - 前端FileReader使用,适用于文件上传预览.(并未传入后端)
<body> <div class="box"> <div class="container"> <ul> &l ...
随机推荐
- crossapp里的位置设置
crossapp里有Frame.Center,这两种都是可以用来确定一个view的位置和大小. 不同点:Frame定位是以View的左上角为参照点,Center是以View的中心点为参照点 注意cro ...
- 解决svn锁定
问题:今天去公司 svn-update的时候,报错svn:E155004,提示说什么locked 解决:svn cleanup解除锁定,然后就可以操作了. 原因:SVN 本地更新时,由于一些操作中断更 ...
- flume学习(四):Flume Interceptors的使用
转载:http://blog.csdn.net/xiao_jun_0820/article/details/38111305 对于flume拦截器,我的理解是:在app(应用程序日志)和 source ...
- 移动web之响应式布局
1.响应式布局的概念 响应式布局是Ethan Marcotte在2010年5月份提出的一个概念.简而言之.就是一个站点可以兼容多个终端--而不是为每一个终端做一个特定的版本号. 这个概念是为解决移动互 ...
- golang一些知识点
2.冒泡排序(二维数组使用): func main() { i := 1 MYLABEL: for { i++ if i > 3 { break MYLABEL } } fmt.Println( ...
- 解决log4j.xml问题http//jakarta.apache.org/log4j/ uri is not registered
在Eclipse中,配置log4j.xml出现"http //jakarta.apache.org/log4j/ uri is not registered"的错误信息. 原始的l ...
- springMVC 前后台日期格式传值解决方式之一(共二) @DateTimeFormat的使用和配置
无意中发现对于时间字符串转Date类,根本不用自己去写转换类,spring mvc已经实现了该功能,还是基于注解的,轻松省事,使用 org.springframework.format.support ...
- JDBC技术总结(二)
上一节主要讲了JDBC的基本操作,这一节主要总结一下JDBC如何处理大文本.如何处理图片以及进行批处理. 1.JDBC处理大文本 在MySQL中,大文本是text类型,使用Java操作数据库中的大文本 ...
- Struts2学习之拦截器
© 版权声明:本文为博主原创文章,转载请注明出处 拦截器: - Struts2大多数核心功能都是通过拦截器实现的,每个拦截器完成某项功能 - 拦截器方法在Action执行之前或之后执行 工作原理: - ...
- iWatch开发:UI 组件说明
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hlbmppZTEyMzQ1Njc4/font/5a6L5L2T/fontsize/400/fill/I0 ...