logo

Android Camera2 实战:全屏预览与实时帧处理全解析

作者:半吊子全栈工匠2025.09.19 11:24浏览量:0

简介:本文深入探讨Android Camera2 API实现全屏预览及实时获取预览帧进行图像处理的技术方案,涵盖Camera2基础架构、全屏预览配置、帧捕获回调机制及实时处理优化策略,为开发者提供完整的实现路径。

Android Camera2 全屏预览+实时获取预览帧进行图像处理

一、Camera2 API基础架构解析

Camera2 API作为Android 5.0引入的全新摄像头控制框架,采用三级管道架构:CameraDevice(设备层)、CameraCaptureSession(会话层)、CaptureRequest(请求层)。这种设计模式使开发者能够精细控制摄像头参数,包括曝光补偿、白平衡、对焦模式等。

1.1 核心组件说明

  • CameraManager:系统摄像头服务入口,通过getCameraIdList()获取可用设备列表
  • CameraCharacteristics:包含设备特性信息(如传感器方向、硬件支持级别)
  • Surface:作为图像数据的输出目标,支持SurfaceViewTextureViewImageReader等多种类型

1.2 初始化流程

  1. // 1. 获取CameraManager实例
  2. CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
  3. // 2. 选择后置摄像头(通常ID为"0")
  4. String cameraId = manager.getCameraIdList()[0];
  5. // 3. 获取设备特性
  6. CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
  7. // 4. 配置预览Surface
  8. SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
  9. surfaceTexture.setDefaultBufferSize(1920, 1080);
  10. Surface previewSurface = new Surface(surfaceTexture);

二、全屏预览实现方案

2.1 视图组件选择

  • SurfaceView:双缓冲机制,性能最优但Z轴控制困难
  • TextureView:支持透明、变换动画,适合UI集成场景
  • ImageReader:用于获取原始图像数据,需配合其他视图使用

2.2 最佳实践配置

  1. // 配置预览请求
  2. CaptureRequest.Builder previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  3. previewBuilder.addTarget(previewSurface);
  4. // 设置自动对焦
  5. previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
  6. // 优化预览帧率
  7. previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
  8. new Range<>(30, 30)); // 固定30fps

2.3 屏幕适配策略

  1. 计算最佳预览尺寸

    1. StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    2. Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);
    3. // 根据屏幕宽高比选择最接近的尺寸
    4. Size optimalSize = findOptimalSize(previewSizes, screenWidth, screenHeight);
  2. 处理方向旋转

    1. int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
    2. int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    3. int totalRotation = (sensorOrientation + displayRotation + 270) % 360;

三、实时帧捕获与处理

3.1 ImageReader配置要点

  1. // 创建ImageReader(YUV_420_888格式兼容性最好)
  2. ImageReader imageReader = ImageReader.newInstance(
  3. 1280, 720, ImageFormat.YUV_420_888, 2);
  4. // 设置帧到达监听
  5. imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
  6. @Override
  7. public void onImageAvailable(ImageReader reader) {
  8. try (Image image = reader.acquireLatestImage()) {
  9. // 处理YUV数据
  10. processYUVImage(image);
  11. }
  12. }
  13. }, backgroundHandler);

3.2 同步捕获机制

  1. // 创建捕获会话时同时添加预览和图像Surface
  2. List<Surface> outputSurfaces = new ArrayList<>();
  3. outputSurfaces.add(previewSurface);
  4. outputSurfaces.add(imageReader.getSurface());
  5. cameraDevice.createCaptureSession(outputSurfaces,
  6. new CameraCaptureSession.StateCallback() {
  7. @Override
  8. public void onConfigured(CameraCaptureSession session) {
  9. try {
  10. // 创建重复请求
  11. CaptureRequest request = previewBuilder.build();
  12. session.setRepeatingRequest(request, null, backgroundHandler);
  13. } catch (CameraAccessException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }, backgroundHandler);

3.3 YUV数据处理优化

  1. private void processYUVImage(Image image) {
  2. // 获取YUV平面
  3. Image.Plane yPlane = image.getPlanes()[0];
  4. Image.Plane uPlane = image.getPlanes()[1];
  5. Image.Plane vPlane = image.getPlanes()[2];
  6. ByteBuffer yBuffer = yPlane.getBuffer();
  7. ByteBuffer uBuffer = uPlane.getBuffer();
  8. ByteBuffer vBuffer = vPlane.getBuffer();
  9. // 转换为NV21格式(兼容Android Bitmap)
  10. byte[] nv21 = yuv420ToNv21(
  11. yBuffer, uBuffer, vBuffer,
  12. image.getWidth(), image.getHeight()
  13. );
  14. // 创建Bitmap进行显示或处理
  15. YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21,
  16. image.getWidth(), image.getHeight(), null);
  17. ByteArrayOutputStream os = new ByteArrayOutputStream();
  18. yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, os);
  19. Bitmap bitmap = BitmapFactory.decodeByteArray(os.toByteArray(), 0, os.size());
  20. // 释放资源
  21. image.close();
  22. }

四、性能优化策略

4.1 帧率控制方案

  • 动态调整目标FPS:根据场景复杂度在15-30fps间切换
  • 丢帧策略:当处理耗时超过帧间隔时,主动丢弃后续帧
    1. long lastProcessingTime = 0;
    2. private boolean shouldProcessFrame(long currentTimestamp) {
    3. long elapsed = currentTimestamp - lastProcessingTime;
    4. if (elapsed < 33_000_000) { // 30fps间隔
    5. return false;
    6. }
    7. lastProcessingTime = currentTimestamp;
    8. return true;
    9. }

4.2 线程模型设计

  • 双线程架构
    • 摄像头线程:负责帧捕获和基础处理
    • 渲染线程:负责UI更新和复杂计算
      ```java
      // 创建带缓冲的线程池
      ExecutorService cameraExecutor = Executors.newFixedThreadPool(2);
      ExecutorService renderExecutor = Executors.newSingleThreadExecutor();

// 帧处理流程
imageReader.setOnImageAvailableListener(image -> {
cameraExecutor.execute(() -> {
// 基础处理
byte[] processedData = preProcess(image);
renderExecutor.execute(() -> {
// UI更新
updateUI(processedData);
});
});
}, backgroundHandler);

  1. ### 4.3 内存管理技巧
  2. - 使用`ImageReader``setMaxImages()`控制缓存数量
  3. - 及时关闭不再使用的`Image`对象
  4. - 采用对象池模式复用处理资源
  5. ## 五、常见问题解决方案
  6. ### 5.1 预览变形问题
  7. **原因**:SurfaceView/TextureView尺寸与摄像头输出尺寸不匹配
  8. **解决**:
  9. ```java
  10. // 在SurfaceHolder.Callback中动态调整
  11. @Override
  12. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  13. if (cameraCaptureSession != null) {
  14. try {
  15. // 重新配置会话
  16. cameraCaptureSession.stopRepeating();
  17. configurePreviewSize(width, height);
  18. startPreview();
  19. } catch (CameraAccessException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }

5.2 帧丢失问题

诊断步骤

  1. 检查CameraCaptureSession.CaptureCallback中的onCaptureFailed()回调
  2. 监控CameraDevice.StateCallback中的错误状态
  3. 使用CameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)验证设备能力

优化方案

  • 降低输出分辨率
  • 减少同时处理的Surface数量
  • 检查后台应用资源占用

六、高级功能扩展

6.1 多摄像头同步

  1. // 获取物理摄像头ID列表
  2. String[] physicalCameraIds = manager.getCameraIdList();
  3. // 创建逻辑摄像头设备(Android 9+)
  4. CameraDevice logicalDevice = ...; // 通过CameraManager.openCamera()创建
  5. // 配置跨摄像头会话
  6. List<Surface> multiSurfaces = new ArrayList<>();
  7. multiSurfaces.add(wideAngleSurface);
  8. multiSurfaces.add(telephotoSurface);
  9. logicalDevice.createCaptureSession(multiSurfaces, ...);

6.2 深度数据获取

  1. // 检查深度功能支持
  2. Integer depthAvailable = characteristics.get(
  3. CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES
  4. ).contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
  5. if (depthAvailable != null && depthAvailable) {
  6. // 配置深度输出
  7. Size depthSize = characteristics.get(
  8. CameraCharacteristics.DEPTH_OUTPUT_SIZES_SUPPORTED
  9. )[0];
  10. ImageReader depthReader = ImageReader.newInstance(
  11. depthSize.getWidth(), depthSize.getHeight(),
  12. ImageFormat.DEPTH16, 2
  13. );
  14. }

七、完整代码示例

  1. public class Camera2PreviewProcessor {
  2. private CameraDevice cameraDevice;
  3. private CameraCaptureSession captureSession;
  4. private ImageReader imageReader;
  5. private Handler backgroundHandler;
  6. public void startCamera(Context context, TextureView textureView) {
  7. // 1. 初始化组件
  8. CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
  9. String cameraId = manager.getCameraIdList()[0];
  10. // 2. 配置预览Surface
  11. SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
  12. surfaceTexture.setDefaultBufferSize(1920, 1080);
  13. Surface previewSurface = new Surface(surfaceTexture);
  14. // 3. 配置ImageReader
  15. imageReader = ImageReader.newInstance(640, 480,
  16. ImageFormat.YUV_420_888, 2);
  17. // 4. 打开摄像头
  18. try {
  19. manager.openCamera(cameraId, new CameraDevice.StateCallback() {
  20. @Override
  21. public void onOpened(CameraDevice device) {
  22. cameraDevice = device;
  23. startPreviewSession(previewSurface);
  24. }
  25. // 其他回调方法...
  26. }, backgroundHandler);
  27. } catch (CameraAccessException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. private void startPreviewSession(Surface previewSurface) {
  32. try {
  33. // 创建捕获请求构建器
  34. CaptureRequest.Builder previewBuilder =
  35. cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  36. previewBuilder.addTarget(previewSurface);
  37. previewBuilder.addTarget(imageReader.getSurface());
  38. // 创建捕获会话
  39. List<Surface> surfaces = Arrays.asList(previewSurface, imageReader.getSurface());
  40. cameraDevice.createCaptureSession(surfaces,
  41. new CameraCaptureSession.StateCallback() {
  42. @Override
  43. public void onConfigured(CameraCaptureSession session) {
  44. captureSession = session;
  45. try {
  46. session.setRepeatingRequest(
  47. previewBuilder.build(), null, backgroundHandler);
  48. } catch (CameraAccessException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. // 其他回调方法...
  53. }, backgroundHandler);
  54. } catch (CameraAccessException e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. // 图像处理回调
  59. imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
  60. @Override
  61. public void onImageAvailable(ImageReader reader) {
  62. try (Image image = reader.acquireLatestImage()) {
  63. // 在此实现图像处理逻辑
  64. processImage(image);
  65. }
  66. }
  67. }, backgroundHandler);
  68. }

八、最佳实践总结

  1. 资源管理:始终在onPause()中释放摄像头资源
  2. 错误处理:实现完整的CameraDevice.StateCallbackCameraCaptureSession.StateCallback
  3. 权限管理:动态请求CAMERAWRITE_EXTERNAL_STORAGE权限
  4. 兼容性处理:检查CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL
  5. 性能监控:使用System.nanoTime()测量帧处理耗时

通过以上技术方案,开发者可以实现稳定的Camera2全屏预览,并构建高效的实时图像处理流水线。实际开发中需根据具体设备特性进行参数调优,建议使用Camera2 API Interop Test应用进行兼容性测试。

相关文章推荐

发表评论