Camera2 录制视频,按下录制按钮后屏幕冻结仅在使用前置摄像头时和在某些设备上

如何解决Camera2 录制视频,按下录制按钮后屏幕冻结仅在使用前置摄像头时和在某些设备上

我浏览了所有互联网并没有找到任何解决方案,我正在使用 Camera2 api 从我的前置摄像头录制视频,我已经在多个设备上进行了测试并且它工作正常,但是当我在我的三星 Galaxy 上尝试时3、我按下录制按钮后,有时录制工作,有时相机预览冻结,您可以在下面找到我实现的代码

  1. 通过延迟加载创建预览和记录请求
private val previewRequest: CaptureRequest? by lazy {
        mCaptureSession.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
            addTarget(viewFinder.holder.surface)
        }.build()
    }


    private val recordRequest: CaptureRequest by lazy {
        mCaptureSession.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
            addTarget(viewFinder.holder.surface)
            addTarget(mMediaRecorder.surface)
        }.build()
    }
  1. 我正在使用 AutoFitSurfaceView,onSurfaceCreated 我正在执行以下操作:
when (cameraDirection) { // I am getting this variable to see what camera I should open
                    CameraDirection.BACK -> { //getCameraPosition gets the cameraId for the given //LENS_FACING direction
                        mCameraId = getCameraPosition(CameraCharacteristics.LENS_FACING_BACK)
                    }
                    CameraDirection.FRONT -> {
                        mCameraId = getCameraPosition(CameraCharacteristics.LENS_FACING_FRONT)
                    }
                    else -> {
                        mCameraId = getCameraPosition(CameraCharacteristics.LENS_FACING_BACK)
                    }
                }
                characteristics = cameraManager.getCameraCharacteristics(mCameraId!!)

                // Selects appropriate preview size and configures view finder
                mPreviewSize = getPreviewOutputSize(
                    viewFinder.display,characteristics,SurfaceHolder::class.java
                )
                // Selects appropriate video size
                mVideoSize = getPreviewOutputSize(
                    viewFinder.display,MediaRecorder::class.java
                )
                viewFinder.setAspectRatio(mPreviewSize.width,mPreviewSize.height)

                // To ensure that size is set,initialize camera in the view's thread
                viewFinder.post {
                    initializeCamera()
                }
  1. initializeCamera() 函数看起来像这样
private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {
        //viewFinder is the AutoFitSurfaceView
        camera = openCamera(cameraManager,mCameraId!!,cameraHandler)
        setupMediaRecorder()

        val targets = listOf(viewFinder.holder.surface)
        camera.createCaptureSession(targets,object : CameraCaptureSession.StateCallback() {
            override fun onConfigured(session: CameraCaptureSession) {
                mCaptureSession = session
                session.setRepeatingRequest(previewRequest!!,null,cameraHandler)
            }

            override fun onConfigureFailed(session: CameraCaptureSession) {

            }

        },cameraHandler)
    }
  1. openCamera() 如下所示
private suspend fun openCamera(
        manager: CameraManager,cameraId: String,handler: Handler? = null
    ): CameraDevice = suspendCancellableCoroutine { cont ->
        manager.openCamera(cameraId,object : CameraDevice.StateCallback() {
            override fun onOpened(device: CameraDevice) = cont.resume(device)

            override fun onDisconnected(device: CameraDevice) {
                finish()
            }

            override fun onError(device: CameraDevice,error: Int) {
                val msg = when (error) {
                    ERROR_CAMERA_DEVICE -> "Fatal (device)"
                    ERROR_CAMERA_DISABLED -> "Device policy"
                    ERROR_CAMERA_IN_USE -> "Camera in use"
                    ERROR_CAMERA_SERVICE -> "Fatal (service)"
                    ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use"
                    else -> "Unknown"
                }
                val exc = RuntimeException("Camera $cameraId error: ($error) $msg")
                if (cont.isActive) cont.resumeWithException(exc)
            }
        },handler)
    }
  1. setupMediaRecorder() 看起来像这样
private fun setupMediaRecorder() {
        mMediaRecorder = MediaRecorder()
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        mMediaRecorder.setOutputFile(outputFile.absolutePath)
        Log.i("CAMERA_INFO",mCameraId!!)
        val profile = CamcorderProfile.get(mCameraId!!.toInt(),CamcorderProfile.QUALITY_LOW)
        Log.i("CAMERA_INFO","Frame Rate: " + profile.videoFrameRate)
        mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate)
        mMediaRecorder.setVideoFrameRate(profile.videoFrameRate)
        mMediaRecorder.setVideoSize(mPreviewSize.width,mPreviewSize.height)
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
        when (mCameraDirection) {
            CameraDirection.BACK -> {
                mMediaRecorder.setOrientationHint(90)
            }
            CameraDirection.FRONT -> {
                mMediaRecorder.setOrientationHint(270)
            }
            else -> {

            }
        }
        mMediaRecorder.prepare()
    }

说明: 首先调用 initializeCamera(),在这个函数中我设置了 Camera 和 previewSession 并准备 MediaRecorder 在用户按下录制按钮时开始录制

在用户按下录制按钮后,我正在执行以下操作:

  • 关闭预览会话
  • 创建记录会话
  • 会话配置成功后,我正在设置通过延迟加载初始化的recordRequest

代码如下:

button_record_video.setOnClickListener {
            mCaptureSession.close() //Closing the previewSession
            try {

                camera.createCaptureSession( //Creating the record session passing the viewFinder surface //and the MediaRecorder session
                    listOf(
                        viewFinder.holder.surface,mMediaRecorder.surface
                    ),object : CameraCaptureSession.StateCallback() {
                        override fun onConfigured(session: CameraCaptureSession) {
                            mCaptureSession = session
                            session.setRepeatingRequest(recordRequest,cameraHandler)
                            mMediaRecorder.start()
                        }

                        override fun onConfigureFailed(p0: CameraCaptureSession) {

                        }

                    },cameraHandler
                )
            } catch (e: Exception) {

            }

        }

PS:此代码在从后置摄像头捕捉时有效,对于前置摄像头,它在某些设备上工作,偶尔在其他设备上失败(设备测试此代码偶尔会失败三星 Galaxy S3)。

需要更多信息,我很乐意提供 提前致谢

解决方法

我没有在您的代码中看到在不同于 Main/GUI 的线程上处理相机。事实上,我从来没有见过一个好的教程来做到这一点 - 所以 - 这就是我所做的:

声明handlerThread

private HandlerThread mBackgroundHandlerThread;
private Handler mBackgroundHandler;

启动和停止后台线程

private void startBackgroundThread() {
    mBackgroundThread = new HandlerThread("CameraBackground");
    mBackgroundThread.start();
    mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}

private void stopBackgroundThread() {
    if(mBackgroundHandlerThread == null)
        return;
    try {
        mBackgroundHandlerThread.quitSafely();
        mBackgroundHandlerThread.join();
        mBackgroundHandlerThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        Log.e(TAG,e.toString());
    }
    catch (Exception e) {
        Log.e(TAG,e.toString());
    }
}

神奇的是当你从后台线程的回调中更新 gui

mCameraDevice.createCaptureSession(surfaces,new CameraCaptureSession.StateCallback()
{
    @Override
    public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) 
    {
        mPreviewSession = cameraCaptureSession;
        State = Constants.RecordingState.RECORDING;
        // Start recording
        if (mMediaRecorder != null)
        {
            mMediaRecorder.start();
        
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Updating the GUI
                    mButtonVideo.setEnabled(true);
                    txtMainMessage.setText("Recording");
                }
            });
        }
     @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                Activity activity = getActivity();
                if (null != activity) {
                    Toast.makeText(activity,"Failed",Toast.LENGTH_SHORT).show();
                }
            }
        
    },mBackgroundHandler);
}
,

由于您正在立即准备媒体录制器,因此您可以只进行一个捕获会话,在预览和录制之间共享。然后,一旦您想开始录制,只需将录制目标 Surface 添加到捕获请求中即可。

这避免了创建新捕获会话时出现的故障,并且可能与您发现问题的设备更兼容。此外,您可能希望查看 MediaCodec 的持久记录表面,以避免必须为第二个记录创建新会话(如果您希望支持该会话)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-