国网B接口语音对讲和广播技术探究及与GB28181差别

简介: 在谈国网B接口的语音广播和语音对讲的时候,大家会觉得,国网B接口是不是和GB28181大同小异?实际上确实信令有差别,但是因为要GB28181设备接入测的对接,再次做国网B接口就简单多了。

接口描述

在谈国网B接口的语音广播和语音对讲的时候,大家会觉得,国网B接口是不是和GB28181大同小异?实际上确实信令有差别,但是因为要GB28181设备接入测的对接,再次做国网B接口就简单多了。


语音对讲和广播包括信令接口和媒体流接口,采用与“调阅实时视频”流程相同的机制,不同之处

在于用户发出的请求消息的 SDP 仅携带音频描述信息。

语音所采用的编解码算法为 ITU-T G.711A

语音对讲和广播,被调阅的平台无需进行音频分发行为。

接口流程

语音对讲和广播的接口流程如下:


ad8a036e2208446089ec94112c8cd528.png

主要功能流程如下:

a) F1:平台用户,对前端系统指定的前端系统摄像机发起实时音频的调阅请求,发送 INVITE 消

息,携带 SDP 内容,通过平台转发到前端系统。

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

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

d) F4:平台发送 ACK 给前端系统,确认会话建立。

e) 实时语音流开始传输,前端设备/用户根据相应的解码器解码并语音输出。

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

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

接口参数

SIP头字段如下:

37070de6c3f5421eaec20b999c15ceb8.png

SIP响应码返回码如下:

3d5a2d952a284eae8ff6430796305ff5.pngSIP参数定义:

8a51a45450f14ccaae17ec72a7c9f87a.png

094159f2f4f4424baec15c5cd357ff24.png

RTP 动态 Payload 定义如下:

d5e7cc725be942bb93821b6ee40f63c4.png

消息示例

INVITE sip:前端设备地址编码@前端系统所属平台域名或IP地址 SIP/2.0
From: <sip:用户地址编码@用户所属平台域名或IP地址>;tag=3101300
To: <sip:前端设备地址编码@前端系统所属平台域名或IP地址>
Contact: <sip:用户地址编码@用户所属平台域名或IP地址>
Call-ID: c47k42
Via: SIP/2.0/UDP 用户所属平台IP地址;branch=z9hG4bK
CSeq: 1 INVITE
Content-type: application/SDP
Content-Length: 消息体的长度
v=0
o=- 0 0 IN IP4 用户会话IP地址描述
s=-
c=IN IP4 用户媒体IP地址描述
m=audio 38564 RTP/AVP 8
a=rtpmap:8 PCMA/8000
a=sendrecv

语音会话请求响应如下:

SIP/2.0 200 OK
From: <sip: 用户地址编码@用户所属平台域名或IP地址>;tag=3101300
To: <sip: 前端设备地址编码@前端系统所属平台域名或IP地址>;tag=20b0660
Contact: <sip: 用户地址编码@用户所属平台域名或IP地址>
Call-ID: c47k42
Via: SIP/2.0/UDP 用户所属平台IP地址;branch=z9hG4bK
CSeq: 1 INVITE
Content-type: application/SDP
Content-Length: 消息体的长度
v=0
o=- 0 0 IN IP4 前端设备会话IP地址描述
s=-
c=IN IP4 前端设备媒体IP地址描述
m=audio 1000 RTP/AVP 8
a=rtpmap:8 PCMA/8000
a=sendrecv

代码实现

    @Override
    public void ntsOnInviteTalk(String deviceId, SessionDescription sessionDescription) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 先振铃响应下
                gb28181_agent_.respondTalkInvite(180, device_id_);
                MediaSessionDescription audio_description = null;
                SDPRtpMapAttribute rtp_map_attribute = null;
                Vector<MediaSessionDescription> audio_des_list = session_description_.getAudioDescriptions();
                if (audio_des_list != null && !audio_des_list.isEmpty()) {
                    // 先尝试获取PCMA格式
                    for(MediaSessionDescription m : audio_des_list) {
                        if (m != null && m.isValidAddressType() && m.isHasAddress()) {
                            rtp_map_attribute = m.getRtpMapAttribute(SDPRtpMapAttribute.PCMA_ENCODING_NAME);
                            if (rtp_map_attribute != null) {
                                audio_description = m;
                                break;
                            }
                        }
                    }
                    // 如果没有PCMA格式,尝试获取PS格式
                    if (null == rtp_map_attribute) {
                        for(MediaSessionDescription m : audio_des_list) {
                            if (m != null && m.isValidAddressType() && m.isHasAddress()) {
                                rtp_map_attribute = m.getRtpMapAttribute(SDPRtpMapAttribute.PS_ENCODING_NAME);
                                if (rtp_map_attribute != null) {
                                    audio_description = m;
                                    break;
                                }
                            }
                        }
                    }
                }
                if (null == audio_description) {
                    gb28181_agent_.respondTalkInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInviteTalk get audio description is null, response 488, device_id:" + device_id_);
                    return;
                }
                if (null == rtp_map_attribute ) {
                    gb28181_agent_.respondTalkInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInviteTalk get rtp map attribute is null, response 488, device_id:" + device_id_);
                    return;
                }
                Log.i(TAG,"ntsOnInviteTalk, device_id:" +device_id_+", is_tcp:" + audio_description.isRTPOverTCP()
                        + " rtp_port:" + audio_description.getPort() + " ssrc:" + audio_description.getSSRC()
                        + " address_type:" + audio_description.getAddressType() + " address:" + audio_description.getAddress()
                        + " payload_type:" +   rtp_map_attribute.getPayloadType() + " encoding_name:" + rtp_map_attribute.getEncodingName());
                long rtp_sender_handle = libPublisher.CreateRTPSender(0);
                if (0 == rtp_sender_handle) {
                    gb28181_agent_.respondTalkInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInviteTalk CreateRTPSender failed, response 488, device_id:" + device_id_);
                    return;
                }
                gb_talk_rtp_payload_type_  = rtp_map_attribute.getPayloadType();
                gb_talk_rtp_encoding_name_ = rtp_map_attribute.getEncodingName();
                Log.i(TAG, "gb_talk_rtp_payload_type: " + gb_talk_rtp_payload_type_ + " gb_talk_rtp_encoding_name: " + gb_talk_rtp_encoding_name_);
                gb_talk_rtp_encoding_name_ =  "PS";
                libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, audio_description.isRTPOverUDP()?0:1);
                libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, audio_description.isIPv4()?0:1);
                //libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
                libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, audio_description.getPort());
                libPublisher.SetRTPSenderSSRC(rtp_sender_handle, audio_description.getSSRC());
                libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 256*1024); // 音频配置到256KB
                libPublisher.SetRTPSenderClockRate(rtp_sender_handle, rtp_map_attribute.getClockRate());
                libPublisher.SetRTPSenderDestination(rtp_sender_handle, audio_description.getAddress(), audio_description.getPort());
                gb_talk_is_receive_ = audio_description.isHasAttribute("sendrecv");
                Log.i(TAG, "gb_talk_is_receive: " + gb_talk_is_receive_);
                if (gb_talk_is_receive_) {
                    libPublisher.EnableRTPSenderReceive(rtp_sender_handle, 1);
                    // 收包SSRC, 暂时不设置, 因为部分平台ssrc不一致的
                    // libPublisher.SetRTPSenderReceiveSSRC(rtp_sender_handle, audio_description.getSSRC());
                    // 这个一定要设置
                    libPublisher.SetRTPSenderReceivePayloadType(rtp_sender_handle, gb_talk_rtp_payload_type_, gb_talk_rtp_encoding_name_, 2,  rtp_map_attribute.getClockRate());
                    // 目前发现某些平台 PS-PCMA 是8000, 不建议设置
                    if (gb_talk_rtp_encoding_name_.equals("PS")) {
                        libPublisher.SetRTPSenderReceivePSClockFrequency(rtp_sender_handle, 8000);
                     }
                    // 如果是PCMA编码, 采样率和通道可以先不设置
                    // libPublisher.SetRTPSenderReceiveAudioSamplingRate(rtp_sender_handle, 8000);
                    // libPublisher.SetRTPSenderReceiveAudioChannels(rtp_sender_handle, 1);
                }
                if (libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
                    gb28181_agent_.respondTalkInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }
                int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);
                if (0==local_port) {
                    gb28181_agent_.respondTalkInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }
                Log.i(TAG,"ntsOnInviteTalk get local_port:" + local_port);
                String local_ip_addr = IPAddrUtils.getIpAddress(context_);
                MediaSessionDescription main_local_audio_des = new MediaSessionDescription(audio_description.getType());
                main_local_audio_des.addFormat(String.valueOf(rtp_map_attribute.getPayloadType()));
                main_local_audio_des.addRtpMapAttribute(rtp_map_attribute);
                main_local_audio_des.addAttribute(new SDPAttribute("sendonly"));
                if (audio_description.isRTPOverTCP()) {
                    // tcp主动链接服务端
                    main_local_audio_des.addAttribute(new SDPAttribute("setup", "active"));
                    main_local_audio_des.addAttribute(new SDPAttribute("connection", "new"));
                }
                main_local_audio_des.setPort(local_port);
                main_local_audio_des.setTransportProtocol(audio_description.getTransportProtocol());
                main_local_audio_des.setSSRC(audio_description.getSSRC());
                MediaSessionDescription sub_local_audio_des = null;
                if (gb_talk_is_receive_) {
                    sub_local_audio_des = new MediaSessionDescription(audio_description.getType());
                    sub_local_audio_des.addFormat(String.valueOf(rtp_map_attribute.getPayloadType()));
                    sub_local_audio_des.addRtpMapAttribute(rtp_map_attribute);
                    sub_local_audio_des.addAttribute(new SDPAttribute("recvonly"));
                    if (audio_description.isRTPOverTCP()) {
                        // tcp主动链接服务端
                        sub_local_audio_des.addAttribute(new SDPAttribute("setup", "active"));
                        sub_local_audio_des.addAttribute(new SDPAttribute("connection", "new"));
                    }
                    sub_local_audio_des.setPort(local_port);
                    sub_local_audio_des.setTransportProtocol(audio_description.getTransportProtocol());
                    sub_local_audio_des.setSSRC(audio_description.getSSRC());
                }
                if (!gb28181_agent_.respondTalkInviteOK(device_id_, audio_description.getAddressType(), local_ip_addr, main_local_audio_des, sub_local_audio_des) ) {
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    Log.e(TAG, "ntsOnInviteTalk call respondPlayInviteOK failed.");
                    return;
                }
                gb_talk_rtp_sender_handle_ = rtp_sender_handle;
            }
            private String device_id_;
            private SessionDescription session_description_;
            public Runnable set(String device_id, SessionDescription session_des) {
                this.device_id_ = device_id;
                this.session_description_ = session_des;
                return this;
            }
        }.set(deviceId, sessionDescription),0);
    }

总结

国网B接口的语音广播和语音对讲,和GB28181的还是有些差别,B接口的语音广播和语音对讲,不需要先发broadcast过来,不用设备接入端发invite请求,而是电网平台侧发invite,类似实时视频请求播放流程,感兴趣的开发者,可以根据规范仔细解读研究。

相关实践学习
基于函数计算快速搭建Hexo博客系统
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
8月前
|
开发工具 Android开发
Android平台GB28181设备接入端语音广播技术探究和填坑指南
GB/T28181-2016官方规范和交互流程,我们不再赘述。
|
8月前
|
网络安全 KVM 网络虚拟化
变形金刚外传0x03:进一步讨论NSX-T的传输节点就绪
话接上篇,对于NSX-T来说,由于传输节点配置文件将传输区域与主机交换机进行了严格意义上的绑定,因此不会出现在NSX-V场景中传输区域与分布式交换机覆盖不一致的情况。
|
存储 编解码 算法
信道编码概述 |带你读《5G空口特性与关键技术》之六
纠错编码的目的,是通过尽可能小的冗余开销确保接收端能自动地纠正数据传输中所发生的差错。在同样的误码率下,所需要的开销越小,编码的效率也就越高。
10184 2
信道编码概述 |带你读《5G空口特性与关键技术》之六
|
7月前
|
前端开发 芯片
【芯片前端】保持代码手感——握手型同步fifo的进一步拓展
【芯片前端】保持代码手感——握手型同步fifo的进一步拓展
|
8月前
|
开发工具 Android开发 开发者
GB28181媒体保活机制探究与实现
GB28181-2016和GB28181-2022关于媒体保活机制这块,并无调整,平台、设备媒体流保活机制规定如下: a)链路建立后,码流经过的各级平台应具备媒体流丢失监测能力,若监测到媒体流丢失,应释放该条媒体链路,并通过会话内Bye消息通知上下级平台;
132 0
|
8月前
|
开发工具 Android开发 开发者
Android平台GB28181接入端语音广播和语音对讲规范解读和技术实现
我在之前的blog,有提到过Android端GB28181接入端的语音广播和语音对讲,今天主要从GB/T28181-2016官方规范和交互流程,大概介绍下Android平GB28181接入端的语音广播和语音对讲。
151 0
|
负载均衡 监控 安全
网关系统就该这么设计,万能通用,稳的一批!
网关系统就该这么设计,万能通用,稳的一批!
|
负载均衡 监控 安全
网关系统就该这么设计(万能通用),稳的一批!
网关系统就该这么设计(万能通用),稳的一批!
|
传感器 安全 API
|
编解码 前端开发 智能网卡
【LC3开源峰会网络技术系列之二】阿里云开发智能网卡的动机、功能框架和软转发程序
摘要 这篇文章介绍了阿里云开发智能网卡的动机、功能框架和软转发程序以及在软转发过程中发现的问题和优化方法。 主讲人陈静 阿里云高级技术专家 主题Zero-copy Optimization for DPDK vhost-user Receiving 分论坛Network & Orchestration 项目背景 在VPC产品部署中虚拟交换Virtual Switch承担着overlay层和underlay层进行网络协议的加解密encap/decap功能在多租户虚拟机或者容器的主机上也需要进行二三层的路由转发、Qos、限流、安全组等。
5352 0