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 ...
随机推荐
- Java反射学习总结五(Annotation(注解)-基础篇)
Annotation(注解)简单介绍: 注解大家印象最深刻的可能就是JUnit做单元測试,和各种框架里的使用了. 本文主要简介一下注解的用法,下篇文章再深入的研究. annotation并不直接影响代 ...
- Maven够用就好
学习maven.仅仅要知道pom.dependency.coordination就能够了,剩下的就是学习一个一个的plugin的Goal 配置maven 下载 http://maven.apac ...
- hibernate学习系列-----(1)开发环境搭建
其实一两个月前就在了解hibernate方面的知识了,但一直以来,都没有好好的总结,而且一直使用的是myeclipse,感觉有些傻瓜式的操作就可以搭建起hibernate的开发环境,但这样一点都不好, ...
- 图解HTTP第六章:HTTP首部
学习HTTP首部的结构和首部中各字段的用法. HTTP首部字段 使用首部字段是为了给浏览器和server提供报文主体大小.所使用的语言.认证信息等内容. 首部字段相应单个HTTP首部能够有多个值.假设 ...
- lodash 展平数组 flatten flattenDeep
_.flatten(array) 向上一级展平数组嵌套 <!DOCTYPE html> <html lang="zh"> <head> < ...
- 使用transform和transition制作CSS3动画
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...
- UITextView被键盘遮挡的处理
这个应该是一个通用的任务了吧,键盘弹出来的时候,UITextView(或者UITextField)会被遮挡. 解决的办法就不是很能通用了. 1. 如果有UIScrollView做父view的话只需要滚 ...
- Nginx之红黑树
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #ifndef _NGX_RBTREE_H_INCLUDED_ #de ...
- kafka 小案例【一】---设置但个消息集群
启动kafka服务 [ bin/kafka-server-start.sh config/server.properties ] [root@zhangxs kafka_2.]# bin/kafka- ...
- linux中查找文件并合并文件
find ./src -name '*.txt' -exec cat '{}' \; > test.txt