Android平台如何实时叠加电量信息和设备信号状态到GB28181接入端

news2025/1/18 10:01:48

技术背景

我们在Android平台实现GB28181设备接入,把摄像头和麦克风数据,采集过去,用于移动单兵、智能车载、智慧安防、智能家居、工业仿真等行业时,发现大多场景对视频水印的要求越来越高,从之前的固定位置静态文字水印、png水印等慢慢过渡到动态水印需求。

本文,我们要探讨的是,除了常规的时间、经纬度信息获png水印外,如何叠加电量和设备信号状态到视频view。

如何获取电量信息

在Android平台上获取电量信息可以使用以下三种方式:

  1. 通过BatteryManager类获取电量信息:

可以使用Context.getSystemService()方法获取BatteryManager实例,并使用该实例的getIntExtra()方法获取电量信息。具体代码如下:

 BatteryManager batteryManager = (BatteryManager) getSystemService(Context.BATTERY_SERVICE);  
 int batteryLevel = batteryManager.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);  
 int batteryScale = batteryManager.getIntExtra(BatteryManager.EXTRA_SCALE, -1);  
 int batteryPlugType = batteryManager.getIntExtra(BatteryManager.EXTRA_PLUGED, -1);  
 boolean isCharging = batteryManager.getIntExtra(BatteryManager.EXTRA_CHARGING, 0) == 1;
  1. 通过PowerManager类获取电量信息:

可以使用Context.getSystemService()方法获取PowerManager实例,并使用该实例的isDeviceIdle()方法和isPowerSaveMode()方法获取电量信息。具体代码如下:

 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);  
 boolean isDeviceIdle = powerManager.isDeviceIdle();  
 boolean isPowerSaveMode = powerManager.isPowerSaveMode();
  1. 通过UsageStatsManager类获取电量信息:

可以使用Context.getSystemService()方法获取UsageStatsManager实例,并使用该实例的queryStats()方法获取电量信息。具体代码如下:

 UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);  
 long time = System.currentTimeMillis();  
 UsageStats stats = usageStatsManager.queryStats(UsageStatsManager.INTERVAL_DAILY, time);  
 long totalTime = stats.getTotalTime();  
 long screenTime = stats.getScreenTime();  
 float batteryLevel = (totalTime * 100) / screenTime;

如何获取设备信号

要获取Android设备的信号强度,可以使用TelephonyManager类中的getSignalStrength()方法。以下是一个示例代码片段,演示如何获取设备的信号强度:

 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
 int signalStrength = telephonyManager.getSignalStrength();

此代码将返回设备的信号强度,单位为dBm(分贝毫瓦)。如果设备没有电话卡,则返回值为int最小值(0)。

请注意,要使用TelephonyManager类,您需要在AndroidManifest.xml文件中添加以下权限:

 <uses-permission android:name="android.permission.READ_PHONE_STATE" />

此权限允许您的应用程序访问设备的电话状态和信息。

如何把设备电量信息和设备信号状态叠加到view

叠加设备电量和设备实时信号状态,实际上,调用的是我们动态文字水印,通过生成TextBitmap,然后从bitmap里面拷贝获取到text_timestamp_buffer_,通过我们设计的PostLayerImageRGBA8888ByteBuffer()投递到jni层。

private int postTimestampLayer(int index, int left, int top) {

    Bitmap text_bitmap = makeTextBitmap(makeTimestampString(), getFontSize(),
            Color.argb(255, 0, 0, 0), true, Color.argb(255, 255, 255, 255),true);

    if (null == text_bitmap)
        return 0;

    if ( text_timestamp_buffer_ != null) {
        text_timestamp_buffer_.rewind();

        if ( text_timestamp_buffer_.remaining() < text_bitmap.getByteCount())
            text_timestamp_buffer_ = null;
    }

    if (null == text_timestamp_buffer_ )
        text_timestamp_buffer_ = ByteBuffer.allocateDirect(text_bitmap.getByteCount());

    text_bitmap.copyPixelsToBuffer(text_timestamp_buffer_);

    int scale_w = 0, scale_h = 0, scale_filter_mode = 0;

    libPublisher.PostLayerImageRGBA8888ByteBuffer(handle_, index, left, top, text_timestamp_buffer_, 0,
            text_bitmap.getRowBytes(), text_bitmap.getWidth(), text_bitmap.getHeight(),
            0, 0, scale_w, scale_h, scale_filter_mode,0);

    int ret = scale_h > 0? scale_h : text_bitmap.getHeight();

    text_bitmap.recycle();

    return ret;
}

文字水印

文字水印不再赘述,主要注意的是文字的大小、颜色、位置。

private int postText1Layer(int index, int left, int top) {
    Bitmap text_bitmap = makeTextBitmap("文本水印一", getFontSize()+8,
            Color.argb(255, 200, 250, 0),
            false, 0,false);

    if (null == text_bitmap)
        return 0;

    ByteBuffer buffer = ByteBuffer.allocateDirect(text_bitmap.getByteCount());
    text_bitmap.copyPixelsToBuffer(buffer);

    libPublisher.PostLayerImageRGBA8888ByteBuffer(handle_, index, left, top, buffer, 0,
            text_bitmap.getRowBytes(), text_bitmap.getWidth(), text_bitmap.getHeight(),
            0, 0, 0, 0, 0,0);

    int ret = text_bitmap.getHeight();

    text_bitmap.recycle();

    return ret;
}

最终投递接口设计如下,接口不再赘述,几乎你期望的针对图像的处理,都已覆盖:

/**
   * 投递层RGBA8888图像,如果不需要Aplpha通道的话, 请使用RGBX8888接口, 效率高
   *
   * @param index: 层索引, 必须大于等于0, 注意:如果index是0的话,将忽略Alpha通道
   *
   * @param left: 层叠加的左上角坐标, 对于第0层的话传0
   *
   * @param top: 层叠加的左上角坐标, 对于第0层的话传0
   *
   * @param rgba_plane: rgba 图像数据
   *
   * @param offset: 图像偏移, 这个主要目的是用来做clip的, 一般传0
   *
   * @param row_stride: stride information
   *
   * @param width: width, 必须大于1, 如果是奇数, 将减1
   *
   * @param height: height, 必须大于1, 如果是奇数, 将减1
   *
   * @param  is_vertical_flip: 是否垂直翻转, 0不翻转, 1翻转
   *
   * @param  is_horizontal_flip:是否水平翻转, 0不翻转, 1翻转
   *
   * @param  scale_width: 缩放宽,必须是偶数, 0或负数不缩放
   *
   * @param  scale_height: 缩放高, 必须是偶数, 0或负数不缩放
   *
   * @param  scale_filter_mode: 缩放质量, 传0使用默认速度,可选等级范围是:[1,3],值越大缩放质量越好, 但速度越慢
   *
   * @param  rotation_degree: 顺时针旋转, 必须是0, 90, 180, 270, 注意:旋转是在缩放, 垂直/水品反转之后再做, 请留意顺序
   *
   * @return {0} if successful
   */
  public native int PostLayerImageRGBA8888ByteBuffer(long handle, int index, int left, int top,
                       ByteBuffer rgba_plane, int offset, int row_stride, int width, int height,
                       int is_vertical_flip,  int is_horizontal_flip,
                       int scale_width,  int scale_height, int scale_filter_mode,
                       int rotation_degree);

以上水印的显示控制,我们通过LayerPostThread封装处理:

/*
 * LayerPostThread实现动态水印封装
 * Author: https://daniusdk.com
 */
class LayerPostThread extends Thread
{
  private final int update_interval = 400; // 400 毫秒
  private volatile boolean is_exit_ = false;
  private long handle_ = 0;
  private int width_  = 0;
  private int height_ = 0;
  private volatile boolean is_text_ = false;
  private volatile boolean is_picture_ = false;
  private volatile boolean clear_flag_ = false;

  private final int timestamp_index_ = 1;
  private final int text1_index_ = 2;
  private final int text2_index_ = 3;
  private final int picture_index_ = 4;
  private final int rectangle_index_ = 5;

  ByteBuffer text_timestamp_buffer_ = null;
  ByteBuffer rectangle_buffer_ = null;

  @Override
  public void run() {
      text_timestamp_buffer_ = null;
      rectangle_buffer_ = null;

      if (0 == handle_)
          return;

      boolean is_posted_pitcure = false;
      boolean is_posted_text1 = false;
      boolean is_posted_text2 = false;

      int rectangle_aplha = 0;

      while(!is_exit_) {
          long t = SystemClock.elapsedRealtime();

          if (clear_flag_) {
              clear_flag_ = false;
              is_posted_pitcure = false;
              is_posted_text1 = false;
              is_posted_text2 = false;

              if (!is_text_ || !is_picture_) {
                  rectangle_aplha = 0;
                  libPublisher.RemoveLayer(handle_, rectangle_index_);
              }
          }

          int cur_h = 8;
          int ret = 0;

          if (!is_exit_ && is_text_) {
              ret = postTimestampLayer(timestamp_index_, 0, cur_h);
              if ( ret > 0 )
                  cur_h = align(cur_h + ret + 2, 2);
          }

          if(!is_exit_&& is_text_&&!is_posted_text1) {
              cur_h += 6;
              ret = postText1Layer(text1_index_, 0, cur_h);
              if ( ret > 0 ) {
                  is_posted_text1 = true;
                  cur_h = align(cur_h + ret + 2, 2);
              }
          }

          if (!is_exit_ && is_picture_ && !is_posted_pitcure) {
              ret = postPictureLayer(picture_index_, 0, cur_h);
              if ( ret > 0 ) {
                  is_posted_pitcure = true;
                  cur_h = align(cur_h + ret + 2, 2);
              }
          }

          if(!is_exit_&& is_text_&&!is_posted_text2) {
              postText2Layer(text2_index_);
              is_posted_text2 = true;
          }

          // 这个是演示一个矩形, 不需要可以屏蔽掉
          if (!is_exit_ && is_text_ && is_picture_) {
                  postRGBRectangle(rectangle_index_, rectangle_aplha);
                  rectangle_aplha += 8;
                  if (rectangle_aplha > 255)
                      rectangle_aplha = 0;
          }

          waitSleep((int)(SystemClock.elapsedRealtime() - t));
      }

      text_timestamp_buffer_ = null;
      rectangle_buffer_ = null;
  }

实时文字水印可以通过控制显示还是隐藏:

public void enableText(boolean is_text) {
      is_text_ = is_text;
      clear_flag_ = true;
      if (handle_ != 0) {
          libPublisher.EnableLayer(handle_, timestamp_index_, is_text_?1:0);
          libPublisher.EnableLayer(handle_, text1_index_, is_text_?1:0);
          libPublisher.EnableLayer(handle_, text2_index_, is_text_?1:0);
      }
  }

如需移除图层,也可以调用RemoveLayer()接口,具体设计如下:

/**
   * 启用或者停用视频层, 这个接口必须在StartXXX之后调用.
   *
   * @param index: 层索引, 必须大于0, 注意第0层不能停用
   *
   * @param  is_enable: 是否启用, 0停用, 1启用
   *
   * @return {0} if successful
   */
  public native int EnableLayer(long handle, int index, int is_enable);


  /**
   * 移除视频层, 这个接口必须在StartXXX之后调用.
   *
   * @param index: 层索引, 必须大于0, 注意第0层不能移除
   *
   * @return {0} if successful
   */
  public native int RemoveLayer(long handle, int index);

针对启动水印类型等外层封装:

private LayerPostThread layer_post_thread_ = null;

  private void startLayerPostThread() {
      if (3 == video_opt_) {
          if (null == layer_post_thread_) {
              layer_post_thread_ = new LayerPostThread();
              layer_post_thread_.startPost(publisherHandle, videoWidth, videoHeight, currentOrigentation, isHasTextWatermark(), isHasPictureWatermark());
          }
      }
  }

  private void stopLayerPostThread() {
      if (layer_post_thread_ != null) {
          layer_post_thread_.stopPost();
          layer_post_thread_ = null;
      }
  }

总结

Android平台获取设备电量信息和设备实时信号状态,然后叠加到视频view,投递出去,比如执法记录仪之类场景下,非常有价值,可以让GB28181平台侧,获取到更多需要的信息,扩展性极强。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/829351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python(六十)字典的创建

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

Kubernetes——理论基础

Kubernetes——理论基础 一、Kubernetes 概述1.K8S 是什么&#xff1f;2.为什么要用 K8S?3.Kubernetes 主要功能 二、Kubernetes 集群架构与组件三、Master 组件1.Kube-apiserver2.Kube-controller-manager3.Kube-scheduler4.配置存储中心——etcd 四、Node 组件1.Kubelet2.Ku…

城市供水管网水力模型的基本概念及理论

1.1引言 城市供水管网系统由大量管材各异、管径各异、铺设年代各异的管道&#xff0c;泵站&#xff0c;阀门&#xff0c; 水塔等多元素构成&#xff0c;因此决定了供水管网系统是一个拓扑结构庞杂、运行工况多变的巨系统。以前国内供水公司对铺设在地面以下的供水管网多以经验…

HCIP的mgre实验

题目 拓扑图 IP地址配置和缺省 R1 [r1]int g0/0/1 [r1-GigabitEthernet0/0/1]ip add 192.168.1.1 24 Aug 2 2023 20:38:20-08:00 r1 %%01IFNET/4/LINK_STATE(l)[0]:The line protocol IP on the interface GigabitEthernet0/0/1 has entered the UP state. [r1-GigabitEtherne…

【雕爷学编程】Arduino动手做(181)---Maixduino AI开发板4

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

云环境中使用飞蛾火焰和萨尔普群算法组合的工作流调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

AcWing 202. 最幸运的数字

AcWing 202. 最幸运的数字 思路&#xff1a; Code: #include<bits/stdc.h> using namespace std; typedef long long LL; int gcd(LL n,int m) {return m?gcd(m,n%m):n; } LL get_euler(LL x) { //求欧拉函数LL resx;for(int i2;i<x/i;i) {if(x%i0) {while(x%i0)…

经典CNN(三):DenseNet算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 1 前言 在计算机视觉领域&#xff0c;卷积神经网络&#xff08;CNN&#xff09;已经成为最主流的方法&#xff0c;比如GoogleNet&#xff0c;…

结算功能实现(小兔鲜儿)【Vue3】

退出登录 - 清空购物车列表 业务需求 在用户退出登录时,除了清除用户信息之外,也需要把购物车数据清空 // 清除购物车const clearCart () > {cartList.value []}// 退出时清除用户信息const clearUserInfo () > {userInfo.value {}// 执行清除购物车的actioncartS…

2023年攻防演练利器之必修高危漏洞合集(包含详细修复建议)

2023年攻防演练利器之必修高危漏洞合集&#xff08;包含详细修复建议&#xff09; 下载地址见盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1WeCC7oXFieDs4tBOh7bmWA?pwd12u4 提取码&#xff1a;12u4

PyTorch 中的累积梯度

https://stackoverflow.com/questions/62067400/understanding-accumulated-gradients-in-pytorch 有一个小的计算图&#xff0c;两次前向梯度累积的结果&#xff0c;可以看到梯度是严格相等的。 代码&#xff1a; import numpy as np import torchclass ExampleLinear(torch…

MongoDB文档--基本安装-linux安装(mongodb环境搭建)-docker安装(挂载数据卷)-以及详细版本对比

阿丹&#xff1a; 前面了解了mongodb的一些基本概念。本节文章对安装mongodb进行讲解以及汇总。 官网教程如下&#xff1a; 安装 MongoDB - MongoDB-CN-Manual 版本特性 下面是各个版本的选择请在安装以及选择版本的时候参考一下&#xff1a; MongoDB 2.x 版本&#xff1a…

TensorRT学习笔记--基于YoloV8检测图片和视频

1--完整项目 完整项目地址&#xff1a;https://github.com/liujf69/TensorRT-Demo git clone https://github.com/liujf69/TensorRT-Demo.gitcd TRT_YoloV8 2--模型转换 cd yolov8python gen_wts.py 3--编译项目 mkdir buildcd build cmake .. # 需要更改 CMakeLists.txt…

postgresSQL Extended Query执行过程和sharding-proxy的处理

pg Extended Query PostgreSQL: Documentation: 15: 55.2. Message Flow 多个阶段&#xff0c;可复用 Parse → DESCRIBE statement → SYNC Parse 解析&#xff0c; 将 sql 文本字符串&#xff0c;解析成 named preparedStatement 语句&#xff08;生命周期随session&#x…

数据安全能力框架模型-详细解读(一)

8月30日&#xff0c;奇安信集团正式发布“数据安全能力框架”&#xff0c;以及“数据安全概念运行图”&#xff08;数据安全ConOps&#xff09;&#xff0c;旨在为数字化转型不断深入的大型政企客户以及业内伙伴&#xff0c;提供基于甲方视角的数据安全全面图景&#xff0c;以及…

GESP2023年6月C++一级客观题

一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 以下不属于计算机输出设备的有&#xff08; &#xff09;。 A. 麦克风 B. 音箱 C. 打印机 D. 显示器 ChatGPT 是 OpenAI 研发的聊天机器人程序&#xff0c;它能通过理解和学习人类的语言 来进行对话&#xf…

ES6之Promise、Class类与模块化(Modules)

目录 PromiseClass类extendssuper Modules 模块系统export default 和对应importexport 和 import Promise Promise 是 ES6 引入的一种用于处理异步操作的对象。 它解决了传统回调函数&#xff08;callback&#xff09;模式中容易出现的回调地狱和代码可读性差的问题。 Promis…

Vue.js2+Cesium 四、WMS 服务加载,控制自图层显隐

Vue.js2Cesium 四、WMS 服务加载&#xff0c;控制自图层显隐 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"><div class"layer_container"><button id"btn">清除</button&g…

大模型开发(十六):从0到1构建一个高度自动化的AI项目开发流程(中)

全文共1w余字&#xff0c;预计阅读时间约40~60分钟 | 满满干货(附代码)&#xff0c;建议收藏&#xff01; 本文目标&#xff1a;通过LtM提示流程实现自动构建符合要求的函数&#xff0c;并通过实验逐步完整测试code_generate函数功能。 代码下载点这里 一、介绍 此篇文章为…

Redis两种持久化方案RDB持久化和AOF持久化

Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启…