博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GB28181平台实现,支持摄像头公网WEB端直播
阅读量:4136 次
发布时间:2019-05-25

本文共 6387 字,大约阅读时间需要 21 分钟。

背景

28181协议全称为GB/T28181《安全防范视频监控联网系统信息传输、交换、控制技术要求》,是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/TC100)归口,公安部一所等多家单位共同起草的一部国家标准(以下简称28181)。

28181协议在全国平安城市、交通、道路等监控中广泛采用,若想做统一的大监控平台,则支持28181协议接入是必不可少的。如今很多客户都是想在之前使用的28181平台的基础上进行拓展。

说明

LiveGBS GB28181流媒体服务器负责将GB28181设备/平台推送的PS流转成ES流,然后进行分发。

同时,LiveGBS 对外提供HTTP API接口,通过接口可以获知流媒体转发服务的运行状态信息,转发会话信息,服务器配置和版本信息等;

LiveGBS GB28181流媒体服务器提供以下功能:

1. 接受和处理GB28181接入服务器的推流请求(如有推流权限验证则调用验证服务器接口);
2. 接受和处理GB28181设备的推流;
3. 实时流媒体处理,PS(TS)转ES;
4. 推送ES流到EasyDSS流媒体服务器;
5. 接受和处理GB28181接入服务器的断开推流请求;
6. 对外提供服务器获取状态、信息,控制等http API接口;

LiveGBS流媒体直播详细流程

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);             });

至此,整个拉流过程已经完成。

获取更多信息

下载:

转载地址:http://zmvvi.baihongyu.com/

你可能感兴趣的文章
第七章 背包问题——完全背包
查看>>
51nod 分类
查看>>
1136 . 欧拉函数
查看>>
面试题:强制类型转换
查看>>
Decorator模式
查看>>
Template模式
查看>>
State模式
查看>>
Observer模式
查看>>
Iterator模式
查看>>
中国最完整的sysctl.conf优化方案
查看>>
高性能服务器设计
查看>>
性能扩展问题要趁早
查看>>
MySQL-数据库、数据表结构操作(SQL)
查看>>
360在线测试--嵌入式软开
查看>>
优化程序性能—《深入理解计算机系统》
查看>>
《演讲的艺术》有感
查看>>
《亲密关系》摘录
查看>>
win7下安装centOS7双系统
查看>>
Linux鸟哥的私房菜—1
查看>>
matlab 批处理图片
查看>>