Qt/C++开发监控GB28181系统/云台控制/获取预置位信息/添加删除调用预置位
一、前言
之前用onvif已经完美实现了设备的云台控制和预置位的功能,这个基础功能在监控系统中是使用频率很高的,所有gb28181肯定也提供了这样的功能,很多人以为是通过包含xml数据,对应节点指定对应的动作来实现,其实不是的,是类似于早期模拟设备的云台的串口协议中的控制指令,16进制格式的数据,一个个字节表示对应的含义,这个在国标文档中写的非常的详细,按照那个规则来肯定错不了,上下左右移动有个字节位是固定的数据,所以程序这边只需要根据要操作的动作填充对应位的数据即可。
和云台控制一起的还有镜头放大缩小、光圈放大缩小、镜头聚焦和放焦,这些也都是一样的规则,奇怪的是,预置位的添加删除修改调用也都是这个规则,之前还以为是单独的xml格式的数据或者指令。不过遗憾的是预置位的信息并不支持名称的修改,而onvif那边是可以的,有时候希望给预置位改成一个中文的名字方便调用查阅等,不知道后续的国标协议会不会增加进去,个人觉得还是要支持比较好。
//云台控制:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK801c1bb8b2234e2e94be74f0087e28f5
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1022
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: 6822f48163704effb5b58d7031eba9f4
CSeq: 23 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 251
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Control><CmdType>DeviceControl</CmdType><SN>10022</SN><DeviceID>34020000001310000001</DeviceID><PTZCmd>A50F01029600004D</PTZCmd><Info><ControlPriority>5</ControlPriority></Info>
</Control>//控制应答:设备端-》服务端
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK801c1bb8b2234e2e94be74f0087e28f5
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1022
To: <sip:34020000001320000002@192.168.0.64:5060>;tag=1060605518
Call-ID: 6822f48163704effb5b58d7031eba9f4
CSeq: 23 MESSAGE
User-Agent: IP Camera
Content-Length: 0//停止云台:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK3cb5f960d94549888269c12f4ed29c9e
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1023
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: f3124084767d48909decda7579e3f96b
CSeq: 24 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 251
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Control><CmdType>DeviceControl</CmdType><SN>10023</SN><DeviceID>34020000001310000001</DeviceID><PTZCmd>A50F0100000000B5</PTZCmd><Info><ControlPriority>5</ControlPriority></Info>
</Control>//停止应答:设备端-》服务端
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK3cb5f960d94549888269c12f4ed29c9e
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1023
To: <sip:34020000001320000002@192.168.0.64:5060>;tag=1071996623
Call-ID: f3124084767d48909decda7579e3f96b
CSeq: 24 MESSAGE
User-Agent: IP Camera
Content-Length: 0//查询预置位:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK5f11d64dadd7466cb06011e8971b788d
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1024
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: aac3afc576894717a72ed58030479bcb
CSeq: 25 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 155
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Query><CmdType>PresetQuery</CmdType><SN>10024</SN><DeviceID>34020000001310000001</DeviceID>
</Query>//返回预置位:设备端-》服务端
MESSAGE sip:34020000002000000001@3402000000 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.64:5060;rport;branch=z9hG4bK1149716366
From: <sip:34020000001320000002@3402000000>;tag=2001348774
To: <sip:34020000002000000001@3402000000>
Call-ID: 360379917
CSeq: 20 MESSAGE
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: IP Camera
Content-Length: 258<?xml version="1.0" encoding="GB2312"?>
<Response>
<CmdType>PresetQuery</CmdType>
<SN>10024</SN>
<DeviceID>34020000001310000001</DeviceID>
<PresetList Num="1">
<Item>
<PresetID>1</PresetID>
<PresetName>预置点 1</PresetName>
</Item>
</PresetList>
</Response>//添加预置位:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK76caaff468eb43ca9818e6fb38cfd057
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1025
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: a01a7be3ba6b4214ac0755d92d0096c5
CSeq: 26 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 251
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Control><CmdType>DeviceControl</CmdType><SN>10025</SN><DeviceID>34020000001310000001</DeviceID><PTZCmd>A50F018100020038</PTZCmd><Info><ControlPriority>5</ControlPriority></Info>
</Control>//删除预置位:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK9259fa44edce4797a69c4c58aa408d2c
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1027
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: 1bc5c4f7629b436ca66865c112ba2d47
CSeq: 28 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 251
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Control><CmdType>DeviceControl</CmdType><SN>10027</SN><DeviceID>34020000001310000001</DeviceID><PTZCmd>A50F01830002003A</PTZCmd><Info><ControlPriority>5</ControlPriority></Info>
</Control>//修改预置位:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bKb968612c929f488bb3f105b907a61eba
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1031
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: 37bfa9cea6564707af579159d662db4a
CSeq: 32 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 251
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Control><CmdType>DeviceControl</CmdType><SN>10031</SN><DeviceID>34020000001310000001</DeviceID><PTZCmd>A50F018100020038</PTZCmd><Info><ControlPriority>5</ControlPriority></Info>
</Control>//调用预置位:服务端-》设备端
MESSAGE sip:34020000001320000002@192.168.0.64:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.110:15060;branch=z3hG9bK3a2b16dffa134a25ad7158ef07ad8b07
From: <sip:34020000002000000001@192.168.0.110:15060>;tag=1032
To: <sip:34020000001320000002@192.168.0.64:5060>
Call-ID: 22431fe05dec47cbb789617b38caef58
CSeq: 33 MESSAGE
User-Agent: wx_feiyangqingyun
Max-Forwards: 70
Content-Length: 251
Content-Type: application/MANSCDP+xml
Contact: <sip:34020000002000000001@192.168.0.110:15060><?xml version="1.0" encoding="GB2312"?>
<Control><CmdType>DeviceControl</CmdType><SN>10032</SN><DeviceID>34020000001310000001</DeviceID><PTZCmd>A50F018200020039</PTZCmd><Info><ControlPriority>5</ControlPriority></Info>
</Control>
二、效果图
三、相关地址
- 国内站点:https://gitee.com/feiyangqingyun
- 国际站点:https://github.com/feiyangqingyun
- 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 文件地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_gb28181。
四、功能特点
- 支持设备注册、注销、心跳、校时、注册认证、注销认证等。
- 设备上线后可以手动获取设备状态、设备信息、配置信息、预置位信息等。
- 设备上线后自动获取设备通道信息,包括中文通道名称。识别到通道上线离线变化,会重新获取该设备的所有通道信息。
- 支持视频点播,可以分别点播主码流和子码流,内置rtp解包线程,解包后发给视频播放组件解码播放。
- 每个设备每个通道支持点播多个视频,通过ssrc区分,支持共用端口和不同端口收流。
- 支持对某个设备下面所有通道、某个通道、某个通道对应的某个流分别关闭。
- 支持录像文件查询和回放,回放控制支持暂停播放、继续播放、倍速播放、切换播放进度。
- 支持录像文件下载,支持倍速比如8倍速下载,可同时多线程批量下载。
- 回放和下载同时支持IPC和NVR,比如摄像头自带的SD存储卡录像文件回放,NVR上的硬盘录像文件回放。
- 支持云台控制,向上、向下、向左、向右、左上、右上、左下、右下方位移动,镜头放大缩小,光圈放大缩小,镜头聚焦放焦。
- 支持预置位信息的查询、调用、添加、修改、删除等操作。
- 自动目录订阅功能,通道上线下线都有对应的信号通知。
- 内置定时读取通道信息机制,以保证通道信息是最新的,比如有些NVR是不断更新的通道信息。
- 支持警情订阅,各种警情事件比如运动目标检测报警、入侵检测报警、徘徊检测报警等自动上报。
- 国标服务同时支持udp和tcp方式,可选只监听一种或者两种都监听,tcp方式自动处理粘包问题。
- 国标拉流同时支持udp、tcp被动、tcp主动三种方式,每个通道都可以自由选择何种拉流方式。
- 内置拉流端口池,每次拉流从中取出一个,关闭流自动回收端口号,重复利用。
- 收流端口自动纠错,自动跳过被占用的端口,不会出现端口占用导致收流失败的情况。
- 视频播放自适应硬解码,极低资源占用,实时性极好,带悬浮条显示视频流信息,可以直接在悬浮条单击按钮保存录像文件到本地。
- 支持几千路国标消息交互并发,实时视频流支持64路同时显示,可以拓展更多路数。
- 支持阿里云等云服务器,可以分别设置内网监听地址和外网访问地址,一般云服务器上是监听地址用内网,对外访问用外网地址。
- 支持视频分发,也就是推流,视频通道打开后可以自动推流到流媒体服务器,其他需要的地方拉流即可,支持rtsp、rtmp、hls、webrtc等方式拉流。
- 同时支持gb28181-2011、gb28181-2016、gb28181-2022以及后续可能的所有协议版本。
- SIP解析和交互采用纯Qt底层代码实现,udp/tcp通信交互,祖传原创代码解析,不依赖任何第三方。
- 代码量少,gb28181交互部分共几千行代码,注释详细,接口友好,使用极其简单,提供非常详细的使用示例。
- 支持海康、大华、宇视、华为、天地伟业等所有国标设备。
- 支持所有Qt版本和编译器以及操作系统,包括但不限于win、linux、mac、android、嵌入式linux、树莓派香橙派、国产os等。
五、相关代码
//具体格式规范在gb28181-2016文档的第79页
//预置位和巡航等操作也是属于云台部分/速度参数就对应的预置位编号
QString GB28181Body::getPtzCmd(PtzType ptzType, quint8 ptzSpeed)
{//协议固定数据格式QByteArray data;data.append(0xA5);data.append(0x0F);data.append(0x01);data.append((char)0x00);data.append((char)0x00);data.append((char)0x00);data.append((char)0x00);data.append((char)0x00);//不同动作填充对应数据/下面的与运算是为了防止数据越界if (ptzType == PtzType_Right) {data[3] = 0x01;data[4] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_RightUp) {data[3] = 0x09;data[4] = ptzSpeed & 0xFF;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_Up) {data[3] = 0x08;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_LeftUp) {data[3] = 0x0A;data[4] = ptzSpeed & 0xFF;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_Left) {data[3] = 0x02;data[4] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_LeftDown) {data[3] = 0x06;data[4] = ptzSpeed & 0xFF;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_Down) {data[3] = 0x04;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_RightDown) {data[3] = 0x05;data[4] = ptzSpeed & 0xFF;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_ZoomIn) {data[3] = 0x10;data[6] = (ptzSpeed & 0x0F) << 4;} else if (ptzType == PtzType_ZoomOut) {data[3] = 0x20;data[6] = (ptzSpeed & 0x0F) << 4;} else if (ptzType == PtzType_IrisIn) {data[3] = 0x44;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_IrisOut) {data[3] = 0x48;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_FocusIn) {data[3] = 0x41;data[4] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_FocusOut) {data[3] = 0x42;data[4] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_AddPreset) {data[3] = 0x81;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_SetPreset) {data[3] = 0x82;data[5] = ptzSpeed & 0xFF;} else if (ptzType == PtzType_DelPreset) {data[3] = 0x83;data[5] = ptzSpeed & 0xFF;}//末尾字节是前面字节加起来int end = 0;int count = data.size();for (int i = 0; i < count - 1; ++i) {end += data.at(i);}data[7] = end;return QString(data.toHex()).toUpper();
}