将 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());
}
我正在尝试使用 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());
}