规范解读
我在之前的blog,有提到过Android端GB28181接入端的语音广播和语音对讲,今天主要从GB/T28181-2016官方规范和交互流程,大概介绍下Android平GB28181接入端的语音广播和语音对讲。
关于交互流程,本文不再赘述,一张图足矣:
接下来,我们主要来看看规范里面提到的协议接口。
语音广播通知、语音广播应答命令
消息头 Content-type字段为 Content-type:Application/MANSCDP+xml。
语音广播通知、语音广播应答命令采用 MANSCDP协议格式定义。
消息示例如下:
a) 语音广播通知
MESSAGE sip:31010403001370002272@192.168.0.199:5511SIP/2.0 From: <sip:31010400002000000001 @ 3101040000>;tag = b05e7e60-ca00a8c0-1587-3a3-7fb52a44-3a3 To:<sip:31010403001370002272@192.168.0.199:5511> Call-ID:b05e7e60-ca00a8c0-1587-3a3-2b297f29-3a3@3101040000 CSeq:1761796551 MESSAGE Via:SIP/2.0/UDP192.168.0.202:5511;rport;branch=z9hG4bK-3a3-e3849-287ef646 Max-Forwards:70 Content-Type:application/MANSCDP+xml Content-Length:159 <? xmlversion="1.0" ?> <Notify> <CmdType>Broadcast</CmdType> <SN>992</SN> <SourceID>31010400001360000001</SourceID> <TargetID>31010403001370002272</TargetID> </Notify>
b) 语音广播应答
MESSAGEsip:31010400002000000001@3101040000SIP/2.0 From:<sip:31010403001370002272@3101040300>;tag=b55b4cf8-c700a8c0-1587-a3-1ba9ac5-a3 To:<sip:31010400002000000000@3101040000> Call-ID:b55b4cf8-c700a8c0-1587-a3-5eacf182-a3@3101040300 CSeq:1856483244 MESSAGE Via:SIP/2.0/UDP192.168.0.199:5511;rport;branch=z9hG4bK-a3-27e0b-71dd2b33 Max-Forwards:70 Content-Type:application/MANSCDP+xml <? xmlversion="1.0" ?> <Response> <CmdType>Broadcast</CmdType> <SN>992</SN> <DeviceID>31010403001370002272</DeviceID> <Result>OK</Result> </Response>
涉及到的SDP相关参数:
v=0 o=6401060000202000000100INIP4172.20.16.3 s=Play c=INIP4172.20.16.3 t=00 m=audio8000RTP/AVP8 //标识语音媒体流内容 a=sendonly a=rtpmap:8PCMA/8000 //RTP+音频流 y=0100000001 f=v/a/1/8/1 //音频参数描述
技术实现
本文以大牛直播SDK的Android平台基于Camera2的采集demo为例,如果需要注册到GB28181平台,点击页面的“启动GB28181”即可,有语音广播过来后,使能“GB28181语音广播”按钮,用于主动关闭语音广播之用。
语音广播信令Listener如下:
package com.gb28181.ntsignalling; public interface GBSIPAgentListener { /* *收到语音广播通知 */ void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID); /* *需要准备接受语音广播的SDP内容 */ void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID); /* *音频广播, 发送Invite请求异常 */ void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo); /* *音频广播, 等待Invite响应超时 */ void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID); /* *音频广播, 收到Invite消息最终响应 */ void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int; /* * 音频广播, 收到BYE Message */ void ntsOnByeAudioBroadcast(String sourceID, String targetID); /* * 不是在收到BYE Message情况下, 终止音频广播 */ void ntsOnTerminateAudioBroadcast(String sourceID, String targetID); }
相关信令接口如下:
package com.gb28181.ntsignalling; public interface GBSIPAgent { /* *语音广播应答 */ void respondBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID, boolean; /* *语音广播接收者发送Invite消息, rtp ssrc暂时由sdk生成 *@param addressType: ipv4:"IP4", ipv6:"IP6", 其他不支持, 填充SDP用 *@param localAddress: 本地IP地址, 填充SDP用 *@param localPort: 本地端口, 填充SDP用 *@param mediaTransportProtocol: 媒体传输协议, rtp over udp:"RTP/AVP", rtp over tcp:"TCP/RTP/AVP". 其他不支持, 填充SDP用 */ boolean inviteAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID, String addressType, String localAddress, int; /* *取消音频广播, 这个需要在invite收到临时响应之后,最终响应之前才能成功, 如果UAS已经发送过最终响应, UAS收到cancel不做处理, 具体参考RFC3261 */ boolean cancelAudioBroadcast(String sourceID, String targetID); /* *终止语音广播会话, 发送BYE消息 */ boolean byeAudioBroadcast(String sourceID, String targetID); }
接下来就是收到RTP音频包和解码输出这块,我们直接在播放端做扩展即可,设计如下:
/* * SmartPlayerJniV2.java * SmartPlayerJniV2 * * WebSite: https://daniusdk.com */ /*++++++++++++++++++RTP Receiver++++++++++++++++++++++*/ /* * 创建RTP Receiver * * @param reserve:保留参数传0 * * @return RTP Receiver 句柄,0表示失败 */ public native long CreateRTPReceiver(int reserve); /** *设置 RTP Receiver传输协议 * * @param rtp_receiver_handle, CreateRTPReceiver * @param transport_protocol, 0:UDP, 1:TCP, 默认是UDP * * @return {0} if successful */ public native int SetRTPReceiverTransportProtocol(long rtp_receiver_handle, int transport_protocol); /** *设置 RTP Receiver IP地址类型 * * @param rtp_receiver_handle, CreateRTPReceiver * @param ip_address_type, 0:IPV4, 1:IPV6, 默认是IPV4 * * @return {0} if successful */ public native int SetRTPReceiverIPAddressType(long rtp_receiver_handle, int ip_address_type); /** *设置 RTP Receiver RTP Socket本地端口 * * @param rtp_receiver_handle, CreateRTPReceiver * @param port, 必须是偶数,设置0的话SDK会自动分配, 默认值是0 * * @return {0} if successful */ public native int SetRTPReceiverLocalPort(long rtp_receiver_handle, int port); /** *设置 RTP Receiver SSRC * * @param rtp_receiver_handle, CreateRTPReceiver * @param ssrc, 如果设置的话,这个字符串要能转换成uint32类型, 否则设置失败 * * @return {0} if successful */ public native int SetRTPReceiverSSRC(long rtp_receiver_handle, String ssrc); /** *创建 RTP Receiver 会话 * * @param rtp_receiver_handle, CreateRTPReceiver * @param reserve, 保留值,目前传0 * * @return {0} if successful */ public native int CreateRTPReceiverSession(long rtp_receiver_handle, int reserve); /** *获取 RTP Receiver RTP Socket本地端口 * * @param rtp_receiver_handle, CreateRTPReceiver * * @return 失败返回0, 成功的话返回响应的端口, 请在CreateRTPReceiverSession返回成功之后调用 */ public native int GetRTPReceiverLocalPort(long rtp_receiver_handle); /** *设置 RTP Receiver Payload 相关信息 * * @param rtp_receiver_handle, CreateRTPReceiver * * @param payload_type, 请参考 RFC 3551 * * @param encoding_name, 编码名, 请参考 RFC 3551, 如果payload_type不是动态的, 可能传null就好 * * @param media_type, 媒体类型, 请参考 RFC 3551, 1 是视频, 2是音频 * * @param clock_rate, 请参考 RFC 3551 * * @return {0} if successful */ public native int SetRTPReceiverPayloadType(long rtp_receiver_handle, int payload_type, String encoding_name, int media_type, int clock_rate); /** *设置 RTP Receiver 音频采样率 * * @param rtp_receiver_handle, CreateRTPReceiver * @param sampling_rate, 音频采样率 * * @return {0} if successful */ public native int SetRTPReceiverAudioSamplingRate(long rtp_receiver_handle, int sampling_rate); /** *设置 RTP Receiver 音频通道数 * * @param rtp_receiver_handle, CreateRTPReceiver * @param channels, 音频通道数 * * @return {0} if successful */ public native int SetRTPReceiverAudioChannels(long rtp_receiver_handle, int channels); /** *设置 RTP Receiver 远端地址 * * @param rtp_receiver_handle, CreateRTPReceiver * @param address, IP地址 * @param port, 端口 * * @return {0} if successful */ public native int SetRTPReceiverRemoteAddress(long rtp_receiver_handle, String address, int port); /** *初始化 RTP Receiver * * @param rtp_receiver_handle, CreateRTPReceiver * * @return {0} if successful */ public native int InitRTPReceiver(long rtp_receiver_handle); /** *UnInit RTP Receiver * * @param rtp_receiver_handle, CreateRTPReceiver * * @return {0} if successful */ public native int UnInitRTPReceiver(long rtp_receiver_handle); /** *Destory RTP Receiver Session * * @param rtp_receiver_handle, CreateRTPReceiver * * @return {0} if successful */ public native int DestoryRTPReceiverSession(long rtp_receiver_handle); /** *Destory RTP Receiver * * @param rtp_receiver_handle, CreateRTPReceiver * * @return {0} if successful */ public native int DestoryRTPReceiver(long rtp_receiver_handle); /*++++++++++++++++++RTP Receiver++++++++++++++++++++++*/
以上是GB/T28181-2016规范关于语音广播和语音对讲的部分说明和Android端GB28181接入端针对语音广播的技术实现,感兴趣的开发者可酌情参考。