• 作者:老汪软件技巧
  • 发表时间:2024-09-30 15:02
  • 浏览量:

​技术背景

我们在做Android平台GB28181设备接入模块、轻量级RTSP服务模块和RTMP推流模块的时候,遇到这样的技术诉求,开发者希望把实时CPU占用、电池电量信息等叠加在视频界面。

获取CPU占用率

Android平台获取CPU占用情况,可以读取/proc/stat文件,解析出各个 CPU 时间参数,然后计算出 CPU 的使用率,示例代码如下:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class MainActivity extends Activity {
    private Handler handler = new Handler(Looper.getMainLooper());
    private Runnable runnable;
    private long prevTotalCpuTime = 0;
    private long prevIdleCpuTime = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    // 读取 /proc/stat 文件获取 CPU 时间信息
                    BufferedReader reader = new BufferedReader(new FileReader("/proc/stat"));
                    String line = reader.readLine();
                    reader.close();
                    String[] tokens = line.split("\\s+");
                    long user = Long.parseLong(tokens[2]);
                    long nice = Long.parseLong(tokens[3]);
                    long system = Long.parseLong(tokens[4]);
                    long idle = Long.parseLong(tokens[5]);
                    long iowait = Long.parseLong(tokens[6]);
                    long irq = Long.parseLong(tokens[7]);
                    long softirq = Long.parseLong(tokens[8]);
                    long totalCpuTime = user + nice + system + idle + iowait + irq + softirq;
                    long idleCpuTime = idle;
                    // 计算 CPU 使用率
                    if (prevTotalCpuTime!= 0 && prevIdleCpuTime!= 0) {
                        long diffTotalCpuTime = totalCpuTime - prevTotalCpuTime;
                        long diffIdleCpuTime = idleCpuTime - prevIdleCpuTime;
                        float cpuUsage = ((diffTotalCpuTime - diffIdleCpuTime) / (float) diffTotalCpuTime) * 100;
                        Log.d("CPU_USAGE", "CPU Usage: " + cpuUsage + "%");
                    }
                    prevTotalCpuTime = totalCpuTime;
                    prevIdleCpuTime = idleCpuTime;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // 延迟一段时间后再次执行
                handler.postDelayed(this, 1000);
            }
        };
        // 启动获取 CPU 使用率的任务
        handler.post(runnable);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止任务
        handler.removeCallbacks(runnable);
    }
}

使用BatteryManager类获取电池电量

在 Android 中,可以使用BatteryManager类来获取电池电量信息。以下是具体步骤:

一、注册广播接收器

在你的 Android 组件(如 Activity 或 Service)中注册一个广播接收器来监听电池状态变化的广播。可以在onCreate方法中进行注册。

IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(batteryReceiver, filter);

这里创建了一个IntentFilter来指定要监听的广播为电池状态变化广播(Intent.ACTION_BATTERY_CHANGED),然后使用registerReceiver方法注册广播接收器。

二、创建广播接收器

创建一个广播接收器类来处理电池状态变化的广播,如下所示:

private BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        int batteryPercentage = (level / (float)scale) * 100;
        // 这里可以根据获取到的电池电量信息进行相应的处理
    }
};

在广播接收器的onReceive方法中,可以从广播意图中获取电池电量的级别(BatteryManager.EXTRA_LEVEL)和总刻度(BatteryManager.EXTRA_SCALE),然后计算出电池电量的百分比。

当前使用电量_电池占有率_

三、取消注册广播接收器

在适当的时候,如组件的onDestroy方法中,取消注册广播接收器,以避免资源泄漏:

unregisterReceiver(batteryReceiver);

这样,当电池状态发生变化时,你的广播接收器将会收到通知,并可以获取到电池电量信息进行相应的处理。

需要注意的是,获取电池电量信息可能需要相应的权限。在 AndroidManifest.xml 文件中添加以下权限声明:

name="android.permission.BATTERY_STATS" />

场景应用

以大牛直播SDK的GB28181设备接入模块为例,摄像头实时视频采集,可以叠加上实时CPU占用和实时电量信息,亦或实时时间信息,会对场景带来很大益处,比如CPU占用率或电量信息,简单来说可以通过bitmap把文字读取下来,投递到底层比如jni层即可,非常方便。下面就文字和图片水印做个简短的技术普及。

文字水印

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

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;
}

png水印

png水印,除了常规的位置需要注意之外,还涉及到logo水印的大小问题,为此,我们添加了缩放效果,可以缩放后,再贴到图层,确保以更合适的比例展示在图层期望位置。

private int postPictureLayer(int index, int left, int top) {
    Bitmap bitmap = getAssetsBitmap();
    if (null == bitmap) {
        Log.e(TAG, "postPitcureLayer getAssetsBitmap is null");
        return 0;
    }
    if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
        Log.e(TAG, "postPitcureLayer config is not ARGB_8888, config:" + Bitmap.Config.ARGB_8888);
        return 0;
    }
    ByteBuffer buffer = ByteBuffer.allocateDirect(bitmap.getByteCount());
    bitmap.copyPixelsToBuffer(buffer);
    final int w = bitmap.getWidth();
    final int h = bitmap.getHeight();
    if ( w < 2 || h < 2 )
        return 0;
    int scale_w = 0, scale_h = 0, scale_filter_mode = 0;
    final float r_w = width_ - left; // 有可能负数
    final float r_h = height_ - top; // 有可能负数
    if (w > r_w || h > r_h) {
        float s_w = w;
        float s_h = h;
        // 0.85的10次方是0.19687, 缩放到0.2倍差不多了
        for ( int i = 0; i < 10; ++i)  {
            s_w *= 0.85f;
            s_h *= 0.85f;
            if (s_w < r_w && s_h < r_h )
                break;
        }
        if (s_w > r_w || s_h > r_h)
            return 0;
        // 如果小于16就算了,太小看也看不见
        if (s_w < 16.0f || s_h < 16.0f)
            return  0;
        scale_w = align((int)(s_w + 0.5f), 2);
        scale_h = align( (int)(s_h + 0.5f), 2);
        scale_filter_mode = 3;
    }
    /*
    if ( scale_w > 0 && scale_h > 0)
        Log.i(TAG, "postTextLayer scale_w:" + scale_w + ", scale_h:" + scale_h + " w:" + w + ", h:" + h) ; */
    libPublisher.PostLayerImageRGBA8888ByteBuffer(handle_, index, left, top, buffer, 0, bitmap.getRowBytes(), w, h,
            0, 0, scale_w, scale_h, scale_filter_mode,0);
    int ret = scale_h > 0 ? scale_h : bitmap.getHeight();
    bitmap.recycle();
    return ret;
}