首页 > 其他分享 >Android平台GB28181设备接入侧如何实现按需打开视音频采集传输

Android平台GB28181设备接入侧如何实现按需打开视音频采集传输

时间:2023-09-15 12:35:17浏览次数:42  
标签:publisher _. GB28181 des 视音频 video device Android id

GB/T28181规范

GB/T28181是中国国家标准,全称为《安全防范视频监控联网系统信息传输、交换、控制技术要求》,该标准规定了城市安全防范监控系统中视频监控联网系统的一般要求和架构,以及信息传输、交换、控制的技术要求。它主要应用于安防领域,为各种视频监控系统提供了一致的接口规范,使得不同厂商生产的视频监控设备可以相互兼容。规范规定了公共安全视频监控联网系统(以下简称“联网系统”)的互联结构,传输、交换、控制的基本要求和安全性要求,以及控制、传输流程和协议接口等技术要求。适用于公共安全视频监控联网系统的方案设计、系统检测、验收以及与之相关的设备研发生产。其他视频监控联网系统可参照执行。目前已更新至GB/T28181-2022版。

为什么要开发Android平台GB28181接入模块

实际上,Android平台GB28181接入模块,主要目标是可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景。

Android平台GB28181设备接入侧如何实现按需打开视音频采集传输_GB28181安卓端

Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲,历史视音频文件查询和下载,支持对接数据类型如下:

  1. 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型),其中,Android平台前后摄像头数据,或者屏幕数据,或者Unity拿到的数据,均属编码前数据;
  2. 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据);
  3. 拉取RTSP或RTMP流并接入至GB28181平台(比如其他IPC的RTSP流,可通过Android平台GB28181接入到国标平台)。

功能设计

实际上,我们在做Android平台GB28181设备接入模块之前,已经有非常成熟的视音频采集(屏幕、摄像头、外部音视频数据)、软硬编码、录像、快照、实时动态水印等技术储备,所以,GB28181设备接入,主要考虑的是信令和媒体流传输这块,考虑到设备性能和实际场景,我们信令和媒体传输设计是分离的,Android端GB28181设备接入侧注册到国标平台后,如果国标平台不需要查看前端设备数据,我们仅维持心跳(KeepAlive),需要查看的时候,我们再开摄像头、麦克风编码打包投递数据给平台侧,尽可能的减少性能消耗,这块在执法记录仪、智能安全帽等场景下,非常实用。

  • [视频格式]H.264/H.265(Android H.265硬编码);
  •  [音频格式]G.711 A律、AAC;
  •  [音量调节]Android平台采集端支持实时音量调节;
  •  [H.264硬编码]支持H.264特定机型硬编码;
  •  [H.265硬编码]支持H.265特定机型硬编码;
  •  [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  •  [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  • 支持纯视频、音视频PS打包传输;
  • 支持RTP OVER UDP和RTP OVER TCP被动模式(TCP媒体流传输客户端);
  • 支持信令通道网络传输协议TCP/UDP设置;
  • 支持注册、注销,支持注册刷新及注册有效期设置;
  • 支持设备目录查询应答;
  • 支持心跳机制,支持心跳间隔、心跳检测次数设置;
  • 支持移动设备位置(MobilePosition)订阅和通知;
  • 支持语音广播;
  • 支持语音对讲;
  • 支持历史视音频文件检索;
  • 支持历史视音频文件下载;
  • 支持云台控制和预置位查询;
  •  [实时水印]支持动态文字水印、png水印;
  •  [镜像]Android平台支持前置摄像头实时镜像功能;
  •  [实时静音]支持实时静音/取消静音;
  •  [实时快照]支持实时快照;
  •  [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  •  [外部编码前视频数据对接]支持YUV数据对接;
  •  [外部编码前音频数据对接]支持PCM对接;
  •  [外部编码后视频数据对接]支持外部H.264数据对接;
  •  [外部编码后音频数据对接]外部AAC数据对接;
  •  [扩展录像功能]支持和录像模块组合使用,录像相关功能。

Android端如何实现后台视音频GB28181接入

后台采集摄像头和麦克风这块,不再赘述,基本做Andorid开发的,都能搞得定,需要注意的是,后台service推送,需要加入省电优化白名单,以免8.0及以上版本设备后台运行超过一分钟被自动停掉,6.0以上版本,需要动态获取权限:

if (Build.VERSION.SDK_INT >=26)
{
  if(!isIgnoringBatteryOptimizations())
  {
    gotoSettingIgnoringBatteryOptimizations();
  }
}

//6.0及以上版本,动态获取Audio权限
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
  RequestAudioPermission();
}

我们要做的就是选择分辨率、软硬编码等参数后,启动GB28181即可:

Android平台GB28181设备接入侧如何实现按需打开视音频采集传输_GB28181安卓记录仪_02

因为系后台服务,启动后,任务栏可以看到:

Android平台GB28181设备接入侧如何实现按需打开视音频采集传输_GB28181安卓端_03

收到平台侧发来的Invite后,我们会调用try_preview_camera()来启动摄像头后台预览。

/*
 * BackgroudService.java
 * Author: daniusdk.com
 */
@Override
public void ntsOnInvitePlay(String deviceId, SessionDescription session_des) {
  handler_.postDelayed(new Runnable() {
    @Override
    public void run() {
      // 先振铃响应下
      gb28181_agent_.respondPlayInvite(180, device_id_);

      if (!try_preview_camera()) {
        gb28181_agent_.respondPlayInvite(488, device_id_);
        Log.i(TAG, "ntsOnInvitePlay try_preview_camera failed, response 488, device_id:" + device_id_);
        return;
      }

      MediaSessionDescription video_des = null;
      SDPRtpMapAttribute ps_rtpmap_attr = null;

      // 28181 视频使用PS打包
      Vector<MediaSessionDescription> video_des_list = session_des_.getVideoPSDescriptions();
      if (video_des_list != null && !video_des_list.isEmpty()) {
        for(MediaSessionDescription m : 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;
      }

      Log.i(TAG,"ntsOnInvitePlay, device_id:" +device_id_+", is_tcp:" + video_des.isRTPOverTCP()
            + " rtp_port:" + video_des.getPort() + " ssrc:" + video_des.getSSRC()
            + " address_type:" + video_des.getAddressType() + " address:" + video_des.getAddress());

      long rtp_sender_handle = lib_publisher_.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();

      lib_publisher_.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1);
      lib_publisher_.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1);
      lib_publisher_.SetRTPSenderLocalPort(rtp_sender_handle, 0);
      lib_publisher_.SetRTPSenderSSRC(rtp_sender_handle, video_des.getSSRC());
      lib_publisher_.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2M
      lib_publisher_.SetRTPSenderClockRate(rtp_sender_handle, ps_rtpmap_attr.getClockRate());
      lib_publisher_.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort());

      if ( lib_publisher_.InitRTPSender(rtp_sender_handle) != 0 ) {
        gb28181_agent_.respondPlayInvite(488, device_id_);
        lib_publisher_.DestoryRTPSender(rtp_sender_handle);
        return;
      }

      int local_port = lib_publisher_.GetRTPSenderLocalPort(rtp_sender_handle);
      if (local_port == 0) {
        gb28181_agent_.respondPlayInvite(488, device_id_);
        lib_publisher_.DestoryRTPSender(rtp_sender_handle);
        return;
      }

      Log.i(TAG,"get local_port:" + local_port);

      String local_ip_addr = IPAddrUtils.getIpAddress(context_);

      MediaSessionDescription local_video_des = new MediaSessionDescription(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());
      local_video_des.setSSRC(video_des.getSSRC());

      if (!gb28181_agent_.respondPlayInviteOK(device_id_,local_video_des) ) {
        lib_publisher_.DestoryRTPSender(rtp_sender_handle);
        Log.e(TAG, "ntsOnInvitePlay call respondPlayInviteOK failed.");
        return;
      }

      gb28181_rtp_sender_handle_ = rtp_sender_handle;
    }

    private String device_id_;
    private SessionDescription session_des_;

    public Runnable set(String device_id, SessionDescription session_des) {
      this.device_id_ = device_id;
      this.session_des_ = session_des;
      return this;
    }
  }.set(deviceId, session_des),0);
}

try_preview_camera()实现如下:

private boolean try_preview_camera() {
     if (camera_ != null)
         return true;

    SurfaceHolder surface_holder = get_surface_holder();
    if (null == surface_holder) {
        Log.e(TAG, "try_preview_camera surface_holder is null");
        return false;
    }

     camera_ = open_camera(current_camera_type_);
     if (null == camera_) {
         Log.e(TAG, "try_preview_camera open_camera is null type:" + current_camera_type_);
         return false;
     }

    if (!start_camera_preview(surface_holder)) {
        release_camera();
        Log.i(TAG, "try_preview_camera start_camera_preview failed");
        return false;
    }

    return true;
 }

收到ack后,直接发送数据到国标平台侧即可

@Override
public void ntsOnAckPlay(String deviceId) {
  handler_.postDelayed(new Runnable() {
    @Override
    public void run() {
      Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);

      InitAndSetConfig();

      lib_publisher_.SetGB28181RTPSender(publisher_handle_, 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);

      int startRet = lib_publisher_.StartGB28181MediaStream(publisher_handle_);
      if (startRet != 0) {
        if (publisher_handle_ != 0) {
          lib_publisher_.SmartPublisherClose(publisher_handle_);
          publisher_handle_ = 0;
        }

        destoryRTPSender();

        Log.e(TAG, "Failed to start GB28181 service..");
        return;
      }

      startAudioRecorder();

      startLayerPostThread();

      is_gb_stream_running_ = true;
    }

    private String device_id_;

    public Runnable set(String device_id) {
      this.device_id_ = device_id;
      return this;
    }

  }.set(deviceId),0);
}

关闭预览查看,处理bye信令:

@Override
public void ntsOnByePlay(String deviceId) {
  handler_.postDelayed(new Runnable() {
    @Override
    public 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);
}

其中stopGB28181Stream()需要关闭摄像头(对应release_camera())和麦克风(对应stopAudioRecorder()),确保只有国标平台测查看的时候,才开启摄像头,尽可能的减少性能损耗。

//停止GB28181 媒体流
private void stopGB28181Stream() {
  if (!is_gb_stream_running_)
    return;

  stopLayerPostThread();
  stopAudioRecorder();
  release_camera();

  is_gb_stream_running_ = false;
  lib_publisher_.StopGB28181MediaStream(publisher_handle_);

  if (publisher_handle_ != 0) {
    lib_publisher_.SmartPublisherClose(publisher_handle_);
    publisher_handle_ = 0;
  }
}

总结

以上是大概的流程,摄像头麦克风采集做到后台的话,可以在需要预览采集数据的时候才打开,不用的时候,直接关闭,只保留信令这块,打开视音频预览后,如果有语音广播过来,可以直接播放语音广播的数据,这样尽可能的减少设备的性能消耗,提高待机时间,特别是执法记录仪等户外设备,按需打开摄像头和麦克风,按需投递视音频数据到平台外侧,意义非常大。

标签:publisher,_.,GB28181,des,视音频,video,device,Android,id
From: https://blog.51cto.com/daniusdk/7479770

相关文章

  • 过去3个月各种Android面试
    在过去的3个月里,笔者历经了各种面试的考验。这场考验,就像是在造火箭,复杂而艰辛。尽管如此,笔者凭借自己的实力和努力,最终拿下了百度、腾讯和京东的录用通知。经过深思熟虑,笔者可能会选择京东,这也许让你感到有些意外。腾讯无疑是一个诱人的选择,它提供的薪资和福利待遇都相当可观。笔......
  • EasyGBS是一款基于国标GB28181协议的视频融合管理平台,它可以实现视频资源的集中管理和
    EasyGBS国标视频融合云平台是一款基于端-边-云一体化架构的视频融合+AI智能分析网关平台,EasyGBS平台支持视频汇聚、融合管理,能兼容多类型设备、多协议接入,可提供的视频功能包括:视频监控、无插件直播录像、云存储、检索回放、智能告警、平台级联、GIS定位监测等。EasyGBS具备强大的......
  • android中的VERSION和VERSION_CODES和compileSdkVersion, minSdkVersion 和 targetSdk
    一背景经常会有代码中用到  Build.VERSION.SDK_INT<Build.VERSION_CODES.O,这是指什么意思。在app项目中,经常会看到android{compileSdkVersion30buildToolsVersion"30.0.3"defaultConfig{applicationId"com.yl.qrcode"minSdkVersio......
  • Android studio 修改APK打包生成名称
    在app的build.gradle的android{}添加一下代码android.applicationVariants.all{variant->variant.outputs.all{defcreateTime=newDate().format("YYYYMMdd",TimeZone.getTimeZone("GMT+08:00"))//在这里修改apk文件名......
  • Flutter插件flutter_boost 在android模块中的报红问题解决.
    1,在开发Flutter插件时,打开插件的android项目,准备编写native端的代码时,发现各种报红,代码无法跳转,体验十分不好。就像我下面的截图一样:导入了FlutterBoostflutterBoost源码爆红。但是运行正常。。这说明本身是没有问题的。。分明是没有错误的类都存在。但是就是爆红。。。。可......
  • Android 9 WiFi连接过程
    我们从setting 入口开始分析该过程;1.setting界面 packages\apps\Settings\src\com\android\settings\wifi\WifiSettings.java 创建Dialog2@OverridepublicDialogonCreateDialog(intdialogId){switch(dialogId){caseWIFI_DIALOG_ID:......
  • 独家珍藏的Android面试突击宝典,轻松应对95%秋招面试题
    前言最近发现了很多同学出现的一个问题,简历已读不回,没有面试机会等等问题,觉得互联网不行了,Android不行了,甚至有人说自己这辈子最后悔的事情就是进入it这个行业。我每次看到这样的反馈或者这样的说法的时候,心里总不是滋味,我承认大家现在反馈的问题在一定程度上确实是客观存在的情况,......
  • Android广播接收器详解
    1.Android广播接收器简介在Android中,BroadcastReceiver(广播接收器)是一种组件,用于监听系统广播或应用程序内自定义广播,并在广播发生时接收并处理这些广播。广播接收器可以用于实现组件之间的通信,无论是在同一个应用程序内还是在不同应用程序之间。广播接收器有两个主要部分:注册广......
  • Android软键盘弹出关闭监听
    https://juejin.cn/post/6844903489051557902?from=singlemessage&isappinstalled=0#commentpackagecom.xiucai.common.manager;importandroid.graphics.Rect;importandroid.util.Log;importandroid.view.View;importandroid.view.ViewTreeObserver;import......
  • Android Studio中无法显示main.dart(Flutter项目在Android Studio中显示不全)
    问题描述创建完项目后只出现android文件选择ProjectFiles就会显示整个目录内容设置后......