我在之前的blog,有提到过Android端GB28181接入端的语音广播和语音对讲,今天主要从GB/T28181-2016官方规范和交互流程,大概介绍下Android平GB28181接入端的语音广播和语音对讲。
关于交互流程,本文不再赘述,一张图足矣:
接下来,我们主要来看看规范里面提到的协议接口。
语音广播通知、语音广播应答命令
消息头 Content-type字段为 Content-type:Application/MANSCDP xml。
语音广播通知、语音广播应答命令采用 MANSCDP协议格式定义。
消息示例如下:
a) 语音广播通知
代码语言:javascript复制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) 语音广播应答
代码语言:javascript复制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相关参数:
代码语言:javascript复制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如下:
代码语言:javascript复制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);
}
相关信令接口如下:
代码语言:javascript复制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音频包和解码输出这块,我们直接在播放端做扩展即可,设计如下:
代码语言:javascript复制/*
* 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接入端针对语音广播的技术实现,感兴趣的开发者可酌情参考。