1. 为什么需要CameraX外接USB摄像头支持

在Android设备上使用外接USB摄像头一直是个头疼的问题。传统Camera API和Camera2 API虽然功能强大,但代码复杂度高,尤其是面对非手机设备(如工业平板、收银机、智能终端)时,外接摄像头的适配往往需要厂商定制驱动。我去年给某连锁超市改造自助收银机时就深有体会——他们的扫码摄像头都是USB接口的,用传统方式开发光是摄像头枚举就写了200多行代码。

CameraX 1.3.0-alpha版本带来的LENS_FACING_EXTERNAL配置项简直是救星。这个设计初衷很明确:为日益增长的Android非手机设备(特别是商用工控设备)提供标准化的外接摄像头支持。实测在搭载Android 10以上的设备,接入常见的Logitech C920、A4Tech PK-635G等USB摄像头都能即插即用。

与传统方案相比,CameraX的优势主要体现在三个层面:

  • 代码量减少70%:不再需要手动处理CameraDevice.StateCallback等复杂回调
  • 生命周期自动管理:不再出现因Activity销毁导致的摄像头资源泄漏
  • 统一API体验:无论内置还是外接摄像头,都用同一套CameraSelector控制

2. 环境配置与权限处理

2.1 基础环境搭建

先说说我踩过的坑:最开始在Android 9设备上测试时,发现USB摄像头始终无法识别。后来发现是Gradle配置漏了关键项。完整的环境配置应该包含以下步骤:

  1. settings.gradle中添加JitPack仓库(某些USB摄像头需要额外驱动支持):
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
  1. 模块级build.gradle的依赖配置(注意要用alpha版本):
dependencies {
    def camerax_version = "1.3.0-alpha04"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-view:1.3.0-alpha04"
}

提示:如果遇到"Manifest merger failed"错误,在AndroidManifest.xml中添加<uses-feature android:name="android.hardware.camera.any" android:required="false"/>

2.2 权限处理最佳实践

很多教程忽略了一个关键点:USB摄像头可能需要额外权限。除了常规的CAMERA权限,还需要在AndroidManifest.xml中添加:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 部分摄像头带麦克风 -->
<uses-feature android:name="android.hardware.usb.host" /> <!-- USB主机模式必须 -->

动态权限请求建议用以下代码(兼容Android 6.0+):

private val requiredPermissions = arrayOf(
    Manifest.permission.CAMERA,
    Manifest.permission.RECORD_AUDIO
)

fun checkPermissions() {
    val missingPermissions = requiredPermissions.filter {
        ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
    }
    
    if (missingPermissions.isNotEmpty()) {
        requestPermissions(missingPermissions.toTypedArray(), REQUEST_CODE_PERMISSIONS)
    } else {
        startCamera()
    }
}

3. 外接摄像头核心实现

3.1 CameraSelector配置技巧

CameraX 1.3.0-alpha最大的亮点就是支持LENS_FACING_EXTERNAL选择器。但实际使用时要注意几个细节:

val cameraSelector = CameraSelector.Builder()
    .requireLensFacing(CameraSelector.LENS_FACING_EXTERNAL)
    .build()

这个简单的配置背后有几个隐藏知识点:

  1. 多摄像头处理:当连接多个USB摄像头时,可以通过CameraProvider.getAvailableCameraInfos()获取设备列表,然后根据CameraInfo.getLensFacing()筛选外部设备
  2. 方向校正:外接摄像头的默认旋转角度可能不正确,需要通过Preview.Builder.setTargetRotation()手动校正
  3. 分辨率适配:建议使用QualitySelector指定分辨率,避免某些USB摄像头返回不支持的格式

3.2 完整的预览绑定流程

这是我优化过的绑定代码,增加了异常处理和设备检测:

private fun bindCameraUseCases() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    
    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
        
        // 检查是否有外接摄像头
        val hasExternalCamera = cameraProvider.availableCameraInfos.any {
            (it as CameraInfo).lensFacing == CameraSelector.LENS_FACING_EXTERNAL
        }
        
        if (!hasExternalCamera) {
            showToast("未检测到外接摄像头")
            return@addListener
        }

        val preview = Preview.Builder()
            .setTargetRotation(windowManager.defaultDisplay.rotation)
            .build()
            .also { it.setSurfaceProvider(previewView.surfaceProvider) }

        try {
            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(
                this, 
                CameraSelector.DEFAULT_EXTERNAL_CAMERA,
                preview
            )
        } catch (e: Exception) {
            Log.e(TAG, "绑定失败: ${e.message}")
        }
    }, ContextCompat.getMainExecutor(this))
}

4. 常见问题与兼容性处理

4.1 设备兼容性问题排查

在测试了20+款Android设备后,我整理出这份兼容性对照表:

设备类型 系统版本要求 常见问题 解决方案
商显平板 Android 10+ 供电不足导致掉线 使用带外接电源的USB Hub
工业手持终端 Android 9+ 图像旋转90度 设置targetRotation为Surface.ROTATION_90
自助收银机 Android 11+ 无法识别UVC摄像头 检查内核是否启用CONFIG_USB_VIDEO_CLASS

4.2 性能优化建议

  1. 帧率控制:某些USB摄像头在默认设置下帧率不稳定,可以通过以下方式锁定:
Preview.Builder()
    .setSupportedResolutions(listOf(Size(1280, 720)))
    .setDefaultResolution(Size(1280, 720))
  1. 内存优化:长时间预览时建议启用纹理缓存:
<androidx.camera.view.PreviewView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:implementationMode="COMPATIBLE" /> <!-- 使用TextureView -->
  1. 热插拔处理:注册USB设备监听广播:
private val usbReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == UsbManager.ACTION_USB_DEVICE_ATTACHED) {
            restartCamera()
        }
    }
}

override fun onResume() {
    super.onResume()
    registerReceiver(usbReceiver, IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED))
}

5. 扩展功能实现

5.1 多摄像头协同工作

在智能零售场景中,经常需要同时使用内置和外接摄像头。这是经过验证的多摄像头方案:

fun setupDualCamera() {
    val backCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    val externalCameraSelector = CameraSelector.DEFAULT_EXTERNAL_CAMERA
    
    val backPreview = Preview.Builder().build().apply {
        setSurfaceProvider(backPreviewView.surfaceProvider)
    }
    
    val externalPreview = Preview.Builder().build().apply {
        setSurfaceProvider(externalPreviewView.surfaceProvider)
    }
    
    cameraProvider.bindToLifecycle(
        this, 
        backCameraSelector, 
        backPreview
    )
    
    cameraProvider.bindToLifecycle(
        this, 
        externalCameraSelector, 
        externalPreview
    )
}

5.2 高级图像处理

结合CameraX的ImageAnalysis可以实现实时处理:

val imageAnalysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()
    .also {
        it.setAnalyzer(cameraExecutor) { imageProxy ->
            val rotationDegrees = imageProxy.imageInfo.rotationDegrees
            // 在这里添加OpenCV处理逻辑
            imageProxy.close()
        }
    }

最后提醒几个容易忽略的细节:

  • 外接摄像头的对焦模式可能需要手动设置为INFINITY
  • 某些工业摄像头需要先发送USB控制指令才能启动
  • 在Android 12+设备上需要单独申请BLUETOOTH_CONNECT权限才能使用部分无线摄像头
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐