规范解读

GB28181-2022相对2016,增加了设备软件升级、图像抓拍信令流程和协议接口。我们先回顾下规范说明:

图像抓拍基本要求

源设备向目标设备发送图像抓拍配置命令,携带传输路径、会话ID等信息。目标设备完成图像传输后,发送图像抓拍传输完成通知命令,采用IETF RFC 3428中的MESSAGE方法实现,命令流程见9.14.2。图像文件命名规则宜采用“设备编码(20位)、图像编码(2位)、时间编码(17位)、序列码(2位)”的形式,抓拍图像文件命名规则应符合表4的要求。图像格式宜使用JPEG,图像分辨率宜采用与主码流相同的分辨率

GB/T28181-2022图像抓拍规范解读及技术实现_GB28181快照

命令流程

GB/T28181-2022图像抓拍规范解读及技术实现_GB28181推流_02

命令流程描述如下:

a)l:源设备向SIP服务器发送图像抓拍配置命令,该命令采用MESSAGE方法携带;

b)2:SIP服务器收到命令后回复200 OK响应﹔

c)3:SIP服务器向目标设备发送图像抓拍配置命令,该命令采用MESSAGE方法携带;

d)4:目标设备收到消息3后回复200 OK响应﹔

e)5:目标设备向SIP服务器发送图像抓拍配置响应命令﹔

f)6:SIP服务器收到命令后返回200 OK;

g)7:SIP服务器向源设备转发图像抓拍配置响应命令﹔

h)8:源设备收到命令后返回200 OK;

i)9:目标设备完成图像传输后发送Message消息,通知SIP服务器图像传输已完成,消息格式应符合A.2.5.7抓拍图像传输完成通知的要求;

j)10:SIP服务器收到消息9后回复200 OK响应;k)11:SIP服务器向源设备转发消息9;

l)12:源设备收到命令11后回复200 OK。

协议接口

图像抓拍协议接口满足以下要求。

a)MESSAGE消息头Content-type头域为Content-type:Application/MANSCDP+xml。

b)图像抓拍配置命令采用MANSCDP协议格式定义:配置命令消息体采用XML封装﹐消息体元数据序列格式符合A.2.3.2.1和A.2.3.2.12的格式规定。

c)图像传输方式宜采用http。当报警触发图像抓拍时,可将报警信息携带在上传路径中,用于关联抓拍的图像。

d)图像抓拍传输完成通知命令采用MANSCDP协议格式定义:配置命令消息体采用XML,封装,XML消息体格式定义符合A.2.5.1规定的前导声明,消息体元数据序列格式符合A.2.5.7的格式规定。其中,SessionID应与图像抓拍配置命令中的SessionID一致。

技术实现

快照分两部分,一部分是按照GB28181的规范要求,平台侧需要快照的时候,本地编码需要的jpeg格式上传到平台侧的图像存储服务器,另外也可以接入端本地发起快照,本地存储。

GB/T28181-2022图像抓拍规范解读及技术实现_大牛直播SDK_03

本文以Android平台GB28181设备接入模块为例,默认,我们采用的是png格式,如果需要,也可以数据回上来,编码保存成协议规范要求的jpeg格式。

//Author: daniusdk.com
class ButtonCaptureImageListener implements View.OnClickListener {
@SuppressLint("SimpleDateFormat")
public void onClick(View v) {
if(isPushingRtmp || isRecording || isRTSPPublisherRunning || isGB28181StreamRunning)
{
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "dn_" + timeStamp; //创建以时间命名的文件名称

String imagePath = imageSavePath + "/" + imageFileName + ".png";

Log.i(TAG, "imagePath:" + imagePath);

libPublisher.SmartPublisherSaveCurImage(publisherHandle, imagePath);
}
else
{
Log.e(TAG, "快照失败,请确保在推送、录像或内置RTSP服务发布状态..");
}
}
}

快照成功后,会有相应的Event回调上来,对应的事件EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:

class EventHandePublisherV2 implements NTSmartEventCallbackV2 {
@Override
public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {

Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);

String publisher_event = "";

switch (id) {
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
publisher_event = "开始..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
publisher_event = "连接中..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
publisher_event = "连接失败..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
publisher_event = "连接成功..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
publisher_event = "连接断开..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
publisher_event = "关闭..";
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
publisher_event = "开始一个新的录像文件 : " + param3;
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
publisher_event = "已生成一个录像文件 : " + param3;
break;

case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
publisher_event = "发送时延: " + param1 + " 帧数:" + param2;
break;

case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
publisher_event = "快照: " + param1 + " 路径:" + param3;

if (param1 == 0) {
publisher_event = publisher_event + "截取快照成功..";
} else {
publisher_event = publisher_event + "截取快照失败..";
}
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
publisher_event = "RTSP服务URL: " + param3;
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:
publisher_event ="RTSP status code received, codeID: " + param1 + ", RTSP URL: " + param3;
break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:
publisher_event ="服务器不支持RTSP推送, 推送的RTSP URL: " + param3;
break;
}

String str = "当前回调状态:" + publisher_event;

Log.i(TAG, str);

Message message = new Message();
message.what = PUBLISHER_EVENT_MSG;
message.obj = publisher_event;
handler_.sendMessage(message);
}
}