Android 安卓摄像头2,连续拍照

Android 安卓摄像头2,连续拍照,android,freeze,preview,camera2,Android,Freeze,Preview,Camera2,我需要用Camera2 API连续拍照。它在高端设备(例如Nexus 5X)上运行良好,但在较慢的设备(例如三星Galaxy A3)上,预览会冻结 代码有点长,所以我只发布最相关的部分: 方法以启动我的预览: private void startPreview() { SurfaceTexture texture = mTextureView.getSurfaceTexture(); if(texture != null) { try {

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

代码有点长,所以我只发布最相关的部分:

方法以启动我的预览:

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(); }
}
这是我的图像阅读器:

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布尔值,并进行第一次拍照通话

总而言之,我使用了3个线程:

  • 一个用于预览
  • 一个是为了抓捕
  • 一个用于图像保存

造成这种冻结的原因是什么?

当您一直在较弱的设备上拍摄图像时,无法避免预览中的帧丢失。避免这种情况的唯一方法是在支持
TEMPLATE\u ZERO\u SHUTTER\u LAG
的设备上使用
RepositableCaptureSession
。关于这一点的文档非常可怕,找到一种实现它的方法可能是一次冒险。几个月前我遇到了这个问题,最后我找到了实现它的方法:

在这个答案中,您还可以找到一些Google CTS测试,它还实现了RepositableCaptureSession,并使用ZSL模板拍摄了一些突发捕获

最后,您还可以使用带有预览图面和图像读取器图面的
CaptureBuilder
,在这种情况下,您的预览将一直持续工作,并且您还可以将每个帧保存为新图片。但是你仍然会有冻结的问题

我还尝试使用一个处理程序实现突发捕获,该处理程序每100毫秒发送一个新的
capture
调用,第二个选项的性能非常好,避免了帧速率丢失,但您不会像两个
ImageReader
选项那样每秒捕获那么多


希望我的回答能对您有所帮助,API 2仍然有点复杂,关于它的示例或信息也不多。

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


但是camera 2 api提供了在拍摄静止图像时获取原始图像的可能性(这在我使用camera 1时的设备上是不可能的(华为P7、索尼Xperia E5、wiko UFeel)。使用此功能比捕获JPEG要快得多(可能是由于JPEG压缩),因此预览可以提前重新启动,并且预览冻结时间更短。当然,使用此解决方案,您必须在后台任务中将图片从YUV转换为JPEG。

嗨,我知道这是一个有点老的主题,但您能否分享mPreviewBackgroundHandler、mCaptureBackgroundHandler和我假设的mImageSavingBackgroundHandler的实现,我可以看到你调用takePicture方法来重复捕获,但是你第一次调用takePicture的地方是哪里?嗨,对不起,我不再为同一家公司工作,我不再有权访问此代码。