今天,我们主要讲讲Android平台GB28181接入模块的技术对接,Android平台GB28181接入模块设计的目的,可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如智能监控、智慧零售、智慧教育、远程办公、生产运输、智慧交通、车载或执法记录仪等场景。

Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲、云台控制回调和预置位查询,支持对接数据类型如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" ></uses-permission>
<uses-permission android:name="android.permission.INTERNET" ></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
static {
System.loadLibrary("SmartPublisher");
System.loadLibrary("SmartPlayer");
}
splits {
abi {
enable true
reset()
// Specifies a list of ABIs that Gradle should create APKs for
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' //select ABIs to build APKs for
// Specify that we do not want to also generate a universal APK that includes all ABIs
universalApk true
}
}
<string name="app_name">SmartPublisherSDKDemo</string>
以Android平台Camera2对接为例,信令部分需要实现如下标红接口:
public class MainActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener, Camera2Listener,
GBSIPAgentListener, GBSIPAgentPlayListener, GBSIPAgentAudioBroadcastListener,
GBSIPAgentDeviceControlListener, GBSIPAgentQueryCommandListener, GBSIPAgentTalkListener{
}
媒体数据处理接口,可参照SmartPublisherJniV2.java,如需语音广播或语音对讲,可参照SmartPlayerJniV2.java。
GBSIPAgentListener主要系GB28181注册、心跳、DevicePosition等,如注册成功、注册超时、注册网络传输层错误、心跳异常、设备位置请求处理:
public interface GBSIPAgentListener
{
/*注册成功
* @param dateString: 服务器日期,用来校准设备端时间,用户自行决定是否校准设备时间
*/
void ntsRegisterOK(String dateString);
/*
*注册超时
*/
void ntsRegisterTimeout();
/*
*注册网络传输层异常
*/
void ntsRegisterTransportError(String errorInfo);
/*
*心跳达到异常次数
*/
void ntsOnHeartBeatException(int exceptionCount, String lastExceptionInfo);
/*
* 设备位置请求, 这个主要用在移动设备位置订阅上
* @param interval 请求间隔, 单位是毫秒
*/
void ntsOnDevicePositionRequest(String deviceId, int interval);
}
GBSIPAgentPlayListener主要系GB28181的Invite、Ack、Bye等处理:
public interface GBSIPAgentPlayListener {
/*
*收到s=Play的实时视音频点播
*/
void ntsOnInvitePlay(String deviceId, SessionDescription sessionDescription);
/*
*发送play invite response 异常
*/
void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo);
/*
* 收到CANCEL play INVITE请求
*/
void ntsOnCancelPlay(String deviceId);
/*
* 收到Ack
*/
void ntsOnAckPlay(String deviceId);
/*
* 收到Bye
*/
void ntsOnByePlay(String deviceId);
/*
* 不是在收到BYE Message情况下, 终止Play
*/
void ntsOnTerminatePlay(String deviceId);
/*
* Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发
收到这个, 请做相关清理处理
*/
void ntsOnPlayDialogTerminated(String deviceId);
}
GBSIPAgentAudioBroadcastListener主要系GB28181语音广播处理相关,如有语音广播相关需求,可参照demo实例实现:
public interface GBSIPAgentAudioBroadcastListener {
/*
*收到语音广播通知
*/
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, SessionDescription sessionDescription);
/*
* 音频广播, 收到BYE Message
*/
void ntsOnByeAudioBroadcast(String sourceID, String targetID);
/*
* 不是在收到BYE Message情况下, 终止音频广播
*/
void ntsOnTerminateAudioBroadcast(String sourceID, String targetID);
}
GBSIPAgentDeviceControlListener主要系GB28181设备控制相关,比如远程启动、云台控制:
public interface GBSIPAgentDeviceControlListener {
/*
* 收到远程启动控制命令
*/
void ntsOnDeviceControlTeleBootCommand(String deviceId, String teleBootValue);
/*
* 云台控制
*/
void ntsOnDeviceControlPTZCmd(String deviceId, String typeValue);
}
GBSIPAgentQueryCommandListener主要系GB28181查询命令,如预置位查询:
public interface GBSIPAgentQueryCommandListener {
/*
* 设备预置位查询
*/
void ntsOnDevicePresetQueryCommand(String fromUserName, String fromUserNameAtDomain, String sn, String deviceId);
}
GBSIPAgentTalkListener主要系GB28181语音对讲相关处理:
public interface GBSIPAgentTalkListener {
/*
*收到s=Talk 语音对讲
*/
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);
/*
* Talk会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发
收到这个, 请做相关清理处理
*/
void ntsOnTalkDialogTerminated(String deviceId);
}
RTP Sender(SmartPublisherJniV2.java)相关接口设计:
/*
* SmartPublisherJniV2.java
* Author: https://daniusdk.com
*/
/*
* 创建RTP Sender实例
*
* @param reserve:保留参数传0
*
* @return RTP Sender 句柄,0表示失败
*/
public native long CreateRTPSender(int reserve);
/**
*设置 RTP Sender传输协议
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param transport_protocol, 0:UDP, 1:TCP, 默认是UDP
*
* @return {0} if successful
*/
public native int SetRTPSenderTransportProtocol(long rtp_sender_handle, int transport_protocol);
/**
*设置 RTP Sender IP地址类型
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param ip_address_type, 0:IPV4, 1:IPV6, 默认是IPV4, 当前仅支持IPV4
*
* @return {0} if successful
*/
public native int SetRTPSenderIPAddressType(long rtp_sender_handle, int ip_address_type);
/**
*设置 RTP Sender RTP Socket本地端口
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param port, 必须是偶数,设置0的话SDK会自动分配, 默认值是0
*
* @return {0} if successful
*/
public native int SetRTPSenderLocalPort(long rtp_sender_handle, int port);
/**
*设置 RTP Sender SSRC
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param ssrc, 如果设置的话,这个字符串要能转换成uint32类型, 否则设置失败
*
* @return {0} if successful
*/
public native int SetRTPSenderSSRC(long rtp_sender_handle, String ssrc);
/**
*设置 RTP Sender RTP socket 发送Buffer大小
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param buffer_size, 必须大于0, 默认是512*1024, 当前仅对UDP socket有效, 根据视频码率考虑设置合适的值
*
* @return {0} if successful
*/
public native int SetRTPSenderSocketSendBuffer(long rtp_sender_handle, int buffer_size);
/**
*设置 RTP Sender RTP时间戳时钟频率
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param clock_rate, 必须大于0, 对于GB28181 PS规定是90kHz, 也就是90000
*
* @return {0} if successful
*/
public native int SetRTPSenderClockRate(long rtp_sender_handle, int clock_rate);
/**
*设置 RTP Sender 目的IP地址, 注意当前用在GB2818推送上,只设置一个地址,将来扩展如果用在其他地方,可能要设置多个目的地址,到时候接口可能会调整
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param address, IP地址
* @param port, 端口
*
* @return {0} if successful
*/
public native int SetRTPSenderDestination(long rtp_sender_handle, String address, int port);
/**
* 设置是否开启 RTP Receiver
* @param rtp_sender_handle, CreateRTPSender返回值
* @param is_enable, 0表示不收RTP包, 1表示收RTP包, SDK默认值为0.
* @return
*/
public native int EnableRTPSenderReceive(long rtp_sender_handle, int is_enable);
/**
*设置RTP Receiver SSRC
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param ssrc, 如果设置的话,这个字符串要能转换成uint32类型, 否则设置失败
*
* @return {0} if successful
*/
public native int SetRTPSenderReceiveSSRC(long rtp_sender_handle, String ssrc);
/**
*设置RTP Receiver Payload 相关信息
*
* @param rtp_sender_handle, CreateRTPSender返回值
*
* @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 SetRTPSenderReceivePayloadType(long rtp_sender_handle, int payload_type, String encoding_name, int media_type, int clock_rate);
/**
*设置RTP Receiver PS的pts和dts clock frequency
*
* @param rtp_sender_handle, CreateRTPSender返回值
*
* @param ps_clock_frequency, 默认是90000, 一些特殊场景需要设置
*
* @return {0} if successful
*/
public native int SetRTPSenderReceivePSClockFrequency(long rtp_sender_handle, int ps_clock_frequency);
/**
*设置 RTP Receiver 音频采样率
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param sampling_rate, 音频采样率
*
* @return {0} if successful
*/
public native int SetRTPSenderReceiveAudioSamplingRate(long rtp_sender_handle, int sampling_rate);
/**
*设置 RTP Receiver 音频通道数
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param channels, 音频通道数
*
* @return {0} if successful
*/
public native int SetRTPSenderReceiveAudioChannels(long rtp_sender_handle, int channels);
/**
*初始化RTP Sender, 初始化之前先调用上面的接口配置相关参数
*
* @param rtp_sender_handle, CreateRTPSender返回值
*
* @return {0} if successful
*/
public native int InitRTPSender(long rtp_sender_handle);
/**
*获取RTP Sender RTP Socket本地端口
*
* @param rtp_sender_handle, CreateRTPSender返回值
*
* @return 失败返回0, 成功的话返回响应的端口, 请在InitRTPSender返回成功之后调用
*/
public native int GetRTPSenderLocalPort(long rtp_sender_handle);
/**
* UnInit RTP Sender
*
* @param rtp_sender_handle, CreateRTPSender返回值
*
* @return {0} if successful
*/
public native int UnInitRTPSender(long rtp_sender_handle);
/**
* 释放RTP Sender, 释放之后rtp_sender_handle就无效了,请不要再使用
*
* @param rtp_sender_handle, CreateRTPSender返回值
*
* @return {0} if successful
*/
public native int DestoryRTPSender(long rtp_sender_handle);
对应RTP Receiver(SmartPlayerJniV2.java)相关接口设计,如无语音广播或语音对讲相关技术需求,这部分可忽略:
/*
* SmartPlayerJniV2.java
* Author: https://daniusdk.com
*/
/*
* 创建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);
PostAudioPacket(SmartPlayerJniV2.java),投递音频包给外部Live source,目前仅于语音对讲使用:
/*
* SmartPlayerJniV2.java
* Author: https://daniusdk.com
*/
/**
* 投递音频包给外部Live source, 注意ByteBuffer对象必须是DirectBuffer
*
* @param handle: return value from SmartPlayerOpen()
*
* @return {0} if successful
*/
public native int PostAudioPacket(long handle, int codec_id,
java.nio.ByteBuffer packet, int offset, int size, long pts, boolean is_pts_discontinuity,
java.nio.ByteBuffer extra_data, int extra_data_offset, int extra_data_size, int sample_rate, int channels);
GB28181接口调用
对应GB28181相关接口调用相关设计如下:
/*
* SmartPublisherJniV2.java
* Author: https://daniusdk.com
*/
/**
* 设置GB28181 RTP Sender
*
* @param rtp_sender_handle, CreateRTPSender返回值
* @param rtp_payload_type, 对于GB28181 PS, 协议定义是96, 具体以SDP为准, RFC 3551有定义
* @param encoding_name, 编码名, 请参考 RFC 3551, 当前仅支持: "PS", 其他值返回失败
* @return {0} if successful
*/
public native int SetGB28181RTPSender(long handle, long rtp_sender_handle, int rtp_payload_type, String encoding_name);
/**
* 设置GB28181 RTP 收到的音频包回调
* @param handle
* @param audio_packet_callback
* @return
*/
public native int SetGB28181ReceiveAudioPacketCallback(long handle, NTAudioPacketCallback audio_packet_callback);
/**
* 启动 GB28181 媒体流
*
* @return {0} if successful
*/
public native int StartGB28181MediaStream(long handle);
/**
* 停止 GB28181 媒体流
*
* @return {0} if successful
*/
public native int StopGB28181MediaStream(long handle);
以上是大牛直播SDK发布的Android平台GB28181设备接入模块的相关说明,除了上述接口设计外,模块还可以扩展实现实时静音、实时快照、按需录像、实时音量调节等,可扩展性非常好。
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里编辑 3.解析依赖到项目中
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO