2017-02-13 234 views
1

我需要用Camera2 API连续拍照。它适用于高端设备(例如Nexus 5X),但在较慢的设备(例如三星Galaxy A3)上,预览会冻结。Android Camera2,连续拍照

的代码是有点长,所以我张贴只有最相关的部分:

方法调用,开始我的预览:

private void startPreview() { 

    SurfaceTexture texture = mTextureView.getSurfaceTexture(); 

    if(texture != null) { 

     try { 

      // We configure the size of default buffer to be the size of camera preview we want. 
      texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 

      // This is the output Surface we need to start preview. 
      Surface surface = new Surface(texture); 

      // We set up a CaptureRequest.Builder with the output Surface. 
      mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 
      mPreviewRequestBuilder.addTarget(surface); 

      // Here, we create a CameraCaptureSession for camera preview. 
      mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { 

        @Override 
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 

         // If the camera is already closed, return: 
         if (mCameraDevice == null) { return; } 

         // When the session is ready, we start displaying the preview. 
         mCaptureSession = cameraCaptureSession; 

         // Auto focus should be continuous for camera preview. 
         mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 
         mPreviewRequest = mPreviewRequestBuilder.build(); 

         // Start the preview 
         try { mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mPreviewBackgroundHandler); } 
         catch (CameraAccessException e) { e.printStackTrace(); } 
        } 

        @Override 
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { 
         Log.e(TAG, "Configure failed"); 
        } 
       }, null 
      ); 
     } 
     catch (CameraAccessException e) { e.printStackTrace(); } 
    } 
} 

方法调用,用于拍摄照片:

private void takePicture() { 

    try { 

     CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 
     captureBuilder.addTarget(mImageReader.getSurface()); 
     mCaptureSession.capture(captureBuilder.build(), null, mCaptureBackgroundHandler); 
    } 
    catch (CameraAccessException e) { e.printStackTrace(); } 
} 

这里是我的ImageReader:

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { 

    @Override 
    public void onImageAvailable(final ImageReader reader) { 

     mSaveBackgroundHandler.post(new Runnable() { 

      @Override 
      public void run() { 

       // Set the destination file: 
       File destination = new File(getExternalFilesDir(null), "image_" + mNumberOfImages + ".jpg"); 
       mNumberOfImages++; 

       // Acquire the latest image: 
       Image image = reader.acquireLatestImage(); 

       // Save the image: 
       ByteBuffer buffer = image.getPlanes()[0].getBuffer(); 
       byte[] bytes = new byte[buffer.remaining()]; 
       buffer.get(bytes); 

       FileOutputStream output = null; 
       try { 
        output = new FileOutputStream(destination); 
        output.write(bytes); 
       } 
       catch (IOException e) { e.printStackTrace(); } 
       finally { 

        image.close(); 

        if (null != output) { 

         try { output.close(); } 
         catch (IOException e) { e.printStackTrace(); } 
        } 
       } 

       // Take a new picture if needed: 
       if(mIsTakingPictures) { 
        takePicture(); 
       } 
      } 
     }); 
    } 
}; 

我有一个按钮,切换mIsTakingPictures布尔值,并进行第一次takePicture调用。

总括来说,我使用3个线程:

  • 一个用于预览
  • 一个用于捕获
  • 一个将图像保存

可能是什么原因这个冻结?

回答

0

当您在弱设备上拍摄图像时,无法避免在预览中丢失帧。避免这种情况的唯一方法是支持TEMPLATE_ZERO_SHUTTER_LAG并使用reprocessableCaptureSession的设备。关于这个文件是非常可怕的,找到一个方法来实现它可以是一个奥德赛。我在几个月前有这样的问题,终于让我找到实现它的方式:在这个问题的答案,你也可以找到一些谷歌CTS测试的这也实现了ReprocessableCaptureSession和拍摄一些突发与ZSL模板捕获

How to use a reprocessCaptureRequest with camera2 API

最后,您还可以使用带预览界面和图片阅读器界面的CaptureBuilder,在这种情况下,您的预览将始终保持正常工作,并且您还可以将每个框架保存为新照片。但是你仍然会遇到冻结问题。

我也试过用一个处理程序实现一个突发捕获,该处理程序每​​100毫秒调用一次新的capture调用,这第二个选项在性能上非常好,并且避免了帧速率丢失,但是每秒捕获次数不会像两个ImageReader选项。

希望我的回答会对你有所帮助,API 2仍然有点复杂,并没有太多的例子或信息。

1

我在低端设备上注意到的一件事:即使使用相机1 api,预览也会在捕捉后停止,因此必须手动重新启动,因此捕捉高分辨率图片时会产生小的预览冻结。

但是相机2 api提供了在拍摄静止图像时获得原始图像的可能性(在使用相机1(华为P7,索尼Xperia E5,wiko UFeel)时我无法使用这些设备)。使用此功能比捕捉JPEG要快得多(可能是由于JPEG压缩),所以可以在较早的时间重新开始预览,并且缩短预览时间。当然,使用这种解决方案,您将不得不在后台任务中将图片从YUV转换为JPEG。