我需要使用Camera2 API连续拍照。它在高端设备(比如Nexus5X)上工作得很好,但对于速度较慢的设备(比如三星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(); }
}
这是我的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个线程:
冻结的原因是什么?
发布于 2017-02-15 14:54:09
当你在脆弱的设备上拍摄图像时,不可能避免在你的预览中丢失帧。避免这种情况的唯一方法是在支持TEMPLATE_ZERO_SHUTTER_LAG
和使用reprocessableCaptureSession
的设备上。关于这方面的文档是相当可怕的,并且找到一种方法来实现它可能是一个奥德赛。几个月前,我遇到了这个问题,最后我找到了实现它的方法:
How to use a reprocessCaptureRequest with camera2 API
在这个答案中,您还可以找到一些Google测试,它也实现了ReprocessableCaptureSession,并使用ZSL模板拍摄了一些突发捕获。
最后,您还可以使用带有预览面和图像读取器界面的CaptureBuilder
,在这种情况下,预览将继续工作,并且还可以将每一帧保存为新图片。但你还是会有冻结的问题。
我还尝试使用一个处理程序实现一个突发捕获,该处理程序每100毫秒发送一个新的capture
调用,第二个选项的性能相当好,并且避免了帧速率丢失,但是您不会像两个ImageReader
选项那样每秒获得那么多的捕获。
希望我的答案能对你有所帮助,API 2仍然有点复杂,而且没有太多的例子或信息。
发布于 2017-03-13 16:48:36
在低端设备上,我注意到了一件事:预览在捕获后停止,即使使用了Camer1API,因此必须手动重新启动它,从而在捕获高分辨率图片时产生一个小的预览冻结。
但是,相机2 api提供了在拍摄静态捕捉时获取原始图像的可能性(这在我使用相机1(华为P7、索尼Xperia E5、wiko UFeel)时是不可能的)。使用此功能比捕获JPEG (可能是由于JPEG压缩)要快得多,因此可以更早地重新启动预览,并且预览冻结时间更短。当然,使用此解决方案,您必须在后台任务中将图片从YUV转换为JPEG。
https://stackoverflow.com/questions/42215106
复制相似问题