当前位置: 首页 > article >正文

国网B接口调阅实时视频规范解读和代码示例分析

接口描述

国网B接口调阅实时视频,相关规范写的比较粗略:

调阅实时视频包括信令接口和媒体流接口,采用标准的SIP INVITE+SDP流程,媒体传输使用RTP/RTCP。

SDP 中 RTP Payload 的取值应遵守下面接口参数中的定义:

a) SDP 中的媒体信息,应仅有一个 m 行,用于描述视频格式。

b) 视频数据用 RTP 打包传输时,应考虑每个传输分组不大于 MTU,可采用的技术包括编码器层支持(如 ITU-T H.264 的 multi-slice 技术),或采用 RTP 层的分片机制(如 IETF RFC 3984 定义的 FU-A 技术)。

前端设备收到平台的INVITE请求后根据SDP描述进行媒体协商,协商通过后打开前端系统摄像机设备将获得的媒体流通过媒体通道发送到平台。

会话建立成功后,前端系统在某些特殊情况下可以主动结束当前呼叫。

平台应支持视频流的分发,以降低对前端系统的操作频繁性和节省网络带宽。

调阅实时视频的接口流程

主要功能流程如下:

a) F1:用户发送 INVITE 消息,携带 SDP 内容通过平台转发到前端设备。

b) F2:按照 SIP 要求,如前端系统在 0.5s 内未能处理该请求,则先发送 1xx 临时响应通过平台转发到用户。

c) F3:前端系统接受了调阅请求的操作,则发送携带 SDP 的 200 OK 响应通过平台转发到用户。

d) F4:用户发送 ACK 通过平台转发到前端设备。

e) 视频流从前端系统传输经平台转发到用户。

f) F5:用户结束会话,发送 BYE 消息到通过平台转发到前端系统。

g)F6:前端系统发送确认,将媒体通道拆线。

技术实现

由于国网B接口的invite实现和GB28181的差异不大,之前我们GB28181这块,已经有非常好的积累了。

 启动B接口后,完成平台端的register和PushResourse交互,有些平台注册后,会接着响应Push_Resource request,其他不表,这里主要谈下invte和Ack相关回调处理:

Invite信令如下:

    INVITE sip:1301110005010100001@192.168.0.102:5060 SIP/2.0
    Via: SIP/2.0/UDP 192.168.0.104:15060;branch=z9hG4bK864531896
    From: <sip:000000000000000001@0000000000>;tag=482531896
    To: <sip:1301110005010100001@192.168.0.102:5060>
    Call-ID: 804531783
    CSeq: 6 INVITE
    Content-Type: application/sdp
    Contact: <sip:000000000000000001@192.168.0.104:15060>
    Max-Forwards: 70
    User-Agent: SIPB
    Request-URI: <sip:1301110005010100001@192.168.0.102:5060>
    Content-Length: 152
    
    v=0
    o=- 0 0 IN IP4 192.168.0.104
    s=Play
    c=IN IP4 192.168.0.104
    t=0 0
    m=video 30004 RTP/AVP 100
    a=recvonly
    a=rtpmap:100 H264/90000
    y=0130111000

收到Invite回调处理逻辑如下:

    @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;

                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;
                }

                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_  = 100;
                gb28181_rtp_encoding_name_ =  "PS";

                libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1);
                libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1);
                libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
                libPublisher.SetRTPSenderSSRC(rtp_sender_handle, video_des.getSSRC());
                libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2M
                libPublisher.SetRTPSenderClockRate(rtp_sender_handle, 90000 /*ps_rtpmap_attr.getClockRate()*/);
                libPublisher.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort());

                if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }

                int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);
                if (local_port == 0) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }

                Log.i(TAG,"get local_port:" + local_port);

                String local_ip_addr = IPAddrUtils.getIpAddress(context_);

                MediaSessionDescription local_video_des = new MediaSessionDescription(video_des.getType());


                local_video_des.addFormat(String.valueOf(100));
                local_video_des.addRtpMapAttribute(ps_rtpmap_attr);

                local_video_des.setAddressType(video_des.getAddressType());
                local_video_des.setAddress(local_ip_addr);
                local_video_des.setPort(local_port);

                local_video_des.setTransportProtocol(video_des.getTransportProtocol());
                local_video_des.setSSRC(video_des.getSSRC());

                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);
    }

ack信令如下:

    ACK sip:1301110005010100001@192.168.0.102:5060 SIP/2.0
    Via: SIP/2.0/UDP 192.168.0.104:15060;branch=z9hG4bK991532349
    From: <sip:000000000000000001@0000000000>;tag=482531896
    To: <sip:1301110005010100001@192.168.0.102:5060>;tag=2d6ebb3d
    Call-ID: 804531783
    CSeq: 6 ACK
    Contact: <sip:000000000000000001@192.168.0.104:15060>
    Max-Forwards: 70
    User-Agent: SIPB
    Request-URI: <sip:1301110005010100001@192.168.0.102:5060>
    Content-Length: 0

ack回调代码处理逻辑如下:

    @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_);
                int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);
                if (startRet != 0) {

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

                    destoryRTPSender();

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

                startAudioRecorder();

                startLayerPostThread();
                isGB28181StreamRunning = true;
            }

            private String device_id_;

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

        }.set(deviceId),0);
    }

总结

国网B接口调阅实时视频流程和GB28181流程基本一致,感兴趣的开发者,可以参考相关的规范实现,B接口相对GB28181来说,面更窄,资料也更少,如果产品化,有测试平台的话,还是不难实现的。


http://www.kler.cn/a/9863.html

相关文章:

  • Llama架构及代码详解
  • MQTT协议解析 : 物联网领域的最佳选择
  • docker镜像源,亲测可用,时间2024-11-14
  • 系统上线后发现bug,如何回退版本?已经产生的新业务数据怎么办?
  • 字符及字符串(ASCII编码系统)
  • 【AI大模型】ELMo模型介绍:深度理解语言模型的嵌入艺术
  • 代码随想录_二叉树_leetcode236
  • Java阶段一Day19
  • 值栈的概念和作用是什么?
  • 关于iptables封禁国外IP方法
  • 多模态模型技术综述
  • 第一章 webpack与构建发展简史
  • 【Spring】@ConfigurationProperties 注解的简单使用和介绍
  • Hive概论、架构和基本操作
  • ios逆向工具有那些
  • 2022国赛32:NFS服务
  • 【数据库】面试题合集
  • 【JS运算】分组求和/平均值(reduce函数)
  • 2023 年 MQTT 协议的 7 个技术趋势|描绘物联网的未来
  • 使用Nginx反向代理OpenAI API
  • python内置方法的使用方法及示例
  • Python 小型项目大全 46~50
  • 怎样做好仓库管理?必须记住这八条!
  • 文件:IO流
  • 黑马2023JavaScript笔记
  • 鸿鹄工程项目管理系统源码 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统