Android中如何将SurfaceView作为Preview的SurfaceProvider并让PreviewView显示USB相机?
Android CameraX USB相机绑定PreviewView异常问题
我在Android开发中有两个Fragment,各包含一个PreviewView用于显示相机画面:
- DeviceFragment:显示内置相机画面,运行正常
- UsbCameraFragment:应显示外接USB相机画面,但PreviewView始终显示内置相机画面,而该Fragment中的SurfaceView却能正常显示USB相机画面。
最终目标是通过CameraProvider.bindToLifecycle()处理相机画面,创建了Preview对象并设置PreviewView.getSurfaceProvider()作为其SurfaceProvider,但SurfaceView没有返回SurfaceProvider的方法,无法实现相同操作。
核心代码片段
private void bindCamera(ProcessCameraProvider cameraProvider) { ImageAnalysis imageAnalysis = imageAnalysisService.bindImageAnalysis(surfaceView); // 目前无关,后续可提供 Preview preview = new Preview.Builder().build(); preview.setSurfaceProvider(previewView.getSurfaceProvider()); // 此处希望使用SurfaceView或转换为PreviewView的SurfaceProvider CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK).build(); cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis); }
备注
- 注释掉
preview.setSurfaceProvider(previewView.getSurfaceProvider());后,SurfaceView可正常显示且不会被PreviewView覆盖 - 保留该代码时,SurfaceView会短暂显示后被PreviewView渲染的内置相机画面覆盖
- 尝试给CameraSelector添加过滤器,但发现USB相机未在可用相机列表中
问题根源
- CameraSelector匹配错误:默认的
LENS_FACING_BACK仅匹配内置后置相机,USB相机不会被归类到该镜头朝向,且默认不在CameraX的可用相机列表中。 - PreviewView抢占渲染:绑定内置相机的Preview到PreviewView后,会覆盖SurfaceView的USB相机画面,CameraX绑定的渲染优先级更高。
解决步骤
1. 让CameraX识别USB相机
CameraX默认不支持USB相机,需添加扩展依赖并通过过滤器筛选:
- 首先在
build.gradle添加依赖:
implementation "androidx.camera:camera2-extensions:${cameraXVersion}"
- 然后创建USB相机专属的CameraSelector:
CameraSelector usbCameraSelector = new CameraSelector.Builder() .addCameraFilter(cameraInfos -> { List<CameraInfo> usbCameras = new ArrayList<>(); for (CameraInfo info : cameraInfos) { CameraCharacteristics characteristics = ((Camera2CameraInfo) info).getCameraCharacteristics(); Integer deviceType = characteristics.get(CameraCharacteristics.INFO_DEVICE_TYPE); if (deviceType != null && deviceType == CameraCharacteristics.INFO_DEVICE_TYPE_EXTERNAL) { usbCameras.add(info); } } return usbCameras; }) .build();
2. 为SurfaceView自定义SurfaceProvider
如果要继续用SurfaceView配合bindToLifecycle,可以实现自定义SurfaceProvider适配:
preview.setSurfaceProvider(new SurfaceProvider() { @Override public void onSurfaceRequested(@NonNull SurfaceRequest request) { Surface surface = surfaceView.getHolder().getSurface(); if (surface.isValid()) { request.provideSurface(surface, Executors.newSingleThreadExecutor(), result -> { // 处理Surface使用结果 }); } else { surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { request.provideSurface(holder.getSurface(), Executors.newSingleThreadExecutor(), result -> {}); surfaceView.getHolder().removeCallback(this); } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) {} }); } } });
3. 绑定USB相机
替换原CameraSelector,确保绑定的是USB相机:
cameraProvider.bindToLifecycle(this, usbCameraSelector, preview, imageAnalysis);
额外注意事项
- 权限:需申请
CAMERA权限,USB相机还需动态申请USB_PERMISSION并处理设备权限请求。 - 兼容性:不同USB相机的适配差异较大,部分设备可能需要特定驱动支持。
内容的提问来源于stack exchange,提问作者Marcelo
相关产品推荐
相关产品推荐

