Android中手机录屏及数据解析:




获取手机录屏数据:

01.手机权限获取

//动态请求权限的数组
//请求权限的数组,可以在数组中添加你需要动态获取的权限
private val PERMISSIONS_REQUIRED = arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_EXTERNAL_STORAGE
    )

    //共享方法和常量
    companion object {
        //时间戳转换公式
        private const val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS"
        //照片的输出格式
        private const val PHOTO_EXTENSION = ".jpg"
        private const val TAG = "MyCameraXDemo"//标识
        /**  用于检查应用程序所需的所有权限是否都被授予的方法 **/
        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
        }
    }
     
    //共享方法和常量
    companion object {
        //时间戳转换公式
        private const val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS"
        //照片的输出格式
        private const val PHOTO_EXTENSION = ".jpg"
        private const val TAG = "MyCameraXDemo"//标识
        /**  用于检查应用程序所需的所有权限是否都被授予的方法 **/
        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
        }
    }

    //授权结果处理函数
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == PERMISSIONS_REQUEST_CODE) {
            if (PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
                //用户点击权限给予按钮
//                Toast.makeText(this, "获取到权限", Toast.LENGTH_LONG).show()
                //启动相机
                startCamera()
            } else {
                //用户点击拒绝按钮
                Toast.makeText(this, "请赋予权限,否则无法正常使用本软件", Toast.LENGTH_LONG).show()
                /* 可以跳转到权限设置界面,也可以关闭软件 */
            }
        }
    }

01.CameraX获取YUV数据获取

//启动相机
    private fun startCamera() {
        //打开相机
        viewFinder.post {
            // 跟踪这个视图所附加的显示
            displayId = viewFinder.display.displayId
            //初始化CameraX,并准备绑定相机用例
            setUpCamera()
        }
    }

    /** 初始化CameraX,并准备绑定相机用例  */
    private fun setUpCamera() {
        //请求CameraProvider
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        //检查 CameraProvider 可用性
        cameraProviderFuture.addListener(Runnable {
            //获取CameraProvider对象,并赋值我全局变量
            cameraProvider = cameraProviderFuture.get()
            // 创建和绑定相机用例
            bindCameraUseCases()
        }, ContextCompat.getMainExecutor(this))
    }

    // 创建和绑定相机用例(CameraX 的API的使用都在这个方法里)
    private fun bindCameraUseCases() {
        // 获取用于设置摄像头为全屏分辨率的屏幕指标
        val metrics = DisplayMetrics().also { viewFinder.display.getRealMetrics(it) }
        //获取预览框长宽比
        val screenAspectRatio = Util().aspectRatio(metrics.widthPixels, metrics.heightPixels)
        //获取屏幕的旋转方向
        val rotation = viewFinder.display.rotation
        //把全局CameraProvider赋值给局部对象
        val cameraProvider = cameraProvider ?: throw IllegalStateException("相机初始化失败")
        //创建Preview对象
        preview = Preview.Builder()
            .setTargetAspectRatio(screenAspectRatio)//设置预览的宽高比(或者分辨率)
            .setTargetRotation(rotation)//设定初始旋转方向(横竖)
            .build()
        //指定相机是 前置 还是 后置
        /*
        * 前置相机:CameraSelector.LENS_FACING_FRONT
        * 后置相机:CameraSelector.LENS_FACING_BACK
        * */
        val cameraSelector = CameraSelector.Builder()
            .requireLensFacing(lensFacing)
            .build()
        /*
        * 设置分析图片用例
        *
        * 作用:提供可供 CPU 访问的图片来执行图片处理、计算机视觉或机器学习推断
        * */
        imageAnalyzer = ImageAnalysis.Builder()
            .setTargetAspectRatio(screenAspectRatio)//指定分辨率
            .setTargetRotation(rotation)
             /*
             * 阻塞模式:ImageAnalysis.STRATEGY_BLOCK_PRODUCER  (在此模式下,执行器会依序从相应相机接收帧;这意味着,如果 analyze() 方法所用的时间超过单帧在当前帧速率下的延迟时间,所接收的帧便可能不再是最新的帧,因为在该方法返回之前,新帧会被阻止进入流水线)
             * 非阻塞模式: ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST (在此模式下,执行程序在调用 analyze() 方法时会从相机接收最新的可用帧。如果此方法所用的时间超过单帧在当前帧速率下的延迟时间,它可能会跳过某些帧,以便 analyze() 在下一次接收数据时获取相机流水线中的最新可用帧)
             * */
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)//图片分析模式
            .build()
            .also {
                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
                    //分析图片用例返回的结果
//                    Log.d("分析图片用例返回的结果:", "$luma")
                })
            }
        /*
        * 设置图片拍摄用例
        * ImageCapture的介绍:https://developer.android.google.cn/reference/androidx/camera/core/ImageCapture?hl=zh_cn#CAPTURE_MODE_MINIMIZE_LATENCY
        * */
        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)//设置拍照模式
            .setTargetAspectRatio(screenAspectRatio)
            .setTargetRotation(rotation)//设置旋转
            .build()
        //必须在重新绑定用例之前解除绑定
        cameraProvider.unbindAll()
        try {
            //将所选相机和任意用例绑定到生命周期 将 Preview 连接到 PreviewView
           var camera: Camera? = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, imageAnalyzer)
            //将取景器的视图与预览用例进行绑定
            preview?.setSurfaceProvider(viewFinder.surfaceProvider)
        } catch (exc: Exception) {
            Log.e("TAG", "相机启动失败", exc)
        }
    }

YUV数据的旋转:




学习产出: