简体   繁体   English

如何在camera2 API中使用reprocessCaptureRequest

[英]How to use a reprocessCaptureRequest with camera2 API

I'm trying to update a camera project to Android N and in consequence I'm moving my old CameraCaptureSession to a ReprocessableCaptureSession . 我正在尝试将相机项目更新为Android N,因此我将旧的CameraCaptureSession移动到ReprocessableCaptureSession I did it and it is working fine, but with this new feature I can use the CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG template in my device and I can reprocess frames with the reprocessCaptureRequest . 我做到了,它工作正常,但这个新功能,我可以使用CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG在我的设备模板,我可以重新处理与框架reprocessCaptureRequest

Here is where my problem appear. 这是我的问题出现的地方。 Because I don't find any example, and I don't really understand the little documentation about how to use a reprocessCaptureRequest : 因为我没有找到任何示例,我真的不了解如何使用reprocessCaptureRequest的小文档:

Each reprocess CaptureRequest processes one buffer from CameraCaptureSession's input Surface to all output Surfaces included in the reprocess capture request. 每个重新处理CaptureRequest处理从CameraCaptureSession的输入Surface到再处理捕获请求中包含的所有输出Surface的一个缓冲区。 The reprocess input images must be generated from one or multiple output images captured from the same camera device. 必须从从同一相机设备捕获的一个或多个输出图像生成再处理输入图像。 The application can provide input images to camera device via queueInputImage(Image). 应用程序可以通过queueInputImage(Image)向摄像机设备提供输入图像。 The application must use the capture result of one of those output images to create a reprocess capture request so that the camera device can use the information to achieve optimal reprocess image quality. 应用程序必须使用其中一个输出图像的捕获结果来创建重新处理捕获请求,以便摄像机设备可以使用该信息来实现最佳的再处理图像质量。 For camera devices that support only 1 output Surface, submitting a reprocess CaptureRequest with multiple output targets will result in a CaptureFailure. 对于仅支持1个输出Surface的相机设备,提交具有多个输出目标的重新处理CaptureRequest将导致CaptureFailure。

I tried to have a look to the CTS tests about the camera in google.sources but they do the same than me. 我试着看看关于google.sources中相机的CTS测试,但他们做的和我一样。 Using multiples imageReaders, saving the TotalCaptureResult of the pictures in a LinkedBlockingQueue<TotalCaptureResult> . 使用倍数imageReaders,在TotalCaptureResult LinkedBlockingQueue<TotalCaptureResult>保存图片的LinkedBlockingQueue<TotalCaptureResult> And later just calling: 后来只是打电话:

TotalCaptureResult totalCaptureResult = state.captureCallback.getTotalCaptureResult();
CaptureRequest.Builder reprocessCaptureRequest = cameraStore.state().cameraDevice.createReprocessCaptureRequest(totalCaptureResult);
reprocessCaptureRequest.addTarget(state.yuvImageReader.getSurface());
sessionStore.state().session.capture(reprocessCaptureRequest.build(), null, this.handlers.bg());

But it always throw me a RuntimeException: java.lang.RuntimeException: Capture failed: Reason 0 in frame 170, 但它总是抛出一个RuntimeException: java.lang.RuntimeException: Capture failed: Reason 0 in frame 170,

I just want to know which is the right way to work with the ReprocessableCaptureSession because I already tried everything and I don't know what I'm doing wrong. 我只是想知道使用ReprocessableCaptureSession的正确方法是什么,因为我已经尝试了所有内容而且我不知道我做错了什么。

Finally I found the solution to make my reprocessableCaptureSession work. 最后,我找到了使我的reprocessableCaptureSession工作的解决方案。 I use with Flux architecture so don't be confused when you see Dispatcher.dispatch(action) , just see it as a callback. 我使用Flux架构,所以当你看到Dispatcher.dispatch(action)时不要混淆,只是把它看作回调。 So, here is my code: 所以,这是我的代码:

First How the session is created: 首先如何创建会话:

 //Configure preview surface
     Size previewSize = previewState.previewSize;
     previewState.previewTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());

     ArrayList<Surface> targets = new ArrayList<>();
     for (SessionOutputTarget outputTarget : state.outputTargets) {
        Surface surface = outputTarget.getSurface();
        if (surface != null) targets.add(surface);
     }
     targets.add(previewState.previewSurface);
     CameraCharacteristics cameraCharacteristics = cameraStore.state().availableCameras.get(cameraStore.state().selectedCamera);
     Size size = CameraCharacteristicsUtil.getYuvOutputSizes(cameraCharacteristics).get(0);

     InputConfiguration inputConfiguration = new InputConfiguration(size.getWidth(),
        size.getHeight(), ImageFormat.YUV_420_888);

     CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
           if (sessionId != currentSessionId) {
              Timber.e("Session opened for an old open request, skipping. Current %d, Request %d", currentSessionId, sessionId);
              //performClose(session);
              return;
           }

           try {
              session.getInputSurface();
              //This call is irrelevant,
              //however session might have closed and this will throw an IllegalStateException.
              //This happens if another camera app (or this one in another PID) takes control
              //of the camera while its opening
           } catch (IllegalStateException e) {
              Timber.e("Another process took control of the camera while creating the session, aborting!");
           }

           Dispatcher.dispatchOnUi(new SessionOpenedAction(session));
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
           if (sessionId != currentSessionId) {
              Timber.e("Configure failed for an old open request, skipping. Current %d, request %d", currentSessionId, sessionId);
              return;
           }

           Timber.e("Failed to configure the session");
           Dispatcher.dispatchOnUi(new SessionFailedAction(session, new IllegalStateException("onConfigureFailed")));
        }
     };

     if (state.outputMode == OutputMode.PHOTO) {
        cameraState.cameraDevice.createReprocessableCaptureSession(inputConfiguration, targets, sessionStateCallback, handlers.bg());
     } else if (state.outputMode == OutputMode.VIDEO) {
        cameraState.cameraDevice.createCaptureSession(targets, sessionStateCallback, handlers.bg());
     }

  } catch (IllegalStateException | IllegalArgumentException e) {
     Timber.e(e, "Something went wrong trying to start the session");
  } catch (CameraAccessException e) {
     //Camera will throw CameraAccessException if another we try to open / close the
     //session very fast.
     Timber.e("Failed to access camera, it was closed");
  }

Photo session as been created with 4 surfaces(Preview, YUV(input), JPEG and RAW). 使用4个曲面创建的照片会话(预览,YUV(输入),JPEG和RAW)。 After it, I configure my imageWriter: 之后,我配置了我的imageWriter:

   Dispatcher.subscribe(Dispatcher.VERY_HIGH_PRIORITY, SessionOpenedAction.class)
     .filter(a -> isInPhotoMode())
     .subscribe(action -> {
           PhotoState newState = new PhotoState(state());
           newState.zslImageWriter = ImageWriter.newInstance(action.session.getInputSurface(), MAX_REPROCESS_IMAGES);
           setState(newState);
     });

Ok, now we have the ImageWriter and the session created. 好的,现在我们已经创建了ImageWriter和会话。 No we start the streaming with the repeating request: 不,我们通过重复请求开始流式传输:

         CaptureRequest.Builder captureRequestBuilder =
            cameraStore.state().cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);

         captureRequestBuilder.addTarget(previewStore.state().previewSurface);
         captureRequestBuilder.addTarget(photoStore.state().yuvImageReader.getSurface());
         state.session.setRepeatingRequest(captureRequestBuilder.build(), state.zslCaptureCallback, handlers.bg());

To don't add a lot of code, just say that the zslCaptureCallback is a custom callback which save in a LinkedBlockingQueue<TotalCaptureRequest> the X last TotalCaptureRequests. 为了不添加大量代码,只需说zslCaptureCallback是一个自定义回调,它在LinkedBlockingQueue<TotalCaptureRequest>保存X最后的TotalCaptureRequests。 Also, I do the same with the yuvImageReader(input one) saving the last X images in a queue. 另外,我对yuvImageReader(输入一个)执行相同的操作,将最后的X个图像保存在队列中。

Finally here is my "take photo" method: 最后这是我的“拍照”方法:

try {
        //Retrieve the last image stored by the zslImageReader
        Image image = zslImageReaderListener.getImage();
        //Retrieve the last totalCaptureResult from the zslCaptureCallback and create a reprocessableCaptureRequest with it
        TotalCaptureResult captureResult = sessionStore.state().zslCaptureCallback.getCaptureResult(image.getTimestamp());
        CaptureRequest.Builder captureRequest = cameraStore.state().cameraDevice.createReprocessCaptureRequest(captureResult);
        //Add the desired target and values to the captureRequest
        captureRequest.addTarget(state().jpegImageReader.getSurface());
        //Queued back to ImageWriter for future consumption.
        state.zslImageWriter.queueInputImage(image);
        //Drain all the unused and queued CapturedResult from the CaptureCallback
        sessionStore.state().zslCaptureCallback.drain();
        //Capture the desired frame
        CaptureRequest futureCaptureResult = captureRequest.build();
        sessionStore.state().session.capture(futureCaptureResult, new CameraCaptureSession.CaptureCallback() {
           @Override
           public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                          @NonNull CaptureRequest request,
                                          @NonNull TotalCaptureResult result) {
              Dispatcher.dispatchOnUi(new PhotoStatusChangedAction(PhotoState.Status.SUCCESS));
           }

           @Override
           public void onCaptureFailed(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull CaptureFailure failure) {
              super.onCaptureFailed(session, request, failure);
              Exception captureFailedException = new RuntimeException(
                 String.format("Capture failed: Reason %s in frame %d, was image captured? -> %s",
                    failure.getReason(),
                    failure.getFrameNumber(),
                    failure.wasImageCaptured()));
              Timber.e(captureFailedException, "Cannot take mediaType, capture failed!");

              Dispatcher.dispatchOnUi(new PhotoStatusChangedAction(PhotoState.Status.ERROR, captureFailedException));
           }
        }, this.handlers.bg());

        //Capture did not blow up, we are taking the photo now.
        newState.status = PhotoState.Status.TAKING;

     } catch (CameraAccessException | InterruptedException| IllegalStateException | IllegalArgumentException | SecurityException e) {
        Timber.e(e, "Cannot take picture, capture error!");
        newState.status = PhotoState.Status.ERROR;
     }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM