技术方案

将Android平台的GB28181客户端记录仪与应急指挥调度系统对接,可以实现实时视频监控和数据传输,以协助应急指挥中心对工地的监控和调度。具体的对接方法如下:

1. 配置GB28181设备接入端记录仪:在Android设备上安装和配置GB28181客户端记录仪,确保其连接到智慧工地国标服务平台,实现现场设备和GB28181平台侧的通信连接。

2. 集成与调度系统接口:通过与应急指挥调度系统的接口,将GB28181客户端记录仪和调度系统进行集成。根据调度系统的接口规范,进行相关配置和设置,确保客户端记录仪能够与调度系统进行通信。

3. 实时视频监控:通过GB28181客户端记录仪,将智慧工地的实时监控视频传输到调度系统中。在调度系统的界面中,显示摄像头的实时监控画面,并提供对视频流的控制,如实时预览、语音广播、实时位置更新查看等。

4. 录像存储与回放:通过GB28181客户端记录仪,实现设备按需录像功能。调度系统可以通过接口调用,实现对历史视音频文件的下载和回放。例如,在应急调度中心需要回放某个时间段内的监控记录时,可以通过调度系统的界面进行选择和播放。

5. 报警与事件处理:GB28181客户端记录仪可以与调度系统进行报警和事件的通信。当监控设备发生报警时,客户端记录仪可以向调度系统发送报警信息,以便调度员及时响应和处理。

通过与应急指挥调度系统的对接,Android平台上的GB28181记录仪可以提供实时视频监控和数据传输的功能,帮助应急指挥中心对工地进行监控和调度。这样可以提高应急响应的效率和准确性,确保工地安全和管理的有效性。

技术实现

本文以大牛直播SDK的Android平台GB28181设备接入模块为例,想说配置SIP服务器:设定GB28181设备需要连接的SIP服务器地址、端口、用户凭证等信息。

Android平台GB28181记录仪如何对接应急指挥调度系统?_GB28181平台

GBSIPAgent     gb28181_agent_             = null;
    private int    gb28181_sip_local_port_base_  = 5060;
    private String gb28181_sip_server_id_     = "34020000002000000001";
    private String gb28181_sip_domain_        =  "3402000000";
    private String gb28181_sip_server_addr_   = "192.168.0.108";
    private int    gb28181_sip_server_port_   = 15060;

    private String gb28181_sip_user_agent_filed_  = null; // "NT GB UserAgent V1.7";
    private String gb28181_sip_username_   = "34020000011310000039";
    private String gb28181_sip_password_   = "12345678";

    private int gb28181_reg_expired_           = 3600; // 注册有效期时间最小3600秒
    private int gb28181_heartbeat_interval_    = 20; // 心跳间隔GB28181默认是60, 目前调整到20秒
    private int gb28181_heartbeat_count_       = 3; // 心跳间隔3次失败,表示和服务器断开了
    private int gb28181_sip_trans_protocol_    = 0; // 0表示信令用UDP传输, 1表示信令用TCP传输

1. 注册设备:通过SIP协议实现设备的注册,将设备注册到SIP服务器上。

@Override
    public void ntsRegisterOK(String dateString) {
        Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));
    }

    @Override
    public void ntsRegisterTimeout() {
        Log.e(TAG, "ntsRegisterTimeout");
    }

    @Override
    public void ntsRegisterTransportError(String errorInfo) {
        Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));
    }

2. 响应呼叫:当有呼叫请求时,通过SIP协议接收呼叫请求,并进行相应的处理(如接听、拒绝等)。

@Override
    public void ntsOnInvitePlay(String deviceId, SessionDescription session_des) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 先振铃响应下
                gb28181_agent_.respondPlayInvite(180, device_id_);

                MediaSessionDescription video_des = null;
                SDPRtpMapAttribute ps_rtpmap_attr = null;

                // 28181 视频使用PS打包
                Vector<MediaSessionDescription> video_des_list = session_des_.getVideoPSDescriptions();
                if (video_des_list != null && !video_des_list.isEmpty()) {
                    for(MediaSessionDescription m : video_des_list) {
                        if (m != null && m.isValidAddressType() && m.isHasAddress() ) {
                            video_des = m;
                            ps_rtpmap_attr = video_des.getPSRtpMapAttribute();
                            break;
                        }
                    }
                }

                if (null == video_des) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay get video description is null, response 488, device_id:" + device_id_);
                    return;
                }

                if (null == ps_rtpmap_attr) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay get ps rtp map attribute is null, response 488, device_id:" + device_id_);
                    return;
                }

                Log.i(TAG,"ntsOnInvitePlay, device_id:" +device_id_+", is_tcp:" + video_des.isRTPOverTCP()
                        + " rtp_port:" + video_des.getPort() + " ssrc:" + video_des.getSSRC()
                        + " address_type:" + video_des.getAddressType() + " address:" + video_des.getAddress());

                long rtp_sender_handle = libPublisher.CreateRTPSender(0);
                if ( rtp_sender_handle == 0 ) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay CreateRTPSender failed, response 488, device_id:" + device_id_);
                    return;
                }

                gb28181_rtp_payload_type_  = ps_rtpmap_attr.getPayloadType();
                gb28181_rtp_encoding_name_ =  ps_rtpmap_attr.getEncodingName();

                ...

                if (!gb28181_agent_.respondPlayInviteOK(device_id_,local_video_des) ) {
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    Log.e(TAG, "ntsOnInvitePlay call respondPlayInviteOK failed.");
                    return;
                }

                gb28181_rtp_sender_handle_ = rtp_sender_handle;
            }

            private String device_id_;
            private SessionDescription session_des_;

            public Runnable set(String device_id, SessionDescription session_des) {
                this.device_id_ = device_id;
                this.session_des_ = session_des;
                return this;
            }
        }.set(deviceId, session_des),0);
    }

    @Override
    public void ntsOnCancelPlay(String deviceId) {
        // 这里取消Play会话
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "ntsOnCancelPlay, deviceId=" + device_id_);

                destoryRTPSender();
            }

            private String device_id_;

            public Runnable set(String device_id) {
                this.device_id_ = device_id;
                return this;
            }

        }.set(deviceId),0);
    }

3. 视频流传输:通过SIP协议实现GB28181设备之间的视频流传输,使用相关的音视频编解码技术将视频数据进行传输。

@Override
    public void ntsOnAckPlay(String deviceId) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);

                if (!isRTSPPublisherRunning && !isPushingRtmp && !isRecording) {
                    InitAndSetConfig();
                }

                libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_, gb28181_rtp_encoding_name_);

                //libPublisher.SetGBTCPConnectTimeout(publisherHandle, 10*60*1000);
                //libPublisher.SetGBInitialTCPReconnectInterval(publisherHandle, 1000);
                //libPublisher.SetGBInitialTCPMaxReconnectAttempts(publisherHandle, 3);

                int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);
                if (startRet != 0) {

                    if (!isRTSPPublisherRunning && !isPushingRtmp  && !isRecording) {
                        if (publisherHandle != 0) {
                            long handle = publisherHandle;
                            publisherHandle = 0;
                            libPublisher.SmartPublisherClose(handle);
                        }
                    }

                    destoryRTPSender();

                    Log.e(TAG, "Failed to start GB28181 service..");
                    return;
                }

                if (!isRTSPPublisherRunning && !isPushingRtmp && !isRecording) {
                    CheckInitAudioRecorder();
                }

                startLayerPostThread();
                isGB28181StreamRunning = true;
            }

            private String device_id_;

            public Runnable set(String device_id) {
                this.device_id_ = device_id;
                return this;
            }

        }.set(deviceId),0);
    }

4. 语音广播或语音对讲:通过SIP协议实现设备之间的语音对讲功能,使得设备之间可以进行双向的语音通话。

@Override
    public void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "ntsOnAudioBroadcastPlay, fromFromUserName:" + command_from_user_name_
                        + " FromUserNameAtDomain:" + command_from_user_name_at_domain_
                        + " sourceID:" + source_id_ + ", targetID:" + target_id_);

                stopAudioPlayer();
                destoryRTPReceiver();

                if (gb28181_agent_ != null ) {
                    String local_ip_addr = IPAddrUtils.getIpAddress(context_);

                    boolean is_tcp = true; // 考虑到跨网段, 默认用TCP传输rtp包
                    rtp_receiver_handle_ = lib_player_.CreateRTPReceiver(0);
                    if (rtp_receiver_handle_ != 0 ) {
                        lib_player_.SetRTPReceiverTransportProtocol(rtp_receiver_handle_, is_tcp?1:0);
                        lib_player_.SetRTPReceiverIPAddressType(rtp_receiver_handle_, 0);

                        if (0 == lib_player_.CreateRTPReceiverSession(rtp_receiver_handle_, 0) ) {
                            int local_port = lib_player_.GetRTPReceiverLocalPort(rtp_receiver_handle_);
                            boolean ret = gb28181_agent_.inviteAudioBroadcast(command_from_user_name_,command_from_user_name_at_domain_,
                                    source_id_, target_id_, "IP4", local_ip_addr, local_port, is_tcp?"TCP/RTP/AVP":"RTP/AVP");

                            if (!ret ) {
                                destoryRTPReceiver();
                                btnGB28181AudioBroadcast.setText("GB28181语音广播");
                            }
                            else {
                                btnGB28181AudioBroadcast.setText("GB28181语音广播呼叫中");
                            }
                        } else {
                            destoryRTPReceiver();
                            btnGB28181AudioBroadcast.setText("GB28181语音广播");
                        }
                    }
                }
            }

            private String command_from_user_name_;
            private String command_from_user_name_at_domain_;
            private String source_id_;
            private String target_id_;

            public Runnable set(String command_from_user_name, String command_from_user_name_at_domain, String source_id, String target_id) {
                this.command_from_user_name_ = command_from_user_name;
                this.command_from_user_name_at_domain_ = command_from_user_name_at_domain;
                this.source_id_ = source_id;
                this.target_id_ = target_id;
                return this;
            }

        }.set(commandFromUserName, commandFromUserNameAtDomain, sourceID, targetID),0);
    }

5. 视音频录制与历史视音频下载回放:实现对视频流的录制和下载回放功能,可以将实时视频数据进行录制保存,并可以进行下载、回放操作。

信令接口设计:

/**
* Author: daniusdk.com
*/
package com.gb.ntsignalling;
 
public interface GBSIPAgent {
    void addDownloadListener(GBSIPAgentDownloadListener downloadListener);
 
    void removeDownloadListener(GBSIPAgentDownloadListener removeListener);
 
    /*
    *响应Invite Download 200 OK
    */
    boolean respondDownloadInviteOK(long id, String deviceId, String startTime, String stopTime, MediaSessionDescription localMediaDescription);
 
    /*
    *响应Invite Download 其他状态码
    */
    boolean respondDownloadInvite(int statusCode, long id, String deviceId, String startTime, String stopTime);
 
    /*
    * 媒体流发送者在文件下载结束后发Message消息通知SIP服务器回文件已发送完成
    * notifyType 必须是"121“
     */
    boolean notifyDownloadMediaStatus(long id, String deviceId, String startTime, String stopTime, String notifyType);
 
    /*
     *终止Download会话
     */
    void terminateDownload(long id, String deviceId, String startTime, String stopTime, boolean isSendBYE);
 
    /*
     *终止所有Download会话
     */
    void terminateAllDownloads(boolean isSendBYE);
 
}

历史视音频下载listener设计:

/**
* Author: daniusdk.com
*/
package com.gb.ntsignalling;
 
public interface GBSIPAgentDownloadListener {
    /*
     *收到s=Download的文件下载Invite
     */
    void ntsOnInviteDownload(long id, String deviceId, SessionDescription sessionDescription);
 
    /*
     *发送Download invite response 异常
     */
    void ntsOnDownloadInviteResponseException(long id, String deviceId, String startTime, String stopTime, int statusCode, String errorInfo);
 
    /*
     * 收到CANCEL Download INVITE请求
     */
    void ntsOnCancelDownload(long id, String deviceId, String startTime, String stopTime);
 
    /*
     * 收到Ack
     */
    void ntsOnAckDownload(long id, String deviceId, String startTime, String stopTime);
 
    /*
    * 更改下载速度
     */
    void ntsOnDownloadMANSRTSPScaleCommand(long id, String deviceId, String startTime, String stopTime, double scale);
 
    /*
     * 收到Bye
     */
    void ntsOnByeDownload(long id, String deviceId, String startTime, String stopTime);
 
    /*
     * 不是在收到BYE Message情况下, 终止Download
     */
    void ntsOnTerminateDownload(long id, String deviceId, String startTime, String stopTime);
 
    /*
     * Download会话对应的对话终止, 一般不会触发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发
    收到这个, 请做相关清理处理
    */
    void ntsOnDownloadDialogTerminated(long id, String deviceId, String startTime, String stopTime);
}

感兴趣的开发者可以酌情参考。