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

相关文章
|
5月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
165 1
|
6月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
3月前
|
IDE 开发工具 Android开发
移动应用开发之旅:探索Android和iOS平台
在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
107 17
|
4月前
|
算法 JavaScript Android开发
|
5月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
163 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
6月前
|
开发工具 Android开发 iOS开发
安卓与iOS开发环境对比:选择适合你的平台
【9月更文挑战第26天】在移动应用开发的广阔天地中,安卓和iOS是两大巨头。它们各自拥有独特的优势和挑战,影响着开发者的选择和决策。本文将深入探讨这两个平台的开发环境,帮助你理解它们的核心差异,并指导你根据个人或项目需求做出明智的选择。无论你是初学者还是资深开发者,了解这些平台的异同都至关重要。让我们一起探索,找到最适合你的那片开发天地。
|
6月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
7月前
|
传感器 Android开发 芯片
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
本文是系列文章的第三篇,展示了如何在Android系统中利用现有的i2c bus驱动,通过编写设备树节点和应用层的控制代码,实现对基于i2c bus的Slaver设备(如六轴陀螺仪模块QMI8658C)的控制,而无需编写设备驱动代码。
114 0
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
|
7月前
|
Android开发
不写一行代码(二):实现安卓基于PWM的LED设备驱动
本文介绍了在Android系统中不编写任何代码,通过设备树配置和内核支持的通用PWM LED驱动来实现基于PWM的LED设备驱动,并通过测试命令调整LED亮度级别。
115 0
不写一行代码(二):实现安卓基于PWM的LED设备驱动

热门文章

最新文章

  • 1
    Android历史版本与APK文件结构
  • 2
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
  • 3
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 4
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 5
    【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
  • 6
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 7
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
  • 8
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
  • 9
    Android实战经验之Kotlin中快速实现MVI架构
  • 10
    即时通讯安全篇(一):正确地理解和使用Android端加密算法
  • 1
    【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
    65
  • 2
    android FragmentManager 删除所有Fragment 重建
    25
  • 3
    Android实战经验之Kotlin中快速实现MVI架构
    42
  • 4
    即时通讯安全篇(一):正确地理解和使用Android端加密算法
    42
  • 5
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
    46
  • 6
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    158
  • 7
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    55
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    73
  • 9
    Android历史版本与APK文件结构
    182
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    54