将 ImageReader 添加为目标表面的 Camera2API 滞后于我的相机预览

Camera2API adding ImageReader as a target surface laggs my camera preview

我正在尝试使用 ImageReader 处理帧,同时还在 SurfaceView 上显示摄像机预览。如果我添加的唯一目标表面是 SurfaceView,则预览会很流畅,但如果我添加 ImageReader 作为第二个目标,则预览开始严重滞后。为什么会这样?我尝试创建一个 HandlerThread 和一个 Handler 并使用它们,但它没有改变任何东西。我目前唯一的想法是获取然后关闭下一张图像。

public void startBackgroundThread() {
    handlerThread = new HandlerThread("Image Processing Thread");
    handlerThread.start();
    handler = new Handler(handlerThread.getLooper());
}

在此处配置相机:

public void configureCamera() {
    try {
        CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

        Size[] sizes = configs.getOutputSizes(ImageFormat.YUV_420_888);
        imageReader = ImageReader.newInstance(sizes[0].getWidth(), sizes[0].getHeight(), ImageFormat.YUV_420_888, 1);

        Range<Integer>[] ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
        fpsRange = ranges[ranges.length - 1];

        imageReader.setOnImageAvailableListener(this, handler);

        cameraSurfaceView.getHolder().setFixedSize(sizes[0].getWidth(), sizes[0].getHeight());
    } catch (CameraAccessException | NullPointerException e) {
        e.printStackTrace();
    }
}

开始相机预览:

private void startCamera() {
    try {
        cameraManager.openCamera("0", new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                cameraDevice = camera;

                try {
                    cameraDevice.createCaptureSession(Arrays.asList(cameraSurfaceView.getHolder().getSurface(), imageReader.getSurface()),
                            new CameraCaptureSession.StateCallback() {
                                @Override
                                public void onConfigured(@NonNull CameraCaptureSession session) {
                                    captureSession = session;
                                    try {
                                        requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

                                        requestBuilder.addTarget(cameraSurfaceView.getHolder().getSurface());
                                        requestBuilder.addTarget(imageReader.getSurface());

                                        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
                                        requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);

                                        captureRequest = requestBuilder.build();
                                        cameraReady = true;

                                        captureSession.setRepeatingRequest(captureRequest, null, handler);

                                        onStartButtonClick(startButton);
                                    } catch (CameraAccessException e) {
                                        e.printStackTrace();
                                    }
                                }

                                @Override
                                public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                                }
                            }, null);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

然后是 ImageReader 的 OnImageAvailableListener(目前是我的主要 activity):

@Override
public void onImageAvailable(ImageReader reader) {
    reader.acquireNextImage().close();
}

这些代码片段目前都驻留在我的主 activity 中。我使用的 phone 是用于测试目的的摩托罗拉 Moto X Play。 cameraSurfaceView 只是一个简单的 SurfaceView,没有任何自定义。

从缩进中看出来有点困难,但看起来您是运行代码片段中当前线程上的相机处理程序。

cameraManager.openCamera 中的最后一个参数告诉它使用哪个线程——如果它为 null,它只使用当前线程。来自 Android 文档:

Parameters cameraId String: The unique identifier of the camera device to open

callback CameraDevice.StateCallback: The callback which is invoked once the camera is opened

handler Handler: The handler on which the callback should be invoked, or null to use the current thread's looper.

如果您查看 GitHub 上的 Camera2Basic () 示例,您可以看到它们指定了一个单独的处理程序:

manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);

这是在一个单独的线程上启动的:

    /**
     * Starts a background thread and its {@link Handler}.
     */
    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }