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平台部署到外网后,前端设备只要注册到国标服务器,就可以被远程访问、管理和调取视频。但由于设备侧性能并不是非常好,如果要有好的稳定性和性能要求,需尽可能的减少性能消耗,按需打开摄像头、按需编码等。

相关文章
|
25天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
29天前
|
缓存 监控 Java
构建高效Android应用:从优化用户体验到提升性能
在竞争激烈的移动应用市场中,为用户提供流畅和高效的体验是至关重要的。本文深入探讨了如何通过多种技术手段来优化Android应用的性能,包括UI响应性、内存管理和多线程处理。同时,我们还将讨论如何利用最新的Android框架和工具来诊断和解决性能瓶颈。通过实例分析和最佳实践,读者将能够理解并实施必要的优化策略,以确保他们的应用在保持响应迅速的同时,还能够有效地利用系统资源。
|
1月前
|
缓存 移动开发 Android开发
提升安卓应用性能的实用策略
在移动开发领域,应用的性能优化是一个持续的挑战。对于安卓开发者而言,确保应用流畅、快速并且电池使用效率高,是吸引和保持用户的关键因素之一。本文将深入探讨针对安卓平台的性能优化技巧,包括内存管理、代码效率、UI渲染以及电池寿命等方面的考量。这些策略旨在帮助开发者构建出更高效、响应更快且用户体验更佳的安卓应用。
|
1月前
|
数据库 Android开发 UED
提升安卓应用性能的十大技巧
【2月更文挑战第30天】在移动设备上,应用程序的性能直接影响用户体验。本文将分享10个优化安卓应用性能的技巧,包括代码优化、内存管理、UI设计和使用性能分析工具等,帮助开发者提高应用的运行速度和响应时间,从而提升用户满意度。
|
1月前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【2月更文挑战第30天】 随着Kotlin成为开发Android应用的首选语言,开发者社区对于其性能表现持续关注。本文通过深入分析与基准测试,探讨Kotlin与Java在Android平台上的性能差异,揭示两种语言在编译效率、运行时性能和内存消耗方面的具体表现,并提供优化建议。我们的目标是为Android开发者提供科学依据,帮助他们在项目实践中做出明智的编程语言选择。
|
1月前
|
监控 测试技术 Android开发
提升安卓应用性能的实用策略
【2月更文挑战第24天】 在竞争激烈的应用市场中,性能优化是提高用户体验和应用成功的关键。本文将探讨针对安卓平台的性能优化技巧,包括内存管理、多线程处理和UI渲染效率的提升。我们的目标是为开发者提供一套实用的工具和方法,以诊断和解决性能瓶颈,确保应用流畅运行。
|
1月前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
在开发高性能的Android应用时,选择合适的编程语言至关重要。近年来,Kotlin因其简洁性和功能性受到开发者的青睐,但其性能是否与传统的Java相比有所不足?本文通过对比分析Kotlin与Java在Android平台上的运行效率,揭示二者在编译速度、运行时性能及资源消耗方面的具体差异,并探讨在实际项目中如何做出最佳选择。
18 4
|
1月前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【2月更文挑战第24天】 在移动开发领域,性能优化一直是开发者关注的重点。随着Kotlin的兴起,许多Android开发者开始从传统的Java转向Kotlin进行应用开发。本文将深入探讨Kotlin与Java在Android平台上的性能表现,通过对比分析两者在编译效率、运行时性能和内存消耗等方面的差异。我们将基于实际案例研究,为开发者提供选择合适开发语言的数据支持,并分享一些提升应用性能的最佳实践。
|
2天前
|
缓存 监控 Android开发
提升安卓应用性能的关键策略
【4月更文挑战第26天】 在竞争激烈的移动应用市场中,性能优化不仅是提升用户体验的基石,也是决定应用成功与否的关键因素。本文将深入探讨针对安卓平台的性能优化技巧,从内存管理到多线程处理,再到布局优化和响应式设计,旨在帮助开发者构建更快速、更流畅的应用程序。通过分析常见性能瓶颈,并提供实用的解决方案,本文力求成为安卓开发者提质增效的重要参考。
|
5天前
|
Android开发
Android源代码定制:Overlay目录定制|调试Overlay资源是否生效
Android源代码定制:Overlay目录定制|调试Overlay资源是否生效
11 0