Android平台GB28181设备接入端如何降低资源占用和性能消耗

简介: Android平台GB28181设备接入端如何降低资源占用和性能消耗?

背景

我们在做GB28181设备接入模块的时候,考虑到好多设备性能一般,我们一般的设计思路是,先注册设备到平台侧,平台侧发calalog过来,获取设备信息,然后,设备侧和国标平台侧维持心跳,如果有位置订阅信息,按照订阅时间间隔,实时上报设备位置信息。

如果本地没有录像诉求,或者,国标平台侧不发起invite请求,Android平台GB28181设备接入端,不做视频编码,甚至可以连摄像头都不打开,等有实时录像或国标平台侧视频预览播放请求的时候,再打开摄像头,毕竟摄像头单纯的打开,设备都有性能损耗,甚至一些中低端记录仪,还没编码就开始发热。

技术实现

本文以大牛直播SDK的Android平台GB28181设备接入侧为例,先启动GB28181,启动后,直接注册到国标平台侧,整体设计架构图如下:

视沃科技(大牛直播SDK)GB28181设备接入SDK.png

classButtonGB28181AgentListenerimplementsView.OnClickListener {
publicvoidonClick(Viewv) {
stopAudioPlayer();
destoryRTPReceiver();
gb_broadcast_source_id_=null;
gb_broadcast_target_id_=null;
btnGB28181AudioBroadcast.setText("GB28181语音广播");
btnGB28181AudioBroadcast.setEnabled(false);
stopGB28181Stream();
destoryRTPSender();
if (null==gb28181_agent_ ) {
if( !initGB28181Agent() )
return;
    }
if (gb28181_agent_.isRunning()) {
gb28181_agent_.terminateAllPlays(true);// 目前测试下来,发送BYE之后,有些服务器会立即发送INVITE,是否发送BYE根据实际情况看gb28181_agent_.stop();
btnGB28181Agent.setText("启动GB28181");
    }
else {
if ( gb28181_agent_.start() ) {
btnGB28181Agent.setText("停止GB28181");
      }
    }
  }
}

其中,initGB28181Agent()做的工作如下:

privatebooleaninitGB28181Agent() {
if ( gb28181_agent_!=null )
returntrue;
getLocation(context_);
Stringlocal_ip_addr=IPAddrUtils.getIpAddress(context_);
Log.i(TAG, "[daniusdk]initGB28181Agent local ip addr: "+local_ip_addr);
if ( local_ip_addr==null||local_ip_addr.isEmpty() ) {
Log.e(TAG, "[daniusdk]initGB28181Agent local ip is empty");
returnfalse;
  }
gb28181_agent_=GBSIPAgentFactory.getInstance().create();
if ( gb28181_agent_==null ) {
Log.e(TAG, "[daniusdk]initGB28181Agent create agent failed");
returnfalse;
  }
gb28181_agent_.addListener(this);
gb28181_agent_.addPlayListener(this);
gb28181_agent_.addAudioBroadcastListener(this);
gb28181_agent_.addDeviceControlListener(this);
gb28181_agent_.addQueryCommandListener(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_.setUserInfo(gb28181_sip_username_, 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.Devicegb_device=newcom.gb.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
"宇宙","火星1","火星", true);
if (mLongitude!=null&&mLatitude!=null) {
com.gb.ntsignalling.DevicePositiondevice_pos=newcom.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);
if (!gb28181_agent_.createSipStack()) {
gb28181_agent_=null;
Log.e(TAG, "[daniusdk]initGB28181Agent gb28181_agent_.createSipStack failed.");
returnfalse;
  }
booleanis_bind_local_port_ok=false;
// 最多尝试5000个端口inttry_end_port=gb28181_sip_local_port_base_+5000;
try_end_port=try_end_port>65536?65536: try_end_port;
for (inti=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, "[daniusdk]initGB28181Agent gb28181_agent_.bindLocalPort failed.");
returnfalse;
  }
if (!gb28181_agent_.initialize()) {
gb28181_agent_.unBindLocalPort();
gb28181_agent_.releaseSipStack();
gb28181_agent_=null;
Log.e(TAG, "[daniusdk]initGB28181Agent gb28181_agent_.initialize failed.");
returnfalse;
  }
returntrue;
}

注册成功后,会把国标平台侧返回200 OK时带的时间返回上来,便于Android平台GB28181设备侧做校时,如有注册异常,也会返回:

@OverridepublicvoidntsRegisterOK(StringdateString) {
Log.i(TAG, "ntsRegisterOK Date: "+ (dateString!=null?dateString : ""));
}
@OverridepublicvoidntsRegisterTimeout() {
Log.e(TAG, "ntsRegisterTimeout");
}
@OverridepublicvoidntsRegisterTransportError(StringerrorInfo) {
Log.e(TAG, "ntsRegisterTransportError error:"+ (errorInfo!=null?errorInfo :""));
}

周期性的心跳,如有异常,我们也回调到上层:

@OverridepublicvoidntsOnHeartBeatException(intexceptionCount,  StringlastExceptionInfo) {
Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:"+exceptionCount+", exception info:"+ (lastExceptionInfo!=null?lastExceptionInfo:""));
// 停止信令, 然后重启handler_.postDelayed(newRunnable() {
@Overridepublicvoidrun() {
Log.i(TAG, "gb28281_heart_beart_timeout");
stopAudioPlayer();
destoryRTPReceiver();
if (gb_broadcast_source_id_!=null&&gb_broadcast_target_id_!=null&&gb28181_agent_!=null)
gb28181_agent_.byeAudioBroadcast(gb_broadcast_source_id_, gb_broadcast_target_id_);
gb_broadcast_source_id_=null;
gb_broadcast_target_id_=null;
btnGB28181AudioBroadcast.setText("GB28181语音广播");
btnGB28181AudioBroadcast.setEnabled(false);
stopGB28181Stream();
destoryRTPSender();
if (gb28181_agent_!=null) {
gb28181_agent_.terminateAllPlays(true);
Log.i(TAG, "gb28281_heart_beart_timeout sip stop");
gb28181_agent_.stop();
Stringlocal_ip_addr=IPAddrUtils.getIpAddress(context_);
if (local_ip_addr!=null&&!local_ip_addr.isEmpty() ) {
Log.i(TAG, "gb28281_heart_beart_timeout get local ip addr: "+local_ip_addr);
gb28181_agent_.setLocalAddress(local_ip_addr);
        }
Log.i(TAG, "gb28281_heart_beart_timeout sip start");
gb28181_agent_.start();
      }
    }
  },0);
}

如果国标平台侧订阅了实时位置信息,我们的处理如下:

@OverridepublicvoidntsOnDevicePositionRequest(StringdeviceId, intinterval) {
handler_.postDelayed(newRunnable() {
@Overridepublicvoidrun() {
getLocation(context_);
if (mLongitude!=null&&mLatitude!=null) {
com.gb.ntsignalling.DevicePositiondevice_pos=newcom.gb.ntsignalling.DevicePosition();
device_pos.setTime(mLocationTime);
device_pos.setLongitude(mLongitude);
device_pos.setLatitude(mLatitude);
if (gb28181_agent_!=null ) {
gb28181_agent_.updateDevicePosition(device_id_, device_pos);
        }
      }
    }
privateStringdevice_id_;
privateintinterval_;
publicRunnableset(Stringdevice_id, intinterval) {
this.device_id_=device_id;
this.interval_=interval;
returnthis;
    }
  }.set(deviceId, interval),0);
}

如果平台侧发起预览请求,我们的处理如下:

@OverridepublicvoidntsOnInvitePlay(StringdeviceId, SessionDescriptionsession_des) {
handler_.postDelayed(newRunnable() {
@Overridepublicvoidrun() {
// 先振铃响应下gb28181_agent_.respondPlayInvite(180, device_id_);
MediaSessionDescriptionvideo_des=null;
SDPRtpMapAttributeps_rtpmap_attr=null;
// 28181 视频使用PS打包Vector<MediaSessionDescription>video_des_list=session_des_.getVideoPSDescriptions();
if (video_des_list!=null&&!video_des_list.isEmpty()) {
for(MediaSessionDescriptionm : video_des_list) {
if (m!=null&&m.isValidAddressType() &&m.isHasAddress() ) {
video_des=m;
ps_rtpmap_attr=video_des.getPSRtpMapAttribute();
break;
          }
        }
      }
if (null==video_des) {
gb28181_agent_.respondPlayInvite(488, device_id_);
Log.i(TAG, "ntsOnInvitePlay get video description is null, response 488, device_id:"+device_id_);
return;
      }
if (null==ps_rtpmap_attr) {
gb28181_agent_.respondPlayInvite(488, device_id_);
Log.i(TAG, "ntsOnInvitePlay get ps rtp map attribute is null, response 488, device_id:"+device_id_);
return;
      }
longrtp_sender_handle=libPublisher.CreateRTPSender(0);
if ( rtp_sender_handle==0 ) {
gb28181_agent_.respondPlayInvite(488, device_id_);
Log.i(TAG, "ntsOnInvitePlay CreateRTPSender failed, response 488, device_id:"+device_id_);
return;
      }
gb28181_rtp_payload_type_=ps_rtpmap_attr.getPayloadType();
gb28181_rtp_encoding_name_=ps_rtpmap_attr.getEncodingName();
libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1);
libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1);
libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2MlibPublisher.SetRTPSenderClockRate(rtp_sender_handle, ps_rtpmap_attr.getClockRate());
libPublisher.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort());
if ( libPublisher.InitRTPSender(rtp_sender_handle) !=0 ) {
gb28181_agent_.respondPlayInvite(488, device_id_);
libPublisher.DestoryRTPSender(rtp_sender_handle);
return;
      }
intlocal_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);
Stringlocal_ip_addr=IPAddrUtils.getIpAddress(context_);
MediaSessionDescriptionlocal_video_des=newMediaSessionDescription(video_des.getType());
local_video_des.addFormat(String.valueOf(ps_rtpmap_attr.getPayloadType()));
local_video_des.addRtpMapAttribute(ps_rtpmap_attr);
local_video_des.setAddressType(video_des.getAddressType());
local_video_des.setAddress(local_ip_addr);
local_video_des.setPort(local_port);
local_video_des.setTransportProtocol(video_des.getTransportProtocol());
if (!gb28181_agent_.respondPlayInviteOK(device_id_,local_video_des) ) {
libPublisher.DestoryRTPSender(rtp_sender_handle);
Log.e(TAG, "ntsOnInvitePlay call respondPlayInviteOK failed.");
return;
      }
gb28181_rtp_sender_handle_=rtp_sender_handle;
    }
privateStringdevice_id_;
privateSessionDescriptionsession_des_;
publicRunnableset(Stringdevice_id, SessionDescriptionsession_des) {
this.device_id_=device_id;
this.session_des_=session_des;
returnthis;
    }
  }.set(deviceId, session_des),0);
}

收到Ack后,才开始真正发送数据:

@OverridepublicvoidntsOnAckPlay(StringdeviceId) {
handler_.postDelayed(newRunnable() {
@Overridepublicvoidrun() {
Log.i(TAG,"ntsOnACKPlay, device_id:"+device_id_);
if (!isRTSPPublisherRunning&&!isPushingRtmp&&!isRecording) {
InitAndSetConfig();
      }
libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_, gb28181_rtp_encoding_name_);
//libPublisher.SetGBTCPConnectTimeout(publisherHandle, 10*60*1000);//libPublisher.SetGBInitialTCPReconnectInterval(publisherHandle, 1000);//libPublisher.SetGBInitialTCPMaxReconnectAttempts(publisherHandle, 3);intstartRet=libPublisher.StartGB28181MediaStream(publisherHandle);
if (startRet!=0) {
if (!isRTSPPublisherRunning&&!isPushingRtmp&&!isRecording) {
if (publisherHandle!=0) {
longhandle=publisherHandle;
publisherHandle=0;
libPublisher.SmartPublisherClose(handle);
          }
        }
destoryRTPSender();
Log.e(TAG, "Failed to start GB28181 service..");
return;
      }
if (!isRTSPPublisherRunning&&!isPushingRtmp&&!isRecording) {
CheckInitAudioRecorder();
      }
startLayerPostThread();
isGB28181StreamRunning=true;
    }
privateStringdevice_id_;
publicRunnableset(Stringdevice_id) {
this.device_id_=device_id;
returnthis;
    }
  }.set(deviceId),0);
}

总结

除此之外,还有语音广播和语音对讲,这里不再赘述,GB28181规范普及之前,要想从外网远程访问局域网内的监控设备非常麻烦,一般要么RTSP转RTMP推到RTMP服务器,此外还要单独构建信令。GB28181规范,让远程、跨网访问监控设备更方便,把GB28181平台部署到外网后,前端设备只要注册到国标服务器,就可以被远程访问、管理和调取视频。但由于设备侧性能并不是非常好,如果要有好的稳定性和性能要求,需尽可能的减少性能消耗,按需打开摄像头、按需编码等。

相关文章
|
9月前
|
存储 消息中间件 人工智能
【03】AI辅助编程完整的安卓二次商业实战-本地构建运行并且调试-二次开发改注册登陆按钮颜色以及整体资源结构熟悉-优雅草伊凡
【03】AI辅助编程完整的安卓二次商业实战-本地构建运行并且调试-二次开发改注册登陆按钮颜色以及整体资源结构熟悉-优雅草伊凡
277 3
|
存储 Android开发
如何查看Flutter应用在Android设备上已被撤销的权限?
如何查看Flutter应用在Android设备上已被撤销的权限?
710 64
|
存储 Android开发 数据安全/隐私保护
如何在Android设备上撤销Flutter应用程序的所有权限?
如何在Android设备上撤销Flutter应用程序的所有权限?
902 64
|
缓存 Android开发 开发者
Flutter环境配置完成后,如何在Android设备上运行Flutter应用程序?
Flutter环境配置完成后,如何在Android设备上运行Flutter应用程序?
2588 62
|
开发工具 Android开发 开发者
在Android设备上运行Flutter应用程序时,如果遇到设备未授权的问题该如何解决?
在Android设备上运行Flutter应用程序时,如果遇到设备未授权的问题该如何解决?
982 61
|
11月前
|
监控 Android开发 数据安全/隐私保护
批量发送短信的平台,安卓群发短信工具插件脚本,批量群发短信软件【autojs版】
这个Auto.js脚本实现了完整的批量短信发送功能,包含联系人管理、短信内容编辑、发送状态监控等功能
|
存储 编解码 监控
Android平台GB28181执法记录仪技术方案与实现
本文介绍了大牛直播SDK的SmartGBD在执法记录仪场景中的应用。GB28181协议作为视频监控联网的国家标准,为设备互联互通提供规范。SmartGBD专为Android平台设计,支持音视频采集、编码与传输,具备自适应算法和多功能扩展优势。文章分析了执法记录仪的需求,如实时音视频传输、设备管理及数据安全,并详细阐述了基于SmartGBD的技术实现方案,包括环境准备、SDK集成、设备注册、音视频处理及功能扩展等步骤。最后展望了SmartGBD在未来智慧物联领域的广阔应用前景。
909 13
|
存储 编解码 开发工具
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
本文详细探讨了在Android平台上实现HTTP-FLV播放器的过程。首先介绍了FLV格式的基础,包括文件头和标签结构。接着分析了HTTP-FLV传输原理,通过分块传输实现流畅播放。然后重点讲解了播放器的实现步骤,涵盖网络请求、数据解析、音视频解码与渲染,以及播放控制功能的设计。文章还讨论了性能优化和网络异常处理的方法,并总结了HTTP-FLV播放器的技术价值,尤其是在特定场景下的应用意义。
782 11
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
10194 2
|
IDE 开发工具 Android开发
移动应用开发之旅:探索Android和iOS平台
在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
409 17