基于GB28181从海康NVR获取目录/点播/回播信令备忘

Published: 2020-04-09

Tags: gb28181

本文总阅读量

概览

之前的测试使用的是海康的IPC,很多实用场景下不会直接修改每个摄像头配置指向UAS服务器,然后发送指令到摄像头获取数据,更多的是通过已有的支持GB281818的平台来获取数据,比如NVR就是一个比摄像头更大的UAC单元,相比IPC,它有更加丰富的功能,比如查询目录,调取历史视频等,本文记录一些跟NVR通信的一些交互流程和注意事项。

先行知识

1,了解 SIP 协议栈。

2,了解 RTP 协议。

3,了解 PS 封装格式。

4,了解 H.264 编码基础。

之前的两篇文章基本涵盖了先行知识点。

1,《基于国标GB/T28181标准从海康摄像头获取PS流》

2,《海康摄像头PS流格式解析(RTP/PS/H264)》

3,《将海康IPC摄像头PS流保存到文件并转换为MP4视频》

测试环境

海康NVR IP: 192.168.37.200

模拟的SIP服务器(UAS):192.168.3.189

发起请求的SIP客户端(UAC):192.168.104.183

先决条件

NVR需要连接到SIP服务器,并与之保持心跳。

示例图例

这个图例是GB28181文档中的图19,获取历史视音频文件回放。

我没有现成的UAS(SIP 服务器),也没有媒体服务器,我的模拟环境是这样的:

首先 SIP 服务器是我模拟的,它的作用就是 NVR 向它注册时候,它进行响应,保持和设备的连接,它的IP是 192.168.3.189,图中没有标注。

取流的过程就是 UAC 向 NVR 发送 INVITE 指令,NVR 与 SIP 进行一些确认,按照 SIP 客户端发送给 NVR 的 SDP 描述,推送流到指定的地址,我这里把流媒体接收者和 SIP 客户端分开来写的,推送过来的PS流用上一篇文章的 Python 代码就可以保存。

以下的示例中交互中用到的ID和地址可以参照这个图来对照。

获取NVR的设备目录

如图,在NVR的配置管理页面,在平台接入处填写了在 192.168.3.189 的 SIP 服务器地址,另外每个摄像头都是有一个通道ID,我拿到这个 32 路的 NVR 时,这个通道号列表为空,我手动为它们按序分配的编码ID。

28181服务 的Tab中,没有启用 NVR 的服务功能,我在白名单里填写了测试设备的IP地址,如果设备通过 SIP 命令发给 NVR 没有得到响应,试试看在白名单添加 SIP客户端的IP地址。

hikvision nvr

1)UAS -> NVR

发送 Message 命令我使用的 Pjsua2,在 SIP 服务端发送的请求,发送到 NVR 的内容如下:

MESSAGE sip:34020000001110000001@192.168.37.200:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.3.189:5060;rport;branch=z9hG4bKPj275f318d-6fca-4f78-bc46-2c926014a051
Max-Forwards: 70
From: <sip:34020000002000000001@192.168.3.189>;tag=ab242847-abe4-4721-92d9-c6273f5b2650
To: <sip:34020000001110000001@192.168.37.200>
Call-ID: c247d7be-e10f-41a0-8129-81af96bea957
CSeq: 9620 MESSAGE
Accept: text/plain, application/im-iscomposing+xml
User-Agent: sip-server
Content-Type: Application/MANSCDP+xml
Content-Length:   153

<?xml version="1.0" encoding="gb2312"?>
<Query>
    <CmdType>Catalog</CmdType>
    <SN>8720</SN>
    <DeviceID>34020000001110000001</DeviceID>
</Query>

2)NVR -> UAS

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.3.189:5060;rport=5060;branch=z9hG4bKPj275f318d-6fca-4f78-bc46-2c926014a051
From: <sip:34020000002000000001@192.168.3.189>;tag=ab242847-abe4-4721-92d9-c6273f5b2650
To: <sip:34020000001110000001@192.168.37.200>;tag=1856403714
Call-ID: c247d7be-e10f-41a0-8129-81af96bea957
CSeq: 9620 MESSAGE
User-Agent: Embedded Net DVR/NVR/DVS
Content-Length: 0

3)NVR -> UAS

MESSAGE sip:34020000002000000001@3402000000 SIP/2.0
Via: SIP/2.0/UDP 192.168.37.200:5060;rport;branch=z9hG4bK1207881404
From: <sip:34020000001110000001@3402000000>;tag=417372488
To: <sip:34020000002000000001@3402000000>
Call-ID: 483086013
CSeq: 20 MESSAGE
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: Embedded Net DVR/NVR/DVS
Content-Length:   898

<?xml version="1.0" encoding="gb2312"?>
<Response>
<CmdType>Catalog</CmdType>
<SN>8720</SN>
<DeviceID>34020000001110000001</DeviceID>
<SumNum>32</SumNum>
<DeviceList Num="2">
<Item>
<DeviceID>34020000001320000001</DeviceID>
<Name>消控室</Name>
<Manufacturer>Manufacturer</Manufacturer>
<Model>Camera</Model>
<Owner>Owner</Owner>
<CivilCode>CivilCode</CivilCode>
<Address>192.168.37.6</Address>
<Parental>0</Parental>
<SafetyWay>0</SafetyWay>
<RegisterWay>1</RegisterWay>
<Secrecy>0</Secrecy>
<Status>ON</Status>
</Item>
<Item>
<DeviceID>34020000001320000002</DeviceID>
<Name>一层电梯前室</Name>
<Manufacturer>Manufacturer</Manufacturer>
<Model>Camera</Model>
<Owner>Owner</Owner>
<CivilCode>CivilCode</CivilCode>
<Address>192.168.37.7</Address>
<Parental>0</Parental>
<SafetyWay>0</SafetyWay>
<RegisterWay>1</RegisterWay>
<Secrecy>0</Secrecy>
<Status>ON</Status>
</Item>
</DeviceList>
</Response>

陆陆续续还会收到很多条数据,32 说明了总共有 32 条记录, 表示本包中共携带了两条记录,如果你也用的 Pjsua2 来编写的服务示例,那么这些 Message 消息可以通过实例的 onInstantMessage 方法来接收到这些数据,因为我 C/C++ 写的不熟,就把数据直接用 UDP 发送出去了,用 Python 进行接收,判断一些,数据收满 32 条记录后,本次响应就算完成了。

PS:终端打印的内容,中文是乱码的,以上数据的中文是用 GB2312 解码后正常显示的内容。

获取实时视音频

1)UAC -> NVR 发起视频请求

INVITE sip:34020000001320000001@192.168.37.200:5060 SIP/2.0 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6bbb 
Max-Forwards: 70 
From: sip:1001@192.168.104.183;tag=a24d282d-2d00-48a1-ac2a-f366a810e080 
To: sip:34020000001320000001@192.168.37.200 
Contact: <sip:1001@192.168.104.183:5060;ob> 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eab07 
CSeq: 12342 INVITE 
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS 
Supported: replaces, 100rel, timer, norefersub 
Session-Expires: 1800 
Min-SE: 90 
User-Agent: sip-server 
Subject: 34020000001320000001:0,1001:0 
Content-Type: application/sdp 
Content-Length:   142 

v=0 
o=1001 0 0 IN IP4 192.168.104.183 
s=Play 
c=IN IP4 192.168.104.183 
t=0 0 
m=video 6000 RTP/AVP 96 
a=recvonly 
a=rtpmap:96 PS/90000 

注意点:

  1. INVITE 后跟的设备ID是摄像头的,IP地址是NVR的,NVR收到请求会自动把指定摄像头的流推过来。
  2. 1001是UAC的ID,这里是随便写的,To 代表消息发送给谁,Contact 代表 To 收到消息后回复给谁。
  3. Subject 头域是必须的,格式为:“视频通道编码ID:通道号,请求方编码ID:通道号”。
  4. SDP 的 6000 为视频流接收者的端口号,c 字段的IP为接收方的IP地址,s 字段为“Play”代表实时点播。

2)NVR -> UAC NVR 响应 “200 OK”

SIP/2.0 200 OK 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport=5060;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6bbb 
From: <sip:1001@192.168.104.183>;tag=a24d282d-2d00-48a1-ac2a-f366a810e080 
To: <sip:34020000001320000001@192.168.37.200>;tag=373898860 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eab07 
CSeq: 12342 INVITE 
Contact: <sip:34020000001320000001@192.168.37.200:5060> 
Content-Type: application/SDP 
User-Agent: Embedded Net DVR/NVR/DVS 
Content-Length:   259 

v=0 
o=34020000001110000001 0 0 IN IP4 192.168.37.200 
s=Network Video Recorder 
c=IN IP4 192.168.37.200 
t=0 0 
m=video 62536 RTP/AVP 96 
a=sendonly 
a=rtpmap:96 PS/90000 
a=username:34020000001110000001 
a=password:12345678 
a=filesize:0 
y=0000000000 
f= 

3)UAC -> NVR 客户端回复 ACK

ACK sip:34020000001320000001@192.168.37.200:5060 SIP/2.0 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj4926c10e-b480-4cb0-a520-fdbf694c5f9e 
Max-Forwards: 70 
From: sip:1001@192.168.104.183;tag=a24d282d-2d00-48a1-ac2a-f366a810e080 
To: sip:34020000001320000001@192.168.37.200;tag=373898860 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eab07 
CSeq: 12343 ACK 
Content-Length:  0 

注意点:

  1. CSeq 域值相比提交时的值增加了1。
  2. To 域的 tag 值要与 200 OK 回复中的 tag 值相同。
  3. Call-ID 域值保持相同。

接下来实时视频流就推送过来了。

4)UAC -> NVR 客户端回复 Bye 停止流

BYE sip:34020000001320000001@192.168.37.200:5060 SIP/2.0 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj4926c10e-b480-4cb0-a520-fdbf694c5f9e 
Max-Forwards: 70 
From: sip:1001@192.168.104.183;tag=a24d282d-2d00-48a1-ac2a-f366a810e080 
To: sip:34020000001320000001@192.168.37.200;tag=373898860 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eab07 
CSeq: 12343 BYE 
Content-Length:  0 

获取历史视音频

1)UAC -> NVR 获取历史视频流

INVITE sip:34020000001320000001@192.168.37.200:5060 SIP/2.0 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6777 
From: <sip:1001@192.168.104.183:5060>;tag=0b4daa23-53db-41da-994c-9d65c13117f8 
To: <sip:34020000001320000001@192.168.37.200:5060> 
Contact: <sip:1001@192.168.104.183:5060;> 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eazzz 
CSeq: 12342 INVITE 
Max-Forwards: 70 
User-Agent: sip-server 
Subject: 34020000001320000001:0,1001:0 
Content-Type: application/sdp 
Content-Length:   259 

v=0 
o=1001 0 0 IN IP4 192.168.104.183 
s=Playback 
u=34020000001320000001:0 
c=IN IP4 192.168.104.183 
t=1586149200 1586149260 
m=video 6000 RTP/AVP 96 98 97 
a=recvonly 
a=rtpmap:96 PS/90000 
a=rtpmap:98 H264/90000 
a=rtpmap:97 MPEG4/90000 
y=1200009999 
f=

注意点:

  1. SDP中的 s 字段填写 Playback,需要有u域字段,这个字段我没找到详实的解释,网上参数有写0的,有写3的。
  2. SDP中的 t 字段表示视频时间段,十位时间戳的起始到结束,“开始时间与结束时间均为要回放或下载的音视频文件录制时间段中的某个时刻”,这个时间段要根据查询历史视频文件后再进行设置。
  3. SDP中的 y 字段解释:“为是兼职整数字符串,表示SSRC值。格式如下:dddddddddd。其中,第1位为历史或实时媒体流的标识位,0为实时,1为历史;第2位至第6位取20位SIP监控域ID之中的4到8位作为域标识,例如“13010000002000000001”中取数字“10000”;第7位至第10位作为域内媒体流标识,是一个与当前域内产生的媒体流SSRC值后4位不重复的四位十进制整数”——摘自GB28181文档。
  4. SDP中的 f 字段解释:“f = v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率”,测试表明,这个字段可以留空,但不能没有。

2)NVR -> UAC "200 OK"

SIP/2.0 200 OK 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport=5060;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6777 
From: <sip:1001@192.168.104.183:5060>;tag=0b4daa23-53db-41da-994c-9d65c13117f8 
To: <sip:34020000001320000001@192.168.37.200:5060>;tag=647620548 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eazzz 
CSeq: 12342 INVITE 
Contact: <sip:34020000001320000001@192.168.37.200:5060> 
Content-Type: application/SDP 
User-Agent: Embedded Net DVR/NVR/DVS 
Content-Length:   286 

v=0 
o=34020000001110000001 0 0 IN IP4 192.168.37.200 
s=Network Video Recorder 
c=IN IP4 192.168.37.200 
t=1586178000 1586178060 
m=video 62548 RTP/AVP 96 
a=sendonly 
a=rtpmap:96 PS/90000 
a=username:34020000001110000001 
a=password:12345678 
a=filesize:1064536832 
y=1200009999 
f= 

3)UAC -> NVR 回复 ACK

ACK sip:34020000001320000001@192.168.37.200:5060 SIP/2.0 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6777 
Max-Forwards: 70 
From: sip:1001@192.168.104.183;tag=0b4daa23-53db-41da-994c-9d65c13117f8 
To: sip:34020000001320000001@192.168.37.200;tag=647620548 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eazzz 
CSeq: 12343 ACK 
Content-Type: application/sdp 
Content-Length:  0 

此时就可以接收到实时历史流了,流格式与实时流是相同类型的

值得注意的是,每次请求的 Via 字段的 branch 需要为不同值,如果是相同值,NVR可能会有缓存,会返回上一次的视频流,导致 SDP 设置的 t 字段不生效。

4)UAC -> NVR 发送 Bye,停止接收流

BYE sip:34020000001320000001@192.168.37.200:5060 SIP/2.0 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6777 
Max-Forwards: 70 
From: sip:1001@192.168.104.183;tag=0b4daa23-53db-41da-994c-9d65c13117f8 
To: sip:34020000001320000001@192.168.37.200;tag=647620548 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eazzz 
CSeq: 12343 BYE 
Content-Length:  0 

5)NVR -> UAC 回复 “200 OK”

SIP/2.0 200 OK 
Via: SIP/2.0/UDP 192.168.104.183:5060;rport=5060;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0b6777 
From: <sip:1001@192.168.104.183>;tag=0b4daa23-53db-41da-994c-9d65c13117f8 
To: <sip:34020000001320000001@192.168.37.200>;tag=647620548 
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eazzz 
CSeq: 12343 BYE 
User-Agent: Embedded Net DVR/NVR/DVS 
Content-Length: 0 

20200426 补充

下载视音频 INVITE 请求

INVITE sip:34020000001320000001@192.168.37.200:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.104.183:5060;rport;branch=z9hG4bKPj952ad841-0e25-454c-a337-97c39b0bdddd
From: <sip:1001@192.168.104.183:5060>;tag=daddccz2
To: <sip:34020000001320000001@192.168.37.200:5060>
Contact: <sip:1001@192.168.104.183:5060;>
Call-ID: 3d23433e-0392-42e0-8d7b-9d3d692eazzz
CSeq: 12342 INVITE
Max-Forwards: 70
User-Agent: sip-server
Subject: 34020000001320000001:0,1001:0
Content-Type: application/sdp
Content-Length:   278

v=0
o=1001 0 0 IN IP4 192.168.104.183
s=Download
u=34020000001320000001:3
c=IN IP4 192.168.104.183
t=1586149200 1586149900
m=video 6000 RTP/AVP 96 98 97
a=recvonly
a=downloadspeed:8
a=rtpmap:96 PS/90000
a=rtpmap:98 H264/90000
a=rtpmap:97 MPEG4/90000
y=1200009999
f=

PS:需要注意下载时间段要在视频文件时间区间范围内,最好先查阅下视频文件列表确定时间范围。

记录完毕,整个交互流程都通过文本进行了记录,如果有人需要 wireshark 的文件记录或哪里有疑问,可给我发送邮件联系。

参考

  1. 《海康摄像头GB28181对接》
  2. 《GB28181目录订阅记录》
  3. 《sip (gb28181)信令交互-视频点播与回播》
  4. 《使用GB28181从海康NVR设备上获取实时视频》
  5. 《28181接入网关查询NVR录像文件》
  6. 《向海康NVR发送带SDP的INVITE消息后,总是收到400 Bad Request错误提示》