Android 循环录制最近一段时间的视频

本文介绍如何在Android中实现循环录制最近一段时间的视频,通过数据缓存来存储视频帧,并在发现问题时将指定时间范围的视频帧组合成MP4文件。文章详细阐述了权限申请、屏幕录屏的实现以及录屏数据处理步骤,包括JNI函数的使用和BipBuffer的高效缓存机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android 循环录制最近一段时间的视频

在日常开发测试中,往往发生问题了再去想办法复现录屏、抓取日志的工作,往往会出现偶现问题很难复现,导致问题很难定位。在这里给出一个能抓取历史操作视频的解决方案:

  1. 将录屏的视频帧数据一帧帧的缓存到一块固定大小的内存中(空间循环利用)
  2. 发现问题时,触发混合器(MediaMuxer)将指定时间范围的视频帧数据取出存储为指定的mp4文件

数据缓存

数据缓存用来解决历史数据保存,需要合理的分配内存大小,根据自己的实际情况(手机屏幕分辨率、多长时间的视频记录等等)选择合适的大小。

提供四个JNI函数:

/**
 * 视频帧数据缓存工具类
 *
 * @author likunlun
 * @since 2021/12/19
 */
object FrameDataCacheUtils {
   
   
    /**
     * 初始化缓存
     *
     * @param cacheSize 缓存空间大小,单位 M
     * @param isDebug 是否debug模式
     */
    external fun initCache(cacheSize: Int, isDebug: Boolean)

    /**
     * 添加新的一帧数据到缓存
     *
     * @param timestamp 时间戳 ms
     * @param isKeyFrame 是否关键帧
     * @param frameData 帧数据
     * @param length 数据长度
     */
    external fun addFrameData(
        timestamp: Long,
        isKeyFrame: Boolean,
        frameData: ByteArray,
        length: Int
    )

    /**
     * 通过时间戳从缓存区中获取最近的一个关键帧数据
     *
     * @param timestamp 传入的时间戳 ms
     * @param curTimestamp 查找到的关键帧的时间戳 ms
     * @param frameData 帧数据
     * @param length 数据长度
     * @return 0成功,非0失败
     */
    external fun getFirstFrameData(
        timestamp: Long,
        curTimestamp: LongArray,
        frameData: ByteArray,
        length: IntArray
    ): Int

    /**
     * 通过时间戳从缓存区中获取下一帧数据
     *
     * @param preTimestamp 前一帧的时间戳 ms
     * @param curTimestamp 当前帧的时间戳 ms
     * @param frameData 帧数据
     * @param length 数据长度
     * @param isKeyFrame 是否关键帧(I帧)true I帧
     * @return 0成功,非0失败
     */
    external fun getNextFrameData(
        preTimestamp: Long,
        curTimestamp: LongArray,
        frameData: ByteArray,
        length: IntArray,
        isKeyFrame: BooleanArray
    ): Int

    init {
   
   
        System.loadLibrary("framedatacachejni")
    }
}

缓存框架源码:https://download.csdn.net/download/lkl22/73404181

开启屏幕录屏

一、申请权限

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

二、创建service

    <application>
        <service
            android:name=".service.ScreenCaptureService"
            android:enabled="true"
            android:exported="false"
            android:foregroundServiceType="mediaProjection" />
    </application>
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
   
   
        createNotificationChannel()
        val resultCode = intent.getIntExtra(ScreenCapture.KEY_RESULT_CODE, -1)
        val cacheSize = intent.getIntExtra(ScreenCapture.KEY_CACHE_SIZE, ScreenCapture.DEFAULT_CACHE_SIZE)
        val resultData = intent.getParcelableExtra<Intent>(ScreenCapture.KEY_DATA)
        resultData?.apply {
   
   
            ScreenCaptureManager.instance.startRecord(resultCode, this, cacheSize)
            LogUtils.e(TAG, "startRecord.")
        }
        return super.onStartCommand(intent, flags, startId)
    }

    private fun createNotificationChannel() {
   
   
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   
   
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            if (notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
   
   
                val channel = NotificationChannel(
                    NOTIFICATION_CHANNEL_ID,
                    NOTIFICATION_CHANNEL_NAME,
                    NotificationManager.IMPORTANCE_DEFAULT
                )
                notificationManager.createNotificationChannel(channel)
            }
        }

        val builder =
            NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) //获取一个Notification构造器
                .setContentTitle("ScreenCapture") // 设置下拉列表里的标题
                .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
                .setContentText("is running......") // 设置上下文内容
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setWhen(System.currentTimeMillis()) // 设置该通知发生的时间
        LogUtils.d(TAG, "startForeground")
        startForeground(NOTIFICATION_ID, builder.build())
    }

三、开启录屏

1、创建MediaProjectionManager对象
    private val mProjectionManager: MediaProjectionManager <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值