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

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

 技术背景

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

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

如何获取电量信息

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

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

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

    BatteryManagerbatteryManager= (BatteryManager) getSystemService(Context.BATTERY_SERVICE);  
    intbatteryLevel=batteryManager.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);  
    intbatteryScale=batteryManager.getIntExtra(BatteryManager.EXTRA_SCALE, -1);  
    intbatteryPlugType=batteryManager.getIntExtra(BatteryManager.EXTRA_PLUGED, -1);  
    booleanisCharging=batteryManager.getIntExtra(BatteryManager.EXTRA_CHARGING, 0) ==1;

    image.gif

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

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

    PowerManagerpowerManager= (PowerManager) getSystemService(Context.POWER_SERVICE);  
    booleanisDeviceIdle=powerManager.isDeviceIdle();  
    booleanisPowerSaveMode=powerManager.isPowerSaveMode();

    image.gif

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

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

    UsageStatsManagerusageStatsManager= (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);  
    longtime=System.currentTimeMillis();  
    UsageStatsstats=usageStatsManager.queryStats(UsageStatsManager.INTERVAL_DAILY, time);  
    longtotalTime=stats.getTotalTime();  
    longscreenTime=stats.getScreenTime();  
    floatbatteryLevel= (totalTime*100) /screenTime;

    image.gif

    如何获取设备信号

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

    TelephonyManagertelephonyManager= (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
    intsignalStrength=telephonyManager.getSignalStrength();

    image.gif

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

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

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

    image.gif

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

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

    camera2.jpg

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

    privateintpostTimestampLayer(intindex, intleft, inttop) {
    Bitmaptext_bitmap=makeTextBitmap(makeTimestampString(), getFontSize(),
    Color.argb(255, 0, 0, 0), true, Color.argb(255, 255, 255, 255),true);
    if (null==text_bitmap)
    return0;
    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_);
    intscale_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);
    intret=scale_h>0?scale_h : text_bitmap.getHeight();
    text_bitmap.recycle();
    returnret;
    }

    image.gif

    文字水印

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

    privateintpostText1Layer(intindex, intleft, inttop) {
    Bitmaptext_bitmap=makeTextBitmap("文本水印一", getFontSize()+8,
    Color.argb(255, 200, 250, 0),
    false, 0,false);
    if (null==text_bitmap)
    return0;
    ByteBufferbuffer=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);
    intret=text_bitmap.getHeight();
    text_bitmap.recycle();
    returnret;
    }

    image.gif

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

    /*** 投递层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*/publicnativeintPostLayerImageRGBA8888ByteBuffer(longhandle, intindex, intleft, inttop,
    ByteBufferrgba_plane, intoffset, introw_stride, intwidth, intheight,
    intis_vertical_flip,  intis_horizontal_flip,
    intscale_width,  intscale_height, intscale_filter_mode,
    introtation_degree);

    image.gif

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

    /** LayerPostThread实现动态水印封装* Author: https://daniusdk.com*/classLayerPostThreadextendsThread{
    privatefinalintupdate_interval=400; // 400 毫秒privatevolatilebooleanis_exit_=false;
    privatelonghandle_=0;
    privateintwidth_=0;
    privateintheight_=0;
    privatevolatilebooleanis_text_=false;
    privatevolatilebooleanis_picture_=false;
    privatevolatilebooleanclear_flag_=false;
    privatefinalinttimestamp_index_=1;
    privatefinalinttext1_index_=2;
    privatefinalinttext2_index_=3;
    privatefinalintpicture_index_=4;
    privatefinalintrectangle_index_=5;
    ByteBuffertext_timestamp_buffer_=null;
    ByteBufferrectangle_buffer_=null;
    @Overridepublicvoidrun() {
    text_timestamp_buffer_=null;
    rectangle_buffer_=null;
    if (0==handle_)
    return;
    booleanis_posted_pitcure=false;
    booleanis_posted_text1=false;
    booleanis_posted_text2=false;
    intrectangle_aplha=0;
    while(!is_exit_) {
    longt=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_);
                  }
              }
    intcur_h=8;
    intret=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;
      }

    image.gif

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

    publicvoidenableText(booleanis_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);
          }
      }

    image.gif

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

    /*** 启用或者停用视频层, 这个接口必须在StartXXX之后调用.** @param index: 层索引, 必须大于0, 注意第0层不能停用** @param  is_enable: 是否启用, 0停用, 1启用** @return {0} if successful*/publicnativeintEnableLayer(longhandle, intindex, intis_enable);
    /*** 移除视频层, 这个接口必须在StartXXX之后调用.** @param index: 层索引, 必须大于0, 注意第0层不能移除** @return {0} if successful*/publicnativeintRemoveLayer(longhandle, intindex);

    image.gif

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

    privateLayerPostThreadlayer_post_thread_=null;
    privatevoidstartLayerPostThread() {
    if (3==video_opt_) {
    if (null==layer_post_thread_) {
    layer_post_thread_=newLayerPostThread();
    layer_post_thread_.startPost(publisherHandle, videoWidth, videoHeight, currentOrigentation, isHasTextWatermark(), isHasPictureWatermark());
              }
          }
      }
    privatevoidstopLayerPostThread() {
    if (layer_post_thread_!=null) {
    layer_post_thread_.stopPost();
    layer_post_thread_=null;
          }
      }

    image.gif

    总结

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

    相关文章
    |
    2月前
    |
    Java Android开发 Swift
    安卓与iOS开发对比:平台选择对项目成功的影响
    【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
    122 1
    |
    3月前
    |
    IDE Android开发 iOS开发
    探索Android与iOS开发的差异:平台选择对项目成功的影响
    【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
    |
    20天前
    |
    IDE 开发工具 Android开发
    移动应用开发之旅:探索Android和iOS平台
    在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
    44 17
    |
    2月前
    |
    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开发知识可参考相关书籍。
    116 0
    FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
    |
    2月前
    |
    Java Unix Linux
    Android Studio中Terminal运行./gradlew clean build提示错误信息
    遇到 `./gradlew clean build`命令执行出错时,首先应检查错误信息的具体内容,这通常会指向问题的根源。从权限、环境配置、依赖下载、版本兼容性到项目配置本身,逐一排查并应用相应的解决措施。记住,保持耐心,逐步解决问题,往往复杂问题都是由简单原因引起的。
    369 2
    |
    3月前
    |
    监控 Android开发 iOS开发
    深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
    本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
    120 6
    |
    3月前
    |
    开发工具 Android开发 iOS开发
    安卓与iOS开发环境对比:选择适合你的平台
    【9月更文挑战第26天】在移动应用开发的广阔天地中,安卓和iOS是两大巨头。它们各自拥有独特的优势和挑战,影响着开发者的选择和决策。本文将深入探讨这两个平台的开发环境,帮助你理解它们的核心差异,并指导你根据个人或项目需求做出明智的选择。无论你是初学者还是资深开发者,了解这些平台的异同都至关重要。让我们一起探索,找到最适合你的那片开发天地。
    |
    3月前
    |
    Android开发 开发者
    Android平台无纸化同屏如何实现实时录像功能
    Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
    |
    3月前
    |
    安全 API 开发工具
    Android平台RTMP推送|轻量级RTSP服务如何实现麦克风|扬声器声音采集切换
    Android平台扬声器播放声音的采集,在无纸化同屏等场景下,意义很大,早期低版本的Android设备,是没法直接采集扬声器audio的(从Android 10开始支持),所以,如果需要采集扬声器audio,需要先做系统版本判断,添加相应的权限。
    |
    3月前
    |
    编解码 开发工具 Android开发
    Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
    Android平台屏幕采集、音频播放声音采集、麦克风采集编码打包推送到RTMP和轻量级RTSP服务的相关技术实现,做成高稳定低延迟的同屏系统,还需要有配套好的RTMP、RTSP直播播放器