最近对安卓现有代码进行了扩展,支持了GB28181, GB28181协议实现分两块,一块是信令部分,一块是流媒体数据传输。代码分两部分:

信令接口:

public interface GBSIPAgent {void addListener(GBSIPAgentListener listener);/** 设置SIP本地链接地址* @param address 本地IP地址, 如192.168.0.111* @param port本地SIP端口, 如 15070*/void setLocalAddressInfo(String address, int port);/** 设置SIP Server配置参数* @param address SIP服务器地址, 如 192.168.0.101* @param port SIP服务器端口, 如 15060* @param id SIP服务器ID, 如 34020000002000000001* @param domain SIP服务器域, 如 3402000000*/void setServerParameter(String address, int port, String id, String domain);/** 设置GB28181 SIP User配置参数* @param userName SIP用户名, 如 34020000001110000045* @param password 密码, 如 123456*/void setUserInfo(String userName, String password);/** 设置SIP请求头中的UserAgent* @param userAgent用户代理*/void setUserAgent(String userAgent);/** 设置SIP传输协议* @param transport_protocol, 设置SIP信令传输协议: UDP, TCP, 默认是UDP*/void setTransportProtocol(String transport_protocol);/** 设置GB28181配置参数* @param regExpired 注册有效期, 单位: 秒, 如 3600* @param heartBeatInterval 心跳间隔, 单位: 秒, 默认60* @param heartBeatCount 心跳超时次数, 默认3次*/void config(int regExpired, int heartBeatInterval, int heartBeatCount);void clearDevices();boolean addDevice(Device device);boolean initialize();/**启动*/boolean start();boolean isRunning();/**响应Invite play 200OK*/boolean respondPlayInviteOK(String deviceId, String localAddress, int localPort);/**响应Invite play其他状态码*/boolean respondPlayInvite(int statusCode, String deviceId);/**终止会话*/void terminatePlay(String deviceId, boolean isSendBYE);/**终止所有会话*/void terminateAllPlays(boolean isSendBYE);/**停止*/void stop();void unInitialize();
}

信令Listener:

public interface GBSIPAgentListener
{/*注册成功* @param dateString: 服务器日期,用来校准设备端时间,用户自行决定是否校准设备时间*/void ntsRegisterOK(String dateString);/**注册超时*/void ntsRegisterTimeout();/**注册网络传输曾异常*/void ntsRegisterTransportError(String errorInfo);/**心跳达到异常次数*/void ntsOnHeartBeatException(int exceptionCount, String lastExceptionInfo);/**收到s=Play的实时视音频点播*/void ntsOnInvitePlay(String deviceId, InvitePlaySessionDescription 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);/** Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发收到这个, 请做相关清理处理*/void ntsOnPlayDialogTerminated(String deviceId);
}

流媒体传输接口(只是和GB2818相关的部分接口, 其他接口未展示):

public class SmartPublisherJniV2 {/*** Open publisher(启动推送实例)** @param ctx: get by this.getApplicationContext()* * @param audio_opt:* if 0: 不推送音频* if 1: 推送编码前音频(PCM)* if 2: 推送编码后音频(aac/pcma/pcmu/speex).* * @param video_opt:* if 0: 不推送视频* if 1: 推送编码前视频(YUV420SP/YUV420P/RGBA/ARGB)* if 2: 推送编码后视频(H.264)** @param width: capture width; height: capture height.** <pre>This function must be called firstly.</pre>** @return the handle of publisher instance*/public native long SmartPublisherOpen(Object ctx, int audio_opt, int video_opt,  int width, int height);/*+++++++++++++++RTP Sender相关接口+++++++++++++++*//** 创建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 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 Sender相关接口+++++++++++++++*//*+++++++++++++++GB28181相关接口+++++++++++++++*//*** 设置GB28181 RTP Sender** @param rtp_sender_handle, CreateRTPSender返回值* @param rtp_payload_type, 对于GB28181 PS, 协议定义是96, 具体以SDP为准** @return {0} if successful*/public native int SetGB28181RTPSender(long handle, long rtp_sender_handle, int rtp_payload_type);/*** 启动 GB28181 媒体流** @return {0} if successful*/public native int StartGB28181MediaStream(long handle);/*** 停止 GB28181 媒体流** @return {0} if successful*/public native int StopGB28181MediaStream(long handle);/*---------------GB28181相关接口---------------*//*** 关闭推送实例,结束时必须调用close接口释放资源** @return {0} if successful*/public native int SmartPublisherClose(long handle);
}

下面是Demo相关调用代码:

public class GB28181Demo extends Activity, implements Callback, PreviewCallback, GBSIPAgentListener
{/*** GB28181 相关参数,可以修改相关参数后测试 ***/GBSIPAgent     gb28181_agent_             = null;private int    gb28181_sip_local_port_    = 12070;private String gb28181_sip_server_id_     = "34020000002000000001";private String gb28181_sip_server_domain_ = "3402000000";private String gb28181_sip_server_addr_   = "192.168.0.104";private int    gb28181_sip_server_port_   = 15060;private String gb28181_sip_user_agent_filed_  = "NT GB2818 User Agent V1.0";private String gb28181_sip_username_   = "31011500991320000069";private String gb28181_sip_password_   = "12345678";private int gb28181_reg_expired_           = 3600; // 注册有效期时间最小3600秒private int gb28181_heartbeat_interval_    = 20; // 心跳间隔GB28181默认是60, 目前调整到20秒private int gb28181_heartbeat_count_       = 3; // 心跳间隔3次失败,表示和服务器断开了private int gb28181_sip_trans_protocol_    = 0; // 0表示信令用UDP传输, 1表示信令用TCP传输private long gb28181_rtp_sender_handle_ = 0;private int  gb28181_rtp_payload_type_  = 96;/*** GB28181 相关参数,可以修改相关参数后测试 ***/@Overridepublic void onCreate(Bundle savedInstanceState) {/*其他初始化代码**/libPublisher = new SmartPublisherJniV2();if ( initGB28181Agent() ) {gb28181_agent_.start();}}private boolean initGB28181Agent(){if ( gb28181_agent_ != null )return  true;String local_ip_addr = IPAddrUtils.getIpAddress(myContext);Log.i(TAG, "initGB28181Agent local ip addr: " + local_ip_addr);if ( local_ip_addr == null || local_ip_addr.isEmpty() ) {Log.e(TAG, "initGB28181Agent local ip is empty");return  false;}gb28181_agent_ = GBSIPAgentFactory.getInstance().create();if ( gb28181_agent_ == null ) {Log.e(TAG, "initGB28181Agent create agent failed");return false;}gb28181_agent_.addListener(this);// 必填信息gb28181_agent_.setLocalAddressInfo(local_ip_addr, gb28181_sip_local_port_);gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_server_domain_);gb28181_agent_.setUserInfo(gb28181_sip_username_, gb28181_sip_password_);// 可选参数gb28181_agent_.setUserAgent(gb28181_sip_user_agent_filed_);gb28181_agent_.setTransportProtocol(gb28181_sip_trans_protocol_==0?"UDP":"TCP");// GB28181配置gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);com.gb28181.ntsignalling.Device gb_device = new com.gb28181.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,"宇宙","火星1","火星", true);getLocation(this);gb_device.setLongitude(mLongitude);gb_device.setLatitude(mLatitude);gb28181_agent_.addDevice(gb_device);if (!gb28181_agent_.initialize()) {gb28181_agent_ = null;Log.e(TAG, "initGB28181Agent gb28181_agent_.initialize failed.");return  false;}return true;}@Overridepublic void ntsRegisterOK(String dateString) {Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));}@Overridepublic void ntsRegisterTimeout() {Log.e(TAG, "ntsRegisterTimeout");}@Overridepublic void ntsRegisterTransportError(String errorInfo) {Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));}@Overridepublic void ntsOnHeartBeatException(int exceptionCount,  String lastExceptionInfo) {Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:" + exceptionCount+", exception info:" + (lastExceptionInfo!=null?lastExceptionInfo:""));// 10毫秒后,停止信令, 然后重启handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "gb28281_heart_beart_timeout");stopGB28181Stream();destoryRTPSender();if (gb28181_agent_ != null) {Log.i(TAG, "gb28281_heart_beart_timeout sip stop");gb28181_agent_.stop();Log.i(TAG, "gb28281_heart_beart_timeout sip start");gb28181_agent_.start();}}},10);}@Overridepublic void ntsOnInvitePlay(String deviceId, InvitePlaySessionDescription session_des) {handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG,"ntsInviteReceived, device_id:" +device_id_+", is_tcp:" + session_des_.isRTPOverTCP()+ " rtp_port:" + session_des_.getMediaPort() + " ssrc:" + session_des_.getSSRC()+ " address_type:" + session_des_.getAddressType() + " address:" + session_des_.getAddress());// 可以先给信令服务器发送临时振铃响应//sip_stack_android.respondPlayInvite(180, device_id_);long rtp_sender_handle = libPublisher.CreateRTPSender(0);if ( rtp_sender_handle == 0 ) {gb28181_agent_.respondPlayInvite(488, device_id_);Log.i(TAG, "ntsInviteReceived CreateRTPSender failed, response 488, device_id:" + device_id_);return;}gb28181_rtp_payload_type_ = session_des_.getPSRtpMapAttribute().getPayloadType();libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, session_des_.isRTPOverUDP()?0:1);libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, session_des_.isIPv4()?0:1);libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);libPublisher.SetRTPSenderSSRC(rtp_sender_handle, session_des_.getSSRC());libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 1024*1024);libPublisher.SetRTPSenderClockRate(rtp_sender_handle, session_des_.getPSRtpMapAttribute().getClockRate());libPublisher.SetRTPSenderDestination(rtp_sender_handle, session_des_.getAddress(), session_des_.getMediaPort());if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {gb28181_agent_.respondPlayInvite(488, device_id_);libPublisher.DestoryRTPSender(rtp_sender_handle);return;}int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);if (local_port == 0) {gb28181_agent_.respondPlayInvite(488, device_id_);libPublisher.DestoryRTPSender(rtp_sender_handle);return;}Log.i(TAG,"get local_port:" + local_port);String local_ip_addr = IPAddrUtils.getIpAddress(myContext);gb28181_agent_.respondPlayInviteOK(device_id_,local_ip_addr, local_port);gb28181_rtp_sender_handle_ = rtp_sender_handle;}private String device_id_;private InvitePlaySessionDescription session_des_;public Runnable set(String device_id, InvitePlaySessionDescription session_des) {this.device_id_ = device_id;this.session_des_ = session_des;return this;}}.set(deviceId, session_des),0);}@Overridepublic void ntsOnCancelPlay(String deviceId) {// 这里取消Play会话handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnCancelPlay, deviceId=" + device_id_);destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnAckPlay(String deviceId) {handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);InitAndSetConfig();libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_);int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);if (startRet != 0) {if (publisherHandle != 0) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}destoryRTPSender();return;}isGB28181StreamRunning = true;}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {// 这里要释放掉响应的资源Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + deviceId + " statusCode=" +statusCode+ " errorInfo:" + errorInfo);handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + device_id_);destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnByePlay(String deviceId){handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnByePlay, stop GB28181 media stream, deviceId=" + device_id_);stopGB28181Stream();destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnPlayDialogTerminated(String deviceId) {/*Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发收到这个请做相关清理处理*/handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnPlayDialogTerminated, deviceId=" + device_id_);stopGB28181Stream();destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}//停止GB28181 媒体流private void stopGB28181Stream() {if(!isGB28181StreamRunning)return;if (libPublisher != null) {libPublisher.StopGB28181MediaStream(publisherHandle);}if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}isGB28181StreamRunning = false;}private void destoryRTPSender() {if (gb28181_rtp_sender_handle_ != 0) {libPublisher.DestoryRTPSender(gb28181_rtp_sender_handle_);gb28181_rtp_sender_handle_ = 0;}}@Overrideprotected void onDestroy() {Log.i(TAG, "activity destory!");if (gb28181_agent_ != null ) {if (gb28181_agent_.isRunning()) {gb28181_agent_.terminateAllPlays(false);gb28181_agent_.stop();}gb28181_agent_ = null;}stopGB28181Stream();destoryRTPSender();if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}super.onDestroy();}}

音视频采集编码相关功能已经稳定运行多年了,GB28181接入用C++实现H264, H265打包成PS流,然后拆成RTP包发送即可,RTP传输同时支持UDP和TCP(这个根据Invite sdp来确定)。配合28181服务器测试,延时非常低,达到了预期的效果.

当前实现接口也支持多个设备(或者说是多个通道), 可以很方便的实现多个现有rtsp摄像头到GB28181的转换,另外考虑到安卓移动端的特性,也增加了对设备经纬度位置信息的上报. 更多可以参考github

安卓设备接入GB28181(Android GB28181)相关推荐

  1. 宇视摄像机/硬盘录像机等设备接入到国标GB28181协议视频平台EasyGBS的注意事项

    经常有用户咨询宇视的现场对接TSINGSEE青犀视频云边端架构中,国标GB28181协议视频平台EasyGBS需要注意哪些事项,本文我们就在这里整理一下,给大家介绍一下宇视设备接入到国标平台的注意事项 ...

  2. WVP+ZLMediaKit实现NAT穿透推流播放录制,支持IPC、NVR、DVR等设备接入,支持GB28181国标级联

    前言 好久没有更新了,一个是没有时间,二个写东西可能更多讲究的是一个时机,半熟不熟的领域怕写错,太熟的领域又不敢写,只有刚接触的东西才敢乱写. 也罢,正应了那句初生牛犊不畏虎.言归正传,该篇内容主要记 ...

  3. GB/T28181-2016传输要求和Android平台设备接入技术实现

    相关协议规范 GB/T28181-2016公共安全视频监控联网系统 信息传输.交换.控制技术要求相关的传输要求如下: 5.1 网络传输协议要求 联网系统网络层应支持IP协议,传输层应支持 TCP和 U ...

  4. Sonic 开源移动端云真机测试平台 - 设备中心接入安卓设备实例演示,Agent端服务部署过程详解

    Sonic 开源移动端云真机测试平台 - Agent端服务部署与安卓设备接入演示 一加8手机连接效果图展示 第一章:环境准备 ① agent-sources 资源包下载 ② Android SDK安装 ...

  5. android aoa 串口,51单片机安卓AOA协议和Android设备USB通讯实例

    具体如下: A.等待安卓设备连接: B.确定手机支持附属模式(配件模式 ): 当安卓设备接入USB主机时,一定处于以下3种模式之一: *a.支持附属模式,并且已经在处于这种模式中: *b.支持附属模式 ...

  6. 海康监控摄像机接入NTV GBS GB28181平台实现远程调取监控视频

    海康威视各种型号监控摄像头或硬盘录像机(NVR/HVR)接入NTV GBS GB28181平台配置过程都非常简单明了,但有些细节需要注意,避免走弯路踩泥坑. 1.基本要求 1)网络要求 总体来说,只要 ...

  7. Sonic测试平台搭建--设备接入

    前言 上一篇:Sonic开源的云真机测试平台搭建记录 1.设备接入 1.1 安装npm 在NodeJS官网下载nodejs进行安装,安装时会自动添加环境变量,安装完成后查看npm的版本:如果提示不是n ...

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

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

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

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

最新文章

  1. dev编译器:c++如何让其输出小数16.84,浮点数类型数据!
  2. 101个脚本之linux回收站
  3. 谱减法降噪的matlab代码实现
  4. Spring Boot 项目打包 + Shell 脚本部署实践,太有用了!
  5. 使用正则表达式模拟读写INI文件
  6. u盘安装linux启动报错,U盘安装centos7,启动报错
  7. IntelliJ IDEA创建spring boot项目的方法
  8. 左边替换 oracle,sqlsever替换右边第4个字符
  9. 卸载pip、setuptools
  10. 图像分割中dc_loss忽视标签实现
  11. 2019斯坦福CS224n深度学习自然语言处理笔记(1)Word2Vec与Glove
  12. vba有下拉框的模糊查找_巧用数据验证制作模糊匹配的下拉列表
  13. 2018-08-31 基于CSS3D视角,实现视差滚动
  14. 发布微信小程序体验版
  15. qt.qpa.plugin:Cound not load the QT platform plugin “windows“ in “ “even though it was found.
  16. “人类高质量数据”如何训练计算机视觉模型?
  17. 百度地图api将可视区域定位到当前所在位置
  18. 微信小程序,高德地图
  19. 【kronecker积rao积,及其它矩阵运算的一些性质】
  20. 和风天气 (简易版)

热门文章

  1. Netflix大奖数据
  2. 手写识别+.mat格式+图片转为mnist+mnist转为.mat文件
  3. GMT 6.0绘制指针图/GMT绘制指南针/指北针
  4. Java LocalDate
  5. [组原]《程序是怎样运行的》 笔记
  6. php rename函数_PHP rename函数使用详解(php重命名文件)
  7. 转:眼部化妆:单眼皮变双眼皮
  8. 如何设计散列表(哈希表)
  9. JavaScript原型,原型链 ? 有什么特点?
  10. 修改imap服务器地址,如何设置imap服务器地址