如何在Android上的camera2 API中重用MediaRecorder的Surface?

grA*_*uit 6 video android mediarecorder android-camera2

因此,整个周末我都使用camera2 API玩了。现在,我开始了解事物如何连接在一起。在测试api来实现视频录制应用程序时,我碰壁了。

我首先根据需要更改了Android Camera2Video示例。让我感到困扰的是,在每次录制过程之后,都将重新创建摄像机会话。更糟糕的是,当开始录制会话时,发生的情况是预览会话将首先被破坏并创建录制会话。录制会话完成后,它将被销毁并创建一个新的预览会话。

该文档明确指出:

创建会话是一项昂贵的操作,可能需要几百毫秒的时间... CameraCaptureSession文档

当我点击“录制”并停止时,结果看起来非常丑陋,并且屏幕结巴。我想改善这种行为,所以我摆弄了代码。

现在要做的是创建我的一个,并且仅CameraSession在其中添加我的预览图面(a TextureView)以及MediaRecorder通过调用其getSurface方法从已经创建的图面中添加该图面。这对于第一个视频效果很好,但是当我尝试捕获第二个视频时,我得到了一个IllegalArgumentException: Bad argument passed to camera service。我想,这是因为表面MediaRecorder是我传递给CameraSession在它的创作以某种方式破坏或改变时,我重置MediaRecorder准备一个新的记录。

我现在的问题是,有什么办法可以解决这个问题?(setInputSurface(Surface surface))可能是,但api级别太高,所以我没有对其进行测试。

以下是有关代码段的快速概述:

  1. 设置 MediaRecorder

    private void setUpMediaRecorder() throws IOException {
        if (mMediaRecorder == null) {
            mMediaRecorder = new MediaRecorder();
        }
        mMediaRecorder.setVideoEncodingBitRate(5000000);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setVideoFrameRate(24);
        mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mMediaRecorder.setOrientationHint(SENSOR_ORIENTATION_DEFAULT_DEGREES);
        mNextVideoAbsolutePath = getVideoFilePath();
        mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);
        mMediaRecorder.prepare();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建所有强大的录音会话

    SurfaceTexture texture = mTextureView.getSurfaceTexture();
    texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
    
    List<Surface> surfaces = new ArrayList<>();
    // Set up Surface for the camera preview
    mPreviewSurface = new Surface(texture);
    surfaces.add(mPreviewSurface);
    
    // Set up Surface for the MediaRecorder
    mRecorderSurface = mMediaRecorder.getSurface();
    surfaces.add(mRecorderSurface);
    
    // create the capture session
    mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
    
        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            mCameraSession = cameraCaptureSession;
    
            // now that the session is created, start using it  for the preview
            showPreview();
        }
    
        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
            ....
                }
            }
        }, mBackgroundHandler);
    } catch (CameraAccessException) {
        e.printStackTrace();
    }
    
    void showPreview() {
        mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        mPreviewBuilder.addTarget(mPreviewSurface);
        mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 开始录制视频

    mVideoBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    mVideoBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
    mVideoBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    mVideoBuilder.addTarget(mPreviewSurface);
    mVideoBuilder.addTarget(mRecorderSurface);
    // set the request for the capture
    mCameraSession.setRepeatingRequest(mVideoBuilder.build(), null, mBackgroundHandler);
    // Start recording
    mMediaRecorder.start();
    
    Run Code Online (Sandbox Code Playgroud)
  4. 停止录音

    mMediaRecorder.stop();
    mMediaRecorder.reset();
    showPreview();
    setUpMediaRecorder(); // this is key to not get an error from the MediaRecorder
    
    Run Code Online (Sandbox Code Playgroud)

所有这些工作都完美无缺,并且视频录制开始和停止没有任何打h!太棒了,但是当我回到步骤3(4之后)时,我得到了上面提到的IllegalArgumentException: Bad argument passed to camera service。我一直把头撞在墙上,但找不到解决这个问题的方法。

任何帮助是极大的赞赏!

谢谢!

小智 3

查看MediaRecorder#setInputSurface(android.view.Surface)

将录像机配置为在使用 SURFACE 视频源时使用持久表面。

当我试图弄清楚如何重用 MediaRecorder 捕获表面时,我也偶然发现了它。这样,您可以将持久表面设置为捕获会话的输出表面之一,并且不必重新创建捕获会话只是为了更改从新的prepare()调用生成的MediaRecorder表面。

Google Nexus 和 Pixel 相机应用程序能够开始和停止视频录制,预览时不会出现任何卡顿,因此绝对有可能以某种方式做到这一点。