安卓GB28181跨网段语音对讲

8 篇文章 0 订阅
7 篇文章 0 订阅
GB28181语音对讲在跨网段时遇到RTP UDP包穿透问题,文章提出了两种解决方案:方案一是安卓端语音通过实时视音频点播通道,配合STUN服务;方案二是遵循GB28181标准的语音对讲流程,处理SDP格式,通过媒体服务器转发。实现稳定语音对讲需考虑回音、噪音等问题。
摘要由CSDN通过智能技术生成

    GB28181语音对讲实际使用中遇到的主要问题是跨网段后rtp udp包不能穿透,针对这个问题有两套解决方案。

    方案一,安卓端语音发送走实时视音频点播通道,把编码后的语音数据封装到PS包中,和视频帧一起发送,  安卓接收语音走语音广播流程,但因为是跨网段的,安卓端可以尝试主动的rtp over tcp方式,但如果对端不支持tcp,这个方案就走不通了, 还有一点部分GB28181系统实时回看时并不支持语音播放; 不用tcp传包,用udp传就得打洞,要增加打洞成功率,特别是4g, 5g网络环境下,类似stun的服务得部署, 即使有了stun, 也有可能穿透不成功。我实际中搭配某些GB28181 Server使用下来, 效果还不错. 重点是说方案二。

    方案二,走语音对讲流程,这个流程和实时视音频点播流程一致, 就是安卓端收到的INVITE-SDP内容略有不同,SDP大概有以下两种:

s=Talk

............省略部分

t=0 0

m=audio 端口 RTP/AVP 8

a=rtpmap:8 PCMA/8000

a=sendrecv
y=xxxx........

........ 部分省略

  上面这种SDP格式是标准的,s是Talk, 把PCMA打包成rtp包发送或接收. 代码实现上也容易些.

  实际中还有一种SDP:

    s=Play
   ............省略部分
    t=0 0
    m=audio 端口号 RTP/AVP 96

    a=rtpmap:96 PS/90000
    a=sendrecv
    y=xxxxxxxx....

    ............省略部分

    这种SDP处理起来工作量就大了,首先s=Play, 若按GB28181标准, 这是要发起实时视音频点播会话,但根据m=audio可以判断出并不想要视频,只想收发纯音频,那应该算语音对讲会话.如果老的代码实现认为s=Play是实时视音频点播,请及时修改代码, 兼容这种情况. 另外GB28181文档没有对PS打包纯音频进行描述,GB28181文档中只描述了音视频混合打包或纯视频打包方式:

PS包头, System header, PSM, PESV, PEAS

    对于纯音频打包PS,请按上述描述适当扩展,另外代码实现要增加PS解析环节,这个比直接从rtp包中解析PCMA要多不少工作量, 麻烦归麻烦,做还是要做, 打包成纯音频PS包,或直接PCMA over rtp的收发解析我都已实现。

   这个方案的关键点在于"a=sendrecv",  也就是用一个端口同时收发rtp包,按照GB28181标准,语音对讲先把音频rtp包发给媒体服务器, 实际环境下只要确保各个网段内的GB28181设备可以访问到媒体服务器就好(这个基本都可以做到),安卓端先主动发rtp包给媒体服务器,然后媒体服务器用相同端口发rtp包给安卓端。而语音广播在某些GB28181平台上的实现可能走的是点对点,并没有通过媒体服务器转发rtp包。

   另外要注意的是,如果SDP中s=Play,那200 OK响应的SDP中s必须也是"Play", 即s=Play. 

   相对于方案二,方案一把音视频打包在同一个PS包中,使用相同的端口发送, PS包很大,带宽占的多,网络不好的话,容易出现延时或丢包,延时大了, 对讲又是双向的, 效果体验不好。方案二只传纯音频,PCMA码率是固定的64kbps,加上rtp头20多个字节,带宽占用小很多.

  另外语音对讲不光要解决传输跨网段问题,还可能要处理回音,噪音,增益控制等。

  说了这么多,做出来稳定好用才算数,下面是我的实现接口:

// Github: https://github.com/daniulive/SmarterStreaming
// Copyright (C) 1130758427@qq.com


public interface GBSIPAgentTalkListener {
    /*
     *收到语音对讲INVITE
     */
    void ntsOnInviteTalk(String deviceId, SessionDescription sessionDescription);

    /*
     *发送talk invite response 异常
     */
    void ntsOnTalkInviteResponseException(String deviceId, int statusCode, String errorInfo);

    /*
     * 收到CANCEL Talk INVITE请求
     */
    void ntsOnCancelTalk(String deviceId);

    /*
     * 收到Ack
     */
    void ntsOnAckTalk(String deviceId);

    /*
     * 收到Bye
     */
    void ntsOnByeTalk(String deviceId);

    /*
     * 不是在收到BYE Message情况下,终止Talk
     */
    void ntsOnTerminateTalk(String deviceId);


    void ntsOnTalkDialogTerminated(String deviceId);
}


public interface GBSIPAgent {

   // 其他接口省略......

   void addTalkListener(GBSIPAgentTalkListener talkListener);
   
   /*
     *响应Invite Talk 200 OK
     */
    boolean respondTalkInviteOK(String deviceId, String addressType, String localAddress,
                                MediaSessionDescription mainLocalAudioDescription, MediaSessionDescription subLocalAudioDescription);

    /*
     *响应Invite Talk 其他状态码
     */
    boolean respondTalkInvite(int statusCode, String deviceId);


    /*
     *终止Talk会话
     */
    void terminateTalk(String deviceId, boolean isSendBYE);

    /*
     *终止所有Talk会话
     */
    void terminateAllTalks(boolean isSendBYE);
}


    两个方案测试下来,都能达到语音对讲的目的,效果都不错,如果服务支持方案二的话,优先考虑方案二,不支持的话的,再考虑方案一,从文档到代码实现要花很多精力和时间,特别是方案二,要码不少代码。更多问题联系qq: 1130758427

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值