技术背景

对接Android平台GB28181设备接入端语音广播的时候,我们有遇到过INVITE SDP需要PCMA格式的audio,对方同时回了PS和PCMA两种,然后,发数据的时候,直接发了PS的。

举个不恰当的例子,就像我去星巴克跟服务员协商,我需要点一杯卡布基诺,然后服务员说,我有红茶拿铁和卡布基诺,我很高兴,结果最后妹子给我上来的是红茶拿铁。是不是很遗憾?更遗憾的是,GB28181-2016规范里面,针对语音广播PCMA格式有明确的说明和范例,并没有针对PS的描述。

场景还原

服务员:先生您好,您要点咖啡吗?

MESSAGE sip:34020000002000000001@192.168.2.120:15060 SIP/2.0
Call-ID: 5ae7cfcd8b6696b02a512e32c38bbe35@192.168.43.177
CSeq: 5796113 MESSAGE
From: <sip:44138091001320090001@3402000000>;tag=acc504a0
To: <sip:34020000002000000001@192.168.2.120:15060>
Via: SIP/2.0/UDP 192.168.43.177:5060;rport;branch=z9hG4bK-353834-ae5e552996a10590419bd216b81ebaa6
Max-Forwards: 70
User-Agent: Daniusdk GB UserAgent V1.9-20221027
Content-Type: Application/MANSCDP+xml
Content-Length: 172<?xml version="1.0" encoding="GB2312"?>
<Response>
<CmdType>Broadcast</CmdType>
<SN>1</SN>
<DeviceID>34020000001380000001</DeviceID>
<Result>OK</Result>
</Response>

我:是的,我需要咖啡。

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.43.177:5060;rport=5060;branch=z9hG4bK-353834-ae5e552996a10590419bd216b81ebaa6;received=192.168.225.1
From: <sip:44138091001320090001@3402000000>;tag=acc504a0
To: <sip:34020000002000000001@192.168.2.120:15060>;tag=1678330894580
Call-ID: 5ae7cfcd8b6696b02a512e32c38bbe35@192.168.43.177
CSeq: 5796113 MESSAGE
Content-Length: 0
Contact: <sip:34020000002000000001@192.168.2.120:15060>
User-Agent: GoSIP

我:能给我杯卡布基诺吗?

INVITE sip:31010400001360000001@3101040000 SIP/2.0
Call-ID: 33a3b4d751e1bf831f9d7ddd03218fcaf
CSeq: 796255 INVITE
From: <sip:44138091001320090001@3402000000>;tag=f2f8e526
To: <sip:31010400001360000001@3101040000>
Via: SIP/2.0/UDP 192.168.43.177:5060;rport;branch=z9hG4bK-353834-b98838ae9494d94a9e3acb10c1dbc8ad
Max-Forwards: 70
Contact: <sip:44138091001320090001@192.168.43.177:5060>
Subject: 31010400001360000001:0104001939,34020000001380000001:0
User-Agent: Daniusdk GB UserAgent V1.9-20221027
Content-Type: APPLICATION/SDP
Content-Length: 209v=0
o=44138091001320090001 3887319688604 3887319688604 IN IP4 192.168.43.177
s=Play
c=IN IP4 192.168.43.177
t=0 0
m=audio 52428 RTP/AVP 8
a=recvonly
a=rtpmap:8 PCMA/8000
y=0104001939
f=v/a/1/8/1

服务员:先生您好,我们这边有红茶拿铁和卡布基诺。

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.43.177:5060;rport=5060;branch=z9hG4bK-353834-b98838ae9494d94a9e3acb10c1dbc8ad;received=192.168.225.1
From: <sip:44138091001320090001@3402000000>;tag=f2f8e526
To: <sip:31010400001360000001@3101040000>;tag=1678330894725
Call-ID: 33a3b4d751e1bf831f9d7ddd03218fcaf
CSeq: 796255 INVITE
Content-Length: 208
Contact: <sip:34020000002000000001@192.168.2.120:15060>
Content-Type: application/sdp
Allow: INVITE, ACK, CANCEL, REGISTER, MESSAGE, NOTIFY
User-Agent: GoSIPv=2
o=34020000002000000001 0 0 IN IP4 192.168.2.120
s=Play
c=IN IP4 192.168.2.120
t=0 0
m=audio 15082 RTP/AVP 96 8
a=sendonly
a=rtpmap:96 PS/90000
a=rtpmap:8 PCMA/8000
y=0104001939
f=v/a/1/8/1

我:好的,那太棒了。

ACK sip:34020000002000000001@192.168.2.120:15060 SIP/2.0
Call-ID: 33a3b4d751e1bf831f9d7ddd03218fcaf
CSeq: 796255 ACK
Via: SIP/2.0/UDP 192.168.43.177:5060;rport;branch=z9hG4bK-353834-b01972a6eaad0137b0bca192d6388540
From: <sip:44138091001320090001@3402000000>;tag=f2f8e526
To: <sip:31010400001360000001@3101040000>;tag=1678330894725
Max-Forwards: 70
Contact: <sip:44138091001320090001@192.168.43.177:5060>
User-Agent: Daniusdk GB UserAgent V1.9-20221027
Content-Length: 0

咖啡上来了。。

我:怎么给我上的是红茶拿铁?明明我们协商的时候,我要的是卡布基诺!

你能有什么办法?

遇到这种情况,要么push厂商调整,既然SDP回的有PCMA格式,那就直接发送PCMA的语音广播数据,要么只能我们这边兼容,就像明明不喜欢红茶拿铁,还是要耐着性子喝。

规范回顾

说了这么多废话,还是回顾下语音广播的交互流程,因为之前的blog做过几次说明,这里不再赘述:

技术实现

本文以大牛直播SDK的Android平台基于Camera2的采集demo为例,如果需要注册到GB28181平台,点击页面的“启动GB28181”即可,有语音广播过来后,使能“GB28181语音广播”按钮,用于主动关闭语音广播之用。

其他处理不再赘述,这里说说invite audio broadcast response里面的处理:

//Author: daniusdk.com
@Override
public void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int statusCode, SessionDescription sessionDescription) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnInviteAudioBroadcastResponse, statusCode:" + status_code_ +" sourceID:" + source_id_ + ", targetID:" + target_id_);boolean is_need_destory_rtp = true;if (gb28181_agent_ != null ) {boolean is_need_bye = 200==status_code_;if (200 == status_code_ && session_description_ != null && rtp_receiver_handle_ != 0 ) {MediaSessionDescription audio_des = null;SDPRtpMapAttribute audio_attr = null;Vector<MediaSessionDescription> audio_des_list = session_description_.getAudioDescriptions();if (audio_des_list != null && !audio_des_list.isEmpty() ) {for (MediaSessionDescription m : audio_des_list) {if (m != null && m.isValidAddressType() && m.isHasAddress() && m.isHasRtpMapAttribute()) {audio_des = m;audio_attr = audio_des.getRtpMapAttributes().get(0);break;}}}if ( audio_des != null && audio_attr != null ) {lib_player_.SetRTPReceiverSSRC(rtp_receiver_handle_, audio_des.getSSRC());int payload_type = audio_attr.getPayloadType();String encoding_name = audio_attr.getEncodingName();lib_player_.SetRTPReceiverPayloadType(rtp_receiver_handle_, payload_type,encoding_name, 2, audio_attr.getClockRate());if (encoding_name != null && encoding_name.equals("PS")) {// 分析PS流,需要设置下面的特殊值// 如果是90000的话,不需要设置// lib_player_.SetRTPReceiverPSClockFrequency(rtp_receiver_handle_, payload_type, 1000);// 含义请看28181文档, 如果PS流中有PSM的话,不需要设置// lib_player_.SetRTPReceiverPSMap(rtp_receiver_handle_, payload_type, 0x90, 0xC0);}// 如果是PCMA, SDK会默认填 采样率8000, 通道1, 其他音频编码需要手动填入//lib_player_.SetRTPReceiverAudioSamplingRate(rtp_receiver_handle_, payload_type, 8000);//lib_player_.SetRTPReceiverAudioChannels(rtp_receiver_handle_, payload_type, 1);lib_player_.SetRTPReceiverRemoteAddress(rtp_receiver_handle_, audio_des.getAddress(), audio_des.getPort());lib_player_.InitRTPReceiver(rtp_receiver_handle_);if (startAudioPlay()) {is_need_bye = false;is_need_destory_rtp = false;gb_broadcast_source_id_ = source_id_;gb_broadcast_target_id_ = target_id_;btnGB28181AudioBroadcast.setText("终止GB28181语音广播");btnGB28181AudioBroadcast.setEnabled(true);}}} else {btnGB28181AudioBroadcast.setText("GB28181语音广播");}if (is_need_bye)gb28181_agent_.byeAudioBroadcast(source_id_, target_id_);}if (is_need_destory_rtp)destoryRTPReceiver();}private String source_id_;private String target_id_;private int status_code_;private SessionDescription session_description_;public Runnable set(String source_id, String target_id, int status_code, SessionDescription session_description) {this.source_id_ = source_id;this.target_id_ = target_id;this.status_code_ = status_code;this.session_description_ = session_description;return this;}}.set(sourceID, targetID, statusCode, sessionDescription),0);
}

我们根据回上来的SDP,判断encoding_name是PCMA还是PS的,如果是PS的,可选设置下SetRTPReceiverPSClockFrequency()和SetRTPReceiverPSMap()。如果是PCMA的,还是按照老的逻辑处理即可。

总结

GB28181设备接入这块,遇到的国标平台侧的问题真的是五花八门,真是印证了那句话:GB28181相关的模块,做demo容易,做产品,真的太难了,怪不得这么多公司懒得搞这块。

Android平台GB28181设备接入端语音广播支持PS格式相关推荐

  1. Android平台GB28181设备接入端如何支持跨网段语音对讲

    技术背景 如果你是音视频开发者亦或寻求这块技术方案的公司,在探讨这个问题之前,你可能网上看了太多关于语音广播和语音对讲相关的资料,大多文章认为语音对讲和语音广播无本质区别,实现思路也大同小异. 今天我 ...

  2. Android平台GB28181设备接入模块之球机/云台控制探究

    技术背景 好多开发者在做GB28181设备接入的时候,问云台控制是否可以处理(亦或拉取外部RTSP摄像头,通过命令中转的方式,控制摄像头),实际上云台控制命令相对来说还是比较好处理的.协议规范有明确说 ...

  3. 如何实现Android平台GB28181前端设备接入

    技术背景 在实现Android平台GB28181前端设备接入之前,我们几年前就有了非常成熟的RTMP推送.RTSP推送和轻量级RTSP服务等模块,特别是RTMP推送,行业内应用非常广泛,好多开发者可能 ...

  4. Android平台GB28181接入模块技术接入说明

    技术背景 今天,我们主要讲讲Android平台GB28181接入模块的技术对接,Android平台GB28181接入模块设计的目的,可实现不具备国标音视频能力的 Android终端,通过平台注册接入到 ...

  5. Android国标接入端如何播放GB28181平台端语音广播数据

    GB28181语音广播这块,我们依据GB/T28181-2016针对流程和实例代码,做过详细的描述,本次主要是探讨下,广播数据过来后,如何处理. 鉴于我们之前有非常成熟的RTMP|RTSP低延迟播放模 ...

  6. Android平台GB28181接入端如何对接UVC摄像头?

    我们在对接Android平台GB28181接入的时候,有公司提出这样的需求,除了采集执法记录仪摄像头自带的数据外,还想通过执法记录仪采集外接UVC摄像头. 实际上,这块对我们来说有点炒冷饭了,不算新的 ...

  7. Python对阿里云物联网MQTT设备接入端开发

    文章目录 前言 一.快速搭建Python对阿里云物联网MQTT设备接入端代码实现 1. mqtt子应用下view.py 主文件 2. wsgi.py 二.具体使用介绍 1. 安装包 2. 设备认证,一 ...

  8. 物联网课程学习目标_CCS开放物联网平台知识课堂|教你轻松学习物联网平台硬件设备接入...

    Hello Everbaby CCS-IOT知识课堂上一节课为大家带来了<物联网知识普及>,这一节课我们将正式进入操作环节,学习物联网设备的接入与调试 . 由于世界上的物包罗万象,生产厂商 ...

  9. 阿里云物联网平台 > 设备接入 > 使用开放协议自主接入 > CoAP协议接入 >

    CoAP连接通信 更新时间:2020-08-27 17:20:05 编辑我的收藏 本页目录 基础流程 使用对称加密自主接入 使用DTLS自主接入 物联网平台支持CoAP协议连接通信.CoAP协议适用在 ...

最新文章

  1. js-FCC算法Smallest Common Multiple。找出两个参数和它们之间的连续数字的最小公倍数。...
  2. Oracle常用字段数据类型/to_char函数
  3. suse linux 10 ftp服务配置
  4. c语言解析字符串报文,传递字符串数组报文和解析
  5. php 数组对,PHP数组游标实现对数组的各种操作详解
  6. 计算机科学检索课题,文献检索报告课题.docx
  7. 2022年下半年软考报名时间汇总,最新版!
  8. GANs奇思妙想TOP10榜单
  9. Java学习总结第一阶段
  10. 【基于MapReduce的成绩分析系统】——计算每门课程的平均成绩、最高成绩、最低成绩
  11. 二次开发crmeb增加实名认证 20220331
  12. 智能晾衣架(一)--设计
  13. 现代质量工程第五、六、八章课后习题答案
  14. 每日三个笑话-201510116
  15. objectarx + QT
  16. App Cleaner Uninstaller for Mac v7.6 应用卸载工具
  17. sql实现--根据不同类型的时间戳按24小时每小时统计数据,并补齐数据和取整
  18. [贪心算法]Leetcode738. 单调递增的数字
  19. 财务报销流程培训课件PPT模板
  20. 一个屌丝程序猿的人生(二十五)

热门文章

  1. python写迷你小游戏下载_狼人杀?用Python自己写个迷你版狼人杀游戏,自己的游戏自己做主...
  2. 数据分析IJCAI 2020:人工智能女神拥抱认知时代
  3. comsol如何设置距离梯度_常用的COMSOL操作符和数学函数
  4. IBM开源4.4万行blockchain代码,推动区块链物联网商用开发
  5. 计算机网络基础:5 局域网技术
  6. error Couldn‘t find a package.json file in报错的解决方案
  7. 中华活页文选杂志中华活页文选杂志社中华活页文选编辑部2023年第12期目录
  8. 拒绝了一家港资服装厂的邀请
  9. 数据科学家之路 实战2---Kaggle房价预测
  10. 机器学习需要离散数学吗?离散数学有什么用?