GB28181中事件订阅和通知机制是基于RFC3265 中的SIP扩展方法SUBSCRIBE和NOTIFY实现的。代码实现之前,先了解下相关协议.

这里先简单说明下RFC3265:

1.SUBSCRIBE请求中应该包含"Expires“头, 快到期的时候,需要在重新发送SUBSCRIBE刷新订阅有效期(有效期单位是秒)。

2.SUBSCRIBE 2xx响应中也必须包含"Expires”,这个值可以比请求中的小,但不能比它大。

3.SUBSCRIBE请求中"Expires“为0表示取消订阅.

4.SUBSCRIBE请求必须包含"Event"头,对于GB28181来说,移动设备位置订阅这个值是"presence", 也可能包含一个"id"参数,这个"id"参数仅在一个Dialog范围内有效(Dialog概念请参考RFC3261), GB28181中域间目录订阅通就有"id“参数,例如:

Event:Catalog;id=3177

移动设备位置订阅:

Event:presence

5.SUBSCRIBE 请求可能会创建Dialog(INVITE也可能创建Dialog), 订阅被Notifier接受的话要响应一个2xx最终响应(SIP中临时响应是1xx), 如果收到非2xx的最终响应,表示没有订阅成功或相应的Dialog已经被创建,并且没有后续的NOTIFY消息被发送。收到489 Bad Event 表示订阅事件不能被Notifier理解。如果SUBSCRIBE 请求是一个刷新请求,收到481响应表示订阅会话已经被终止。还有一点要注意,使用UDP传输SIP消息时,NOTIFY messages可能会比SUBSCRIBE 200 response先到达,实现中要处理好这一点。

6.取消订阅只要"Expires“头设置为0即可,要注意的是取消订阅成功的话,也会发一个最终的NOTIFY message.

7,Notifier会检查SUBSCRIBE请求中"Expires"值是不是太小,当且仅当这个值大于0且小于1小时,并且小于Notifier配置的最小值时,Notifier可能会返回一个"423 Interval too  small"错误,并包含一个""Min-Expires" 头域。

8.发送SUBSCRIBE 2xx响应后,要立即发送一个NOTIFY message.

9.前面已经提过订阅到期之前,需要在同一个Dialog中发送刷新SUBSCRIBE请求,需要包含"Expires"头,值可以和上一次的不一样,如果这个值太小,Notifier应该响应423("423 Subscription Too Brief"), 如果订阅到期之前没有收到刷新请求,这个订阅需要被移除,移除时需要发送一个NOTIFY message,消息要包含"Subscription-State",具体格式如下:

Subscription-State:terminated;reason=timeout

相应的Dialog也要终止.

10.NOTIFY请求也要有"Event"头,这个头必须和对应的SUBSCRIBE请求中的"Event"头匹配,如果有"id"参数的话,"id"参数也要匹配.

11.NOTIFY request 超时的话,notifier应该移除这个订阅.

12.NOTIFY request响应非2xx的话,响应没有"Retry-After"头,并且也没有其他暗示让重新发送request, notifier必须移除这个订阅.

13.NOTIFY request收到一个481响应, notifier必须移除这个订阅.

14.NOTIFY request必须包含"Subscription-State"头,有三个可选的值:"active", "pending", "terminated". 当值是"active"或"pending"时,应该也包含一个”expires“参数,显示订阅剩余时间,例如:

Subscription-State:active;expires=120
Subscription-State:pending;expires=70

15.对于NOTIFY request中 "Subscription-State" 头参数,RFC3265 3.2.4节中有这么一句:

The "retry-after" and "reason" parameters have no semantics for "active".

The "retry-after" and "reason" parameters have no semantics for "pending".

不过我在GB28181事件通知消息示范中(可以看下GB28181-2016 J.21)看到类似如下示例:

Subscription-State:active;expires=xx;retry-after=yy

多了个"retry-after"参数,不过应该不影响实际应用.

16.多个订阅能关联同一个Dialog上, 多个订阅也可以存在于INVITE创建的Dialog,如果订阅销毁后,没有其他应用状态和Dialog关联,那Dialog也终止.反过来也是,这里要注意一点是使用INVITE创建的Dialog不一定会在收到BYE时终止;多个订阅关联一个Dialog,要等到所有关联订阅销毁后,Dialog才终止.

GB28181事件订阅和通知规定简单说明:

1.事件源接受事件订阅,并发通知。事件观察者订阅事件,接受通知。事件源可以是安卓移动设备等。事件观察者可以是SIP服务器等。事件包括移动设备位置通知等事件。

2.观察者发SUBSCRIBE请求,返回200OK 表示订阅成功,按文档说明返回400应该是订阅没成功吧,或者在刷新订阅的时候,返回400表示订阅会话结束. 关于返回失败状态码问题, RFC3265也有讨论,具体实现还是看实际系统吧。

3.SUBSCRIBE 请求消息Content-type:Application/MANSCDP+xml.

4.移动设备位置上报事件的SUBSCRIBE请求消息体采用 MANSCDP协议格式, 注意这个事件的响应消息没有消息体,而报警事件响应消息有消息体的。

5.事件源接受事件订阅后,在事件触发后立即通知观察者(RFC3265是说事件源发送SUBSCRIBE 2xx响应后,要立即发送一个NOTIFY消息,这里我建议立即发送NOTIFY消息,不要等到事件触发后再发送,有一个原因就是订阅者收到NOTIFY请求(如果还没有收到SUBSCRIBE 2xx)就可以创建一个新的Dialog和Subscription).

6.NOTIFY 请求消息Content-type:Application/MANSCDP+xml.

7,移动设备位置通知的NOTIFY 请求消息体采用 MANSCDP协议格式定义,注意响应没有消息体.

8.移动设备位置上报 SUBSCRIBE 请求示例(这个是我补充的,GB28181中没有给出位置上报相关消息示例):

请求头片段:

CSeq:3 SUBSCRIBE
    Expires:600
    Event:presence
    Content-type:Application/MANSCDP+XML

请求体XML:

<?xml version="1.0" encoding="GB2312" ?>
<Query>
<CmdType>MobilePosition</CmdType>
<SN>71339</SN>
<DeviceID>31011500991320000177</DeviceID>
<Interval>5</Interval>
</Query>

9.移动设备位置上报 NOTIFY 请求示例(这个是我补充的,GB28181中没有给出位置上报相关消息示例):

请求头片段:

CSeq:71 NOTIFY
    Subscription-State:active;expires=301
    Event:presence
    Content-type:Application/MANSCDP+XML

请求体XML:

<?xml version="1.0" encoding="GB2312" ?>
<Notify>
<CmdType>MobilePosition</CmdType>
<SN>71339</SN>
<TargetID>31011500991320000177</TargetID>
<Time>2022-03-08T11:21:39</Time>
<Longitude>143.507222</Longitude>
<Latitude>33.99011311</Latitude>
</Notify>

10. GB28181 A.2.5-e 中定义:

<! --海拔高度,单位:m(可选)-->
<element name="Altitude"  type="tg:deviceIDType" />

type可能是"double",  定义可能是:

<element name="Altitude"  type="double" />

具体以实际系统为准吧.

GB28181位置上报订阅通知相关说明到此为止,接下来是具体代码实现了,毕竟文字说明并不适合描述代码细节,代码才是干货.

移动位置订阅,我安卓上的实现接口如下:

public class DevicePosition {private String mTime; // 产生位置信息的时间,格式如:2022-03-16T10:37:21, yyyy-MM-dd'T'HH:mm:ssprivate String mLongitude; // 经度private String mLatitude; //纬度private String mSpeed; // 速度,单位:km/hprivate String mDirection; // 方向,取值为当前摄像头方向与正北方的顺时针夹角,取值范围0°~360°,单位:(°)private String mAltitude; // 海拔高度,单位:mpublic String getTime() {return mTime;}public void setTime(String time) {this.mTime = time;}public String getLongitude() {return mLongitude;}public void setLongitude(double longitude) {this.mLongitude = String.valueOf(longitude);}public void setLongitude(String longitude) { this.mLongitude =longitude; }public String getLatitude() {return mLatitude;}public void setLatitude(double latitude) {this.mLatitude = String.valueOf(latitude);}public void setLatitude(String latitude) { this.mLatitude = latitude;}public String getSpeed() {return mSpeed;}public void setSpeed(double speed) {this.mSpeed = String.valueOf(speed);}public String getDirection() {return mDirection;}public void setDirection(double direction) {this.mDirection = String.valueOf(direction);}public String getAltitude() {return mAltitude;}public void setAltitude(double altitude) {this.mAltitude = String.valueOf(altitude);}
}public interface GBSIPAgent {// 其他相关接口// ........../**更新设备位置信息*/boolean updateDevicePosition(String deviceId, DevicePosition position);
}public interface GBSIPAgentListener
{// 其他相关接口// ........../** 设备位置请求, 这个主要用在移动设备位置订阅上* @param interval 请求间隔, 单位是毫秒*/void ntsOnDevicePositionRequest(String deviceId, int interval);}

demo 代码如下:

private void addTestDevice() {
com.gb28181.ntsignalling.Device gb_device = new com.gb28181.ntsignalling.Device("34020000001380000037", "某安卓设备", Build.MANUFACTURER, Build.MODEL,"宇宙","火星1","火星", true);if (mLongitude != null && mLatitude != null) {com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();device_pos.setTime(mLocationTime);device_pos.setLongitude(mLongitude);device_pos.setLatitude(mLatitude);gb_device.setPosition(device_pos);gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报}gb28181_agent_.addDevice(gb_device);
}@Overridepublic void ntsOnDevicePositionRequest(String deviceId, int interval) {handler.postDelayed(new Runnable() {@Overridepublic void run() {getLocation(myContext);Log.i(TAG, "ntsOnDevicePositionRequest, deviceId:" + this.device_id_ + ", Longitude:" + mLongitude + ", Latitude:" + mLatitude + ", Time:" + mLocationTime);if (mLongitude != null && mLatitude != null) {com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();device_pos.setTime(mLocationTime);device_pos.setLongitude(mLongitude);device_pos.setLatitude(mLatitude);if (gb28181_agent_ != null ) {gb28181_agent_.updateDevicePosition(device_id_, device_pos);}}}private String device_id_;private int interval_;public Runnable set(String device_id, int interval) {this.device_id_ = device_id;this.interval_ = interval;return this;}}.set(deviceId, interval),0);}

从协议到代码实现,还是要花些精力的, 更多问题可以联系qq: 1130758427,   github

GB28181 安卓移动设备位置上报实现(订阅和通知实现)相关推荐

  1. LiveGBS国标视频流媒体平台GB/T28181针对没有位置上报的设备如何自定义位置经度纬度信息电子地图标注

    GB28181针对没有位置上报的设备如何自定义位置经度纬度信息 1.关于位置订阅 2.自定义位置信息 2.1.国标设备->查看通道->位置 2.2.点击该行的位置字段 3.搭建GB2818 ...

  2. 【安信可NB-IoT模组EC系列AT指令应用笔记④】将设备位置接入阿里云地图,实现空间数据可视化

    一. 前言 安信可EC-01G模块内置了GPS定位芯片,可以将定位信息导入到云阿里平台,实现空间数据的可视化. 更多资料请见阿里云官方文档:https://help.aliyun.com/docume ...

  3. android检测设备信息,安卓手机设备信息检测app

    安卓手机设备信息检测app是一款能够帮助用户快速检测自己的手机设备的硬件以及相关系统的app,这款app能够在最快的时间内检测出手机的所有硬件信息与手机系统的版本信息吗,让用户瞬间了解到自己的手机信息 ...

  4. UWP Windows10开发获取设备位置(经纬度)

    UWP Windows10开发获取设备位置(经纬度) 原文:UWP Windows10开发获取设备位置(经纬度) 1.首先要在UWP项目的Package.appxmanifest文件中配置位置权限,如 ...

  5. android定位地点的保存,Android获得所有存储设备位置的最佳方法

    本方式可以获得内部存储设备地址.SD卡地址.USB设备地址,兼容性能达到99%(别问我为什么这么保证,因为是借鉴了Android设置->存储页面的源码). 由于调用了几个被@hide的方法,所以 ...

  6. 如何获取安卓手机设备名称(包括用户自定义名称)

    获取安卓手机设备名称 最近公司有个需求,需要获取安卓手机的设备名称 搜了一大波资料后发现大多数获取设备名称的方法都是通过android.os.Build这个类拿到的,但是这种方法只能拿到手机型号啊,不 ...

  7. 电脑位置,Windows10系统查找电脑设备位置的方法介绍

    Win10 TH2正式版中新增了一个查看电脑设备位置的功能.不过很多用户至今都不知道该如何使用该功能.下面,小编就大家分享Win10下查找电脑设备位置的具体操作步骤. 具体方法如下: 1.首先,请大家 ...

  8. 百度地图 android 自身地点,Android使用百度地图SDK获得当前设备位置所在的省、市(系列1)...

    百度地图开放平台地址:http://developer.baidu.com/map/ 下载Android定位SDK: java代码: package com.example.baidumap; imp ...

  9. linux挂载安卓手机,安卓USB设备U盘挂载工具(StickMount Pro)

    安卓USB设备挂载软件(StickMount Pro) 能在多台设备上通过OTG功能自动挂载和卸载USB大容量存储设备,例如Galaxy Nexus.Nexus 7和Nexus 10.当您使用OTG数 ...

最新文章

  1. mysql加索引优化sql_MySQL添加索引优化SQL
  2. 审车按月还是日期_新手都该知道的审车流程!
  3. 太强了! 李宏毅:1 天搞懂深度学习,我总结了 300 页 PPT
  4. if-else多级嵌套,输入3/4/5个数寻找最大值(太多了容易乱!!!)
  5. 玩 High API 系列之:智能云相册
  6. ajax提交加载loading图标遮罩层不显示
  7. Python3.x的print()输出问题
  8. 图书管理系统实验报告-面向对象的分析与设计
  9. 斐讯K2路由器,版本号V22.6.507.43(最新)刷华硕固件简明教程(附所有工具包)
  10. 帝国CMS7.5仿可可礼物网漂亮大气淘宝客网站源码 带手机版+火车头采集
  11. .netcore3 下Signalr 关于Joson序列化后对象属性变小写的问题
  12. 去中心化身份 DID( Decentralized Identifiers)
  13. matplotlib.pyplot 标记出曲线上最大点和最小点的位置
  14. wamp phpMyAdmin error #1045 - Access denied for user root@locahost Fixed!
  15. i2c的IOL及上拉电阻
  16. Java面向可复用性和可维护性的设计模式
  17. 减小PDF文档大小(转载)
  18. S3C2440 开发板实战(9):poll机制
  19. Ubuntu 安装出现 error vmlinuz has invalid signature 【或者】 mmx64.efi not found
  20. Spark深入解析(十七):SparkCore之RDD编程进阶

热门文章

  1. URLDownloadToFile调用返回E_ABOR问题
  2. Point Cloud Labeling Tool使用说明
  3. PWM与电压转换芯片:APCPAC芯片
  4. html网页 swf播放器使用代码
  5. BZOJ1066【SCOI2007】蜥蜴 网络流
  6. 关于股息、增发、回购的个人看法
  7. 初探Java设计模式2:结构型模式(代理模式,适配器模式等)
  8. 音视频技术开发周刊 | 232
  9. 你的电脑里还在装着360杀毒软件吗?最好用的,免费的都在这里,给你的电脑换个好的杀软~!...
  10. 使用Eclipse开发Spring的第一个简单程序