安卓GB28181设备语音广播和语音对讲(Android GB28181 语音广播和语音对讲)实现
安卓GB28181语音广播和语音对讲是一个非常重要的功能,很多场景都需要。语音对讲需要安卓有接收语音广播的功能。安卓还需要采集音频,并和视频一起打包到PS传给服务器,采集音频传服务器实现较容易。关键还是语音广播功能实现,语音广播的GB28181流程如下:
1. SIP服务器发MESSAGE语音广播命令到安卓端:
<?xml version="1.0" encoding="GB2312"?><Notify><CmdType>Broadcast</CmdType><SN>679389313</SN><SourceID>31010600002000000001</SourceID><TargetID>31010600001380000001</TargetID></Notify>
2. 安卓端发MESSAGE消息应答语音广播命令:
<?xml version="1.0" encoding="GB2312"?><Response><CmdType>Broadcast</CmdType><SN>679389313</SN><DeviceID>31010600001380000001</DeviceID><Result>OK</Result></Response>
3.安卓收到MESSAGE响应200OK后,发送INVITE消息,SDP:
v=0
o=31010600002000000001 0 0 IN IP4 192.168.0.100
s=Play
c=IN IP4 192.168.0.100
t=0 0
m=audio 25000 TCP/RTP/AVP 8
a=setup:active
a=connection:new
a=recvonly
a=rtpmap:8 PCMA/8000
y=0200005724
f=v/a/1/8/1
4.安卓端收到INVITE 200OK响应,SDP:
v=0
o=31010600002000000001 0 0 IN IP4 192.168.0.105
s=Play
c=IN IP4 192.168.0.105
t=0 0
m=audio 40062 TCP/RTP/AVP 8
a=sendonly
a=rtpmap:8 PCMA/8000
a=setup:passive
a=connection:new
y=0200005724
f=v/a/1/8/1
安卓回复ACK后, 开始读取RTP包,解析RTP包,对音频数据解码,输出到安卓播放设备即可, 流程不是太复杂,但实现细节挺繁琐的,下面是我实现的接口和demo代码:
语音广播信令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 statusCode, PlaySessionDescription sessionDescription);/** 音频广播, 收到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 result);/**语音广播接收者发送Invite消息*@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 localPort, String mediaTransportProtocol);/**取消音频广播,具体参考RFC3261*/boolean cancelAudioBroadcast(String sourceID, String targetID);/**终止语音广播会话, 发送BYE消息*/boolean byeAudioBroadcast(String sourceID, String targetID);
}
RTP音频包接收和解码输出接口:
package com.daniulive.smartplayer;public class SmartPlayerJniV2 {
/*** Initialize Player(启动播放实例)** @param ctx: get by this.getApplicationContext()** <pre>This function must be called firstly.</pre>** @return player handle if successful, if return 0, which means init failed. */public native long SmartPlayerOpen(Object ctx);/*** Set External Audio Output(设置回调PCM数据)** @param handle: return value from SmartPlayerOpen()** @param external_audio_output: External Audio Output** @return {0} if successful*/public native int SmartPlayerSetExternalAudioOutput(long handle, Object external_audio_output);/*** Set Audio Data Callback(设置回调编码后音频数据)** @param handle: return value from SmartPlayerOpen()** @param audio_data_callback: Audio Data Callback.** @return {0} if successful*/public native int SmartPlayerSetAudioDataCallback(long handle, Object audio_data_callback);/*** Set buffer(设置缓冲时间,单位:毫秒)** @param handle: return value from SmartPlayerOpen()** @param buffer:** <pre> NOTE: Unit is millisecond, range is 0-5000 ms </pre>** @return {0} if successful*/public native int SmartPlayerSetBuffer(long handle, int buffer);/*** Set mute or not(设置实时静音)** @param handle: return value from SmartPlayerOpen()** @param is_mute: if with 1:mute, if with 0: does not mute** @return {0} if successful*/public native int SmartPlayerSetMute(long handle, int is_mute);/*** 设置播放音量** @param handle: return value from SmartPlayerOpen()** @param volume: 范围是[0, 100], 0是静音,100是最大音量, 默认是100** @return {0} if successful*/public native int SmartPlayerSetAudioVolume(long handle, int volume);/*** 清除所有 rtp receivers** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerClearRtpReceivers(long handle);/*** 增加 rtp receiver** @param handle: return value from SmartPlayerOpen()** @param rtp_receiver_handle: return value from CreateRTPReceiver()** @return {0} if successful*/public native int SmartPlayerAddRtpReceiver(long handle, long rtp_receiver_handle);/*** 设置需要播放或录像的RTMP/RTSP url** @param handle: return value from SmartPlayerOpen()** @param uri: rtsp/rtmp playback/recorder uri** @return {0} if successful*/public native int SmartPlayerSetUrl(long handle, String uri);/*** Start playback stream(开始播放)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStartPlay(long handle);/*** Stop playback stream(停止播放)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStopPlay(long handle);/*** Start pull stream(开始拉流,用于数据转发,只拉流不播放)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStartPullStream(long handle);/*** Stop pull stream(停止拉流)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStopPullStream(long handle);/*** 关闭播放实例,结束时必须调用close接口释放资源** @param handle: return value from SmartPlayerOpen()** <pre> NOTE: it could not use player handle after call this function. </pre> ** @return {0} if successful*/public native int SmartPlayerClose(long handle);/*++++++++++++++++++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++++++++++++++++++++++*/}
具体的Demo调用代码如下(这里只给出和音频广播相关代码):
public class AndroidGB28181Demo implements GBSIPAgentListener {private String gb_source_id_ = null;private String gb_target_id_ = null;private long player_handle_ = 0;private long rtp_receiver_handle_ = 0;private AtomicLong last_receive_audio_data_time_ = new AtomicLong(0);@Overridepublic void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {if (gb28181_agent_ != null ) {gb28181_agent_.respondBroadcastCommand(from_user_name_, from_user_name_at_domain_,sn_,source_id_, target_id_, true);}}private String from_user_name_;private String from_user_name_at_domain_;private String sn_;private String source_id_;private String target_id_;public Runnable set(String from_user_name, String from_user_name_at_domain, String sn, String source_id, String target_id) {this.from_user_name_ = from_user_name;this.from_user_name_at_domain_ = from_user_name_at_domain;this.sn_ = sn;this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(fromUserName, fromUserNameAtDomain, sn, sourceID, targetID),0);}@Overridepublic void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {stopAudioPlayer();destoryRTPReceiver();if (gb28181_agent_ != null ) {String local_ip_addr = IPAddrUtils.getIpAddress(context_);boolean is_tcp = true; // 默认用TCPrtp_receiver_handle_ = lib_player_.CreateRTPReceiver(0);if (rtp_receiver_handle_ != 0 ) {lib_player_.SetRTPReceiverTransportProtocol(rtp_receiver_handle_, is_tcp?1:0);lib_player_.SetRTPReceiverIPAddressType(rtp_receiver_handle_, 0);if (0 == lib_player_.CreateRTPReceiverSession(rtp_receiver_handle_, 0) ) {int local_port = lib_player_.GetRTPReceiverLocalPort(rtp_receiver_handle_);boolean ret = gb28181_agent_.inviteAudioBroadcast(command_from_user_name_,command_from_user_name_at_domain_,source_id_, target_id_, "IP4", local_ip_addr, local_port, is_tcp?"TCP/RTP/AVP":"RTP/AVP");if (!ret ) {destoryRTPReceiver();}} else {destoryRTPReceiver();}}}}private String command_from_user_name_;private String command_from_user_name_at_domain_;private String source_id_;private String target_id_;public Runnable set(String command_from_user_name, String command_from_user_name_at_domain, String source_id, String target_id) {this.command_from_user_name_ = command_from_user_name;this.command_from_user_name_at_domain_ = command_from_user_name_at_domain;this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(commandFromUserName, commandFromUserNameAtDomain, sourceID, targetID),0);}@Overridepublic void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {destoryRTPReceiver();}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0);}@Overridepublic void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {destoryRTPReceiver();}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0);}class PlayerExternalPCMOutput implements NTExternalAudioOutput {private int buffer_size_ = 0;private ByteBuffer pcm_buffer_ = null;@Overridepublic ByteBuffer getPcmByteBuffer(int size) {if(size < 1)return null;if(buffer_size_ != size) {buffer_size_ = size;pcm_buffer_ = ByteBuffer.allocateDirect(buffer_size_);}return pcm_buffer_;}public void onGetPcmFrame(int ret, int sampleRate, int channel, int sampleSize, int is_low_latency) {if (null == pcm_buffer_)return;pcm_buffer_.rewind();if (ret == 0 && isGB28181StreamRunning && publisherHandle != 0 )// 传给发送端做音频相关处理libPublisher.SmartPublisherOnFarEndPCMData(publisherHandle, pcm_buffer_, sampleRate, channel, sampleSize, is_low_latency);}}class PlayerAudioDataOutput implements NTAudioDataCallback {private int buffer_size_ = 0;private int param_info_size_ = 0;private ByteBuffer buffer_ = null;private ByteBuffer parameter_info_ = null;@Overridepublic ByteBuffer getAudioByteBuffer(int size) {if( size < 1 ) return null;if (size <= buffer_size_ && buffer_ != null )return buffer_;buffer_size_ = align(size + 256, 16);buffer_ = ByteBuffer.allocateDirect(buffer_size_);return buffer_;}@Overridepublic ByteBuffer getAudioParameterInfo(int size) {if(size < 1) return null;if ( size <= param_info_size_ && parameter_info_ != null )return parameter_info_;param_info_size_ = align(size + 32, 16);parameter_info_ = ByteBuffer.allocateDirect(param_info_size_);return parameter_info_;}public void onAudioDataCallback(int ret, int audio_codec_id, int sample_size, int is_key_frame, long timestamp, int sample_rate, int channel, int parameter_info_size, long reserve) {last_receive_audio_data_time_.set(SystemClock.elapsedRealtime());}}class AudioPlayerDataTimer implements Runnable {public static final int THRESHOLD_MS = 60*1000; public static final int INTERVAL_MS = 10*1000; public AudioPlayerDataTimer(long handle) {handle_ = handle;}@Overridepublic void run() {if (0 == handle_)return;if (handle_ != player_handle_)return;long last_update_time = last_receive_audio_data_time_.get();long cur_time = SystemClock.elapsedRealtime();if ( (last_update_time + this.THRESHOLD_MS) > cur_time) {// 继续定时器handler_.postDelayed(new AudioPlayerDataTimer(this.handle_), this.INTERVAL_MS);}else {if (gb_source_id_!= null && gb_target_id_ != null) {if (gb28181_agent_ != null)gb28181_agent_.byeAudioBroadcast(gb_source_id_, gb_target_id_);}gb_source_id_= null;gb_target_id_ = null;stopAudioPlayer();destoryRTPReceiver();}}private long handle_;}private boolean startAudioPlay() {if (player_handle_ != 0 )return false;player_handle_ = lib_player_.SmartPlayerOpen(context_);if (player_handle_ == 0)return false;// lib_player_.SetSmartPlayerEventCallbackV2(player_handle_,new EventHandePlayerV2());lib_player_.SmartPlayerSetBuffer(player_handle_, 0);lib_player_.SmartPlayerSetReportDownloadSpeed(player_handle_, 1, 10);lib_player_.SmartPlayerClearRtpReceivers(player_handle_);lib_player_.SmartPlayerAddRtpReceiver(player_handle_, rtp_receiver_handle_);lib_player_.SmartPlayerSetSurface(player_handle_, null);// lib_player_.SmartPlayerSetRenderScaleMode(player_handle_, 1);lib_player_.SmartPlayerSetAudioOutputType(player_handle_, 1);lib_player_.SmartPlayerSetMute(player_handle_, 0);lib_player_.SmartPlayerSetAudioVolume(player_handle_, 100);lib_player_.SmartPlayerSetExternalAudioOutput(player_handle_, new PlayerExternalPCMOutput());lib_player_.SmartPlayerSetUrl(player_handle_, "rtp://xxxxxxxxxxxxxxxxxxx");if (0 != lib_player_.SmartPlayerStartPlay(player_handle_)) {lib_player_.SmartPlayerClose(player_handle_);player_handle_ = 0;Log.e(TAG, "start audio paly failed");return false;}lib_player_.SmartPlayerSetAudioDataCallback(player_handle_, new PlayerAudioDataOutput());if (0 ==lib_player_.SmartPlayerStartPullStream(player_handle_) ) {// 启动定时器,长时间收不到音频数据,则停止播放,发送BYElast_receive_audio_data_time_.set(SystemClock.elapsedRealtime());handler_.postDelayed(new AudioPlayerDataTimer(player_handle_), AudioPlayerDataTimer.INTERVAL_MS);}return true;}private void stopAudioPlayer() {if (player_handle_ != 0 ) {lib_player_.SmartPlayerStopPullStream(player_handle_);lib_player_.SmartPlayerStopPlay(player_handle_);lib_player_.SmartPlayerClose(player_handle_);player_handle_ = 0;}}private void destoryRTPReceiver() {if (rtp_receiver_handle_ != 0) {lib_player_.UnInitRTPReceiver(rtp_receiver_handle_);lib_player_.DestoryRTPReceiverSession(rtp_receiver_handle_);lib_player_.DestoryRTPReceiver(rtp_receiver_handle_);rtp_receiver_handle_ = 0;}}@Overridepublic void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int statusCode, PlaySessionDescription sessionDescription) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {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 = session_description_.getAudioDescription();SDPRtpMapAttribute audio_attr = null;if (audio_des != null && audio_des.getRtpMapAttributes() != null && !audio_des.getRtpMapAttributes().isEmpty() )audio_attr = audio_des.getRtpMapAttributes().get(0);if ( audio_des != null && audio_attr != null ) {lib_player_.SetRTPReceiverSSRC(rtp_receiver_handle_, audio_des.getSSRC());int clock_rate = audio_attr.getClockRate();lib_player_.SetRTPReceiverPayloadType(rtp_receiver_handle_, audio_attr.getPayloadType(), audio_attr.getEncodingName(), 2, clock_rate);// 如果是PCMA, 会默认填采样率8000, 通道1, 其他音频编码需要手动填入// lib_player_.SetRTPReceiverAudioSamplingRate(rtp_receiver_handle_, 8000);// lib_player_.SetRTPReceiverAudioChannels(rtp_receiver_handle_, 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_source_id_ = source_id_;gb_target_id_ = target_id_;}}} 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 PlaySessionDescription session_description_;public Runnable set(String source_id, String target_id, int status_code, PlaySessionDescription 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);}@Overridepublic void ntsOnByeAudioBroadcast(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {gb_source_id_ = null;gb_target_id_ = null;stopAudioPlayer();destoryRTPReceiver();}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0);}@Overridepublic void ntsOnTerminateAudioBroadcast(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {gb_source_id_ = null;gb_target_id_ = null;stopAudioPlayer();destoryRTPReceiver();}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0);}
}
测试下来音频效果不错,从文档描述到代码实现,需要不少精力,更多问题可以联系qq: 1130758427, github
安卓GB28181设备语音广播和语音对讲(Android GB28181 语音广播和语音对讲)实现相关推荐
- Android平台GB28181设备接入端如何支持跨网段语音对讲
技术背景 如果你是音视频开发者亦或寻求这块技术方案的公司,在探讨这个问题之前,你可能网上看了太多关于语音广播和语音对讲相关的资料,大多文章认为语音对讲和语音广播无本质区别,实现思路也大同小异. 今天我 ...
- Android 四大组件 —— 广播(广播机制解析)
在网络通信中,一个IP网络范围中最大的IP 地址是被保留作为广播地址来使用的.比如某个网络的IP 范围是192.168.0.XXX,子网掩码是255.255.255.0,那么这个网络的广播地址就是19 ...
- Android学习笔记——广播机制
Android广播机制 为了便于进行系统级别的消息通知,Android引入了一套广播消息机制. Android中的广播机制十分灵活,每个程序都可以对自己感兴趣的广播进行注册.程序只会接收自己所关心的广 ...
- Android平台GB28181设备接入端语音广播支持PS格式
技术背景 对接Android平台GB28181设备接入端语音广播的时候,我们有遇到过INVITE SDP需要PCMA格式的audio,对方同时回了PS和PCMA两种,然后,发数据的时候,直接发了PS的 ...
- GB28181 安卓移动设备位置上报实现(订阅和通知实现)
GB28181中事件订阅和通知机制是基于RFC3265 中的SIP扩展方法SUBSCRIBE和NOTIFY实现的.代码实现之前,先了解下相关协议. 这里先简单说明下RFC3265: 1.SUBSCRI ...
- 安卓设备接入GB28181(Android GB28181)
最近对安卓现有代码进行了扩展,支持了GB28181, GB28181协议实现分两块,一块是信令部分,一块是流媒体数据传输.代码分两部分: 信令接口: public interface GBSIPAge ...
- android分享助手下载地址,语音导出分享助手手机版|语音导出分享助手安卓版下载 v2.1 - 跑跑车安卓网...
语音导出分享助手是一款语音导出类的软件,这款软件可为需要的用户提供优质的语音服务,满足你的日常需求,快来下载吧! 软件介绍 还在为手机中语音不能分享而烦恼?还在为昔日误删好友而后悔获取不到两人往日的语 ...
- 安卓Intent的Action中的常值变量:窗口action常量(android.intent.action.+xxx),广播action常量(android.intent.action.+xxx)
全栈工程师开发手册 (作者:栾鹏) 安卓教程全解 安卓Intent的Action中的常值变量:窗口action常量,广播action常量 窗口action名称常量,"android.inte ...
- 国标GB28181设备终端(国标摄像机、国标单兵设备)实现方案流程概述
国标GB/T 28181协议的逐步普及,解决了海康.大华.宇视等各大厂家设备协议统一的问题,尤其是在主动注册到平台这一块的协议上,非常好地解决了所有有线.无线.4G.5G设备的统一接入协议的问题,GB ...
最新文章
- 澳洲计算机学,2020年澳洲计算机科学专业工作好找吗
- 为什么不建议用try catch处理异常?
- wxwidgets mysql_wxWidgets导入Excel文件详细教程
- python matplotlib plt 画图 将刻度 替换为文字/字符以及画断断续续的分段函数
- 老子学不动系列:Vue 3.0 新特性预览
- 二分查找 —— 有序数组不小于(不大于)某数的第一个(最后一个)元素
- 注册 aspnet_regiis
- iconv-----linux gbk 转 UTF-8
- 对我有利就契约,不利就废纸
- 命令调出本地链接_大牛进化路上之Linux基础命令,看看你了解多少?
- Allegro导入Altium Designer的pcb文件
- 在python中无论类的名字是什么构造方法的名字都是_超星尔雅大学生职业素养期末答案...
- 《微电子概论》2.1 理论基础
- 简明C语言教程(七)scanf 用法
- Web地图服务、WMS 请求方式、网络地图服务(WMS)的三大操作
- 阿里云服务器apt install 出错怎么办?出现Package gdb is not available, but is referred to by another package怎么办
- ET框架学习——消息系统之二
- 极简步骤试玩Ant Design Pro myapp demo
- 测试用例-微信发红包
- 生成式对抗网络(GAN)原理推导与网络构建思路
热门文章
- 阿里IOT云平台(二)---10分钟物联网设备接入阿里云IoT平台
- 微信老外产品经理:《中国移动应用设计趋势》
- 钉钉考勤接口调用与OA系统数据对接(多线程版)
- 深度学习笔记(四十一)循环神经网络及正反向传播
- JVM和DVM的区别
- python图像处理:核磁共振图像(3D)的缩放
- java跳转kotlin页面_Kotlin:return与跳转
- mysql按月份分组查询_mysql如何按月份分组查询
- CTFshow-萌新
- php6基因突变,基因突变中那些“披着狼皮的羊” 很多“致命性”基因突变正在被证实无害...