好多开发者在做国标对接的时候,首先想到的是IPC摄像头,通过参数化配置,接入到国标平台,实现媒体数据的按需查看等操作。
像执法记录仪等智能终端,跑在Android平台,对接GB28181平台的需求也非常大,网上相关demo也不少,但真正设计符合相关协议规范、功能完善、长时间稳定运行的并不多。基于此,我们研发了Android平台GB28181接入模块,目前功能设计,总的来说,IPC有的功能要有,IPC缺失的,但是对业务诉求有要求的,也要有,比如MobilePosition实时位置上报,实时快照、本地录像、屏幕采集、拉取RTSP或RTMP对接到GB28181平台、实时动态水印、实时音量调节、外部编码前后对接等:
接口设计这块,我们分三个部分:一个是供Publisher实例调用的接口,一个是RTP发送相关的接口,还有一个是针对语音广播这块,实现的接收RTP数据播放接口。
先说供GB28181推送实例调用的接口:
- /*+++++++++++++++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相关接口---------------*/
再说RTP发送相关的接口:
- /*
- * SmartPublisherJniV2.java
- * SmartPublisherJniV2
- *
- * WebSite: https://daniusdk.com
- * Github: https://github.com/daniulive/SmarterStreaming
- */
-
- /*+++++++++++++++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接收相关的接口设计:
- /*
- * SmartPlayerJniV2.java
- * SmartPlayerJniV2
- *
- * WebSite: https://daniusdk.com
- * Github: https://github.com/daniulive/SmarterStreaming
- */
-
- /*++++++++++++++++++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++++++++++++++++++++++*/
上述接口说过之后,大家可能疑惑,信令交互呢?信令交互,我们是在Android上层实现。
如基础参数配置:
- /*** GB28181 相关参数,可以修改相关参数后测试 ***/
- GBSIPAgent gb28181_agent_ = null;
- private int gb28181_sip_local_port_base_ = 5060;
-
- private String gb28181_sip_server_id_ = "34020000002000000001";
- private String gb28181_sip_domain_ = "3402000000";
- private String gb28181_sip_server_addr_ = "192.168.0.103";
- private int gb28181_sip_server_port_ = 15060;
-
- private String gb28181_sip_user_agent_filed_ = "NT GB User Agent V1.3";
- private String gb28181_sip_username_ = "31011500991320000069";
- private String gb28181_sip_password_ = "12345678";
-
- private long gb28181_rtp_sender_handle_ = 0;
- private int gb28181_rtp_payload_type_ = 96;
-
- private long player_handle_ = 0;
- private long rtp_receiver_handle_ = 0;
- private AtomicLong last_received_audio_data_time_ = new AtomicLong(0);
-
- /*** GB28181 相关参数,可以修改相关参数后测试 ***/
InitGB28181Agent()相关:
- private boolean initGB28181Agent() {
- if ( gb28181_agent_ != null )
- return true;
-
- getLocation(context_);
-
- String local_ip_addr = IPAddrUtils.getIpAddress(context_);
- 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_.setLocalAddress(local_ip_addr);
- gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_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.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
- // "宇宙","火星1","火星", true);
-
- com.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("33010752991327811433", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
- "宇宙","火星1","火星", true);
-
-
- if (mLongitude != null && mLatitude != null) {
- com.gb.ntsignalling.DevicePosition device_pos = new com.gb.ntsignalling.DevicePosition();
-
- device_pos.setTime(mLocationTime);
- device_pos.setLongitude(mLongitude);
- device_pos.setLatitude(mLatitude);
- gb_device.setPosition(device_pos);
-
- gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报
- }
-
- gb28181_agent_.addDevice(gb_device);
-
- /*
- com.gb28181.ntsignalling.Device gb_device1 = new com.gb28181.ntsignalling.Device("34020000001380000002", "安卓测试设备2", Build.MANUFACTURER, Build.MODEL,
- "宇宙","火星1","火星", true);
- if (mLongitude != null && mLatitude != null) {
- com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();
- device_pos.setTime(mLocationTime);
- device_pos.setLongitude(mLongitude);
- device_pos.setLatitude(mLatitude);
- gb_device1.setPosition(device_pos);
- gb_device1.setSupportMobilePosition(true);
- }
- gb28181_agent_.addDevice(gb_device1);
- */
-
- if (!gb28181_agent_.createSipStack()) {
- gb28181_agent_ = null;
- Log.e(TAG, "initGB28181Agent gb28181_agent_.createSipStack failed.");
- return false;
- }
-
- boolean is_bind_local_port_ok = false;
-
- // 最多尝试5000个端口
- int try_end_port = gb28181_sip_local_port_base_ + 5000;
- try_end_port = try_end_port > 65536 ?65536: try_end_port;
-
- for (int i = gb28181_sip_local_port_base_; i < try_end_port; ++i) {
- if (gb28181_agent_.bindLocalPort(i)) {
- is_bind_local_port_ok = true;
- break;
- }
- }
-
- if (!is_bind_local_port_ok) {
- gb28181_agent_.releaseSipStack();
- gb28181_agent_ = null;
- Log.e(TAG, "initGB28181Agent gb28181_agent_.bindLocalPort failed.");
- return false;
- }
-
- if (!gb28181_agent_.initialize()) {
- gb28181_agent_.unBindLocalPort();
- gb28181_agent_.releaseSipStack();
- gb28181_agent_ = null;
- Log.e(TAG, "initGB28181Agent gb28181_agent_.initialize failed.");
- return false;
- }
-
- return true;
- }
信令交互处理:
- @Override
- public void ntsRegisterOK(String dateString) {
- Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));
- }
-
- @Override
- public void ntsRegisterTimeout() {
- Log.e(TAG, "ntsRegisterTimeout");
- }
-
- @Override
- public void ntsRegisterTransportError(String errorInfo) {
- Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));
- }
-
- @Override
- public void ntsOnHeartBeatException(int exceptionCount, String lastExceptionInfo) {
- }
-
- @Override
- public void ntsOnInvitePlay(String deviceId, PlaySessionDescription session_des) {
- }
-
- @Override
- public void ntsOnCancelPlay(String deviceId) {
- }
-
- @Override
- public void ntsOnAckPlay(String deviceId) {
- }
-
- @Override
- public void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {
- }
-
- @Override
- public void ntsOnByePlay(String deviceId) {
- }
-
- @Override
- public void ntsOnPlayDialogTerminated(String deviceId) {
- }
-
- @Override
- public void ntsOnDevicePositionRequest(String deviceId, int interval) {
- }
-
- @Override
- public void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID) {
- }
-
- @Override
- public void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID) {
- }
-
- @Override
- public void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo) {
- }
-
- @Override
- public void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID) {
- }
除了基础的Camera接口外,5.0开始新的Camera2数据依旧可以正常对接,感兴趣的开发者可酌情参考。