使用AndroidUSBCamera库开发安卓多摄像头预览和录制应用
使用AndroidUSBCamera库开发多摄像头预览和录制应用
在现代Android应用开发中,支持多摄像头预览和录制的需求越来越常见,特别是在监控、视频会议和工业检测等领域。本文将详细介绍如何使用开源库AndroidUSBCamera开发一个支持3个摄像头同时预览和录制的应用,并分享在开发过程中遇到的关键问题和解决方案。
1. 项目背景与依赖配置
1.1 依赖配置的坑
在使用AndroidUSBCamera库时,我们首先遇到了依赖配置的问题。按照官方文档,应该在[build.gradle](file://C:\Users\xxx\AndroidStudioProjects\UVC3Camera\app\libs\AndroidUSBCamera-3.3.3\app\build.gradle)中添加JitPack仓库和依赖:
// 在settings.gradle.kts中添加JitPack仓库
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
// 在app/build.gradle.kts中添加依赖
dependencies {
implementation("com.github.jiangdongguo:AndroidUSBCamera:3.3.3")
}
但在实际操作中,我们发现JitPack方式无法下载依赖。通过深入研究,我们发现需要使用Liferay仓库才能成功下载:
// 在settings.gradle.kts中添加Liferay仓库
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// 添加Liferay仓库以解决特定依赖问题
maven { url = uri("https://repository.liferay.com/nexus/content/repositories/public") }
}
}
这个坑让我们花费了不少时间,所以特别提醒大家在遇到类似问题时可以尝试这个解决方案。
2. 核心功能实现
2.1 自定义UI与摄像头连接
项目中我们创建了一个自定义的[CameraFragment](file:///C:/Users/xxx/AndroidStudioProjects/UVC3Camera/app/src/main/java/com/cz/uvc3camera/CameraFragment.kt#L89-L1267)类,继承自MultiCameraFragment以支持多摄像头功能。核心实现包括:
-
UI布局设计:使用三个[AspectRatioTextureView](file:///C:/Users/xxx/AndroidStudioProjects/UVC3Camera/app/libs/AndroidUSBCamera-3.3.3/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt#L43-L236)组件分别显示三个摄像头的预览画面,并为每个摄像头配备独立的录制和停止按钮。
-
摄像头连接管理:通过重写[onCameraConnected](file://C:\Users\xxx\AndroidStudioProjects\UVC3Camera\app\src\main\java\com\cz\uvc3camera\CameraFragment.kt#L509-L558)方法处理摄像头连接事件,为每个连接的摄像头分配预览视图:
override fun onCameraConnected(camera: MultiCameraClient.ICamera) {
addLog("摄像头已连接: ${camera.getUsbDevice().deviceName}")
// 为摄像头分配预览视图
val (textureView, cameraIndex) = when {
camera1 == null -> {
camera1 = camera
Pair(textureView1, 1)
}
camera2 == null -> {
camera2 = camera
Pair(textureView2, 2)
}
camera3 == null -> {
camera3 = camera
Pair(textureView3, 3)
}
else -> {
addLog("已达到最大摄像头数量")
return
}
}
// 等待TextureView准备好后再打开摄像头
if (textureView.isAvailable) {
openCamera(camera, textureView)
} else {
textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
addLog("TextureView surface available for camera: ${camera.getUsbDevice().deviceName}")
openCamera(camera, textureView)
}
// ... 其他方法
}
}
}
2.2 重要注意事项:TextureView vs AspectRatioTextureView
在开发过程中,我们遇到了一个关键问题:使用普通的TextureView无法正常显示摄像头画面。通过深入研究源码和反复测试,我们发现AndroidUSBCamera库要求使用其自定义的[AspectRatioTextureView](file:///C:/Users/xxx/AndroidStudioProjects/UVC3Camera/app/libs/AndroidUSBCamera-3.3.3/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt#L43-L236)组件。
[AspectRatioTextureView](file:///C:/Users/xxx/AndroidStudioProjects/UVC3Camera/app/libs/AndroidUSBCamera-3.3.3/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt#L43-L236)相比普通TextureView的优势:
- 自动处理画面比例,确保预览画面不变形
- 与AndroidUSBCamera库深度集成,提供了必要的接口和回调
- 内部处理了OpenGL ES渲染相关逻辑
// 正确的使用方式
private lateinit var textureView1: AspectRatioTextureView
private lateinit var textureView2: AspectRatioTextureView
private lateinit var textureView3: AspectRatioTextureView
2.3 录制功能实现
项目实现了每个摄像头的独立录制功能,通过以下关键步骤:
- 开始录制:
private fun startRecording(cameraIndex: Int) {
// 生成文件名
val currentTime = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val cameraName = when (cameraIndex) {
1 -> "camera_1"
2 -> "camera_2"
3 -> "camera_3"
else -> "camera_$cameraIndex"
}
val fileName = "camera_${cameraIndex}_${currentTime}__$cameraName.mp4"
// 选择存储路径
val savePath = getOptimalStoragePath(fileName)
// 实际执行录制
startCameraRecording(cameraIndex, savePath)
}
fun startCameraRecording(cameraIndex: Int, filePath: String) {
val camera = when (cameraIndex) {
1 -> camera1
2 -> camera2
3 -> camera3
else -> null
}
camera?.let { cam ->
try {
cam.captureVideoStart(object : ICaptureCallBack {
override fun onBegin() {
activity?.runOnUiThread {
addLog("摄像头${cameraIndex}开始录制")
}
}
override fun onError(error: String?) {
// 处理错误
}
override fun onComplete(path: String?) {
// 处理录制完成
}
}, filePath, 0)
} catch (e: Exception) {
// 异常处理
}
}
}
3. 前台服务实现后台录制
为了确保应用在后台也能持续录制,我们实现了前台服务[CameraRecordService](file:///C:/Users/xxx/AndroidStudioProjects/UVC3Camera/app/src/main/java/com/cz/uvc3camera/CameraRecordService.kt#L15-L259):
class CameraRecordService : Service() {
companion object {
private const val TAG = "CameraRecordService"
const val CHANNEL_ID = "CameraRecordServiceChannel"
const val NOTIFICATION_ID = 2
// Intent actions
const val ACTION_START_RECORDING = "com.cz.uvc3camera.START_RECORDING"
const val ACTION_STOP_RECORDING = "com.cz.uvc3camera.STOP_RECORDING"
const val ACTION_STOP_ALL_RECORDING = "com.cz.uvc3camera.STOP_ALL_RECORDING"
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
ACTION_START_RECORDING -> {
val cameraIndex = intent.getIntExtra(EXTRA_CAMERA_INDEX, -1)
val filePath = intent.getStringExtra(EXTRA_FILE_PATH)
if (cameraIndex != -1 && filePath != null) {
startRecording(cameraIndex, filePath)
}
}
// ... 其他action处理
}
startForeground(NOTIFICATION_ID, createNotification())
return START_STICKY
}
private fun createNotification(): android.app.Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("摄像头录制服务")
.setContentText("正在后台录制摄像头视频")
.setSmallIcon(R.drawable.ic_recording)
.build()
}
}
前台服务的关键优势:
- 提高应用进程优先级,防止被系统杀死
- 显示持续通知,让用户知道应用正在后台运行
- 符合Android后台执行限制的规范
4. 状态管理与同步
项目中实现了复杂的状态管理机制,确保UI状态与服务状态保持同步:
// 使用原子操作确保录制状态的线程安全
private val isRecording1 = AtomicBoolean(false)
private val isRecording2 = AtomicBoolean(false)
private val isRecording3 = AtomicBoolean(false)
// 通过广播接收器同步状态
private val recordingBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
CameraRecordService.ACTION_RECORDING_STATUS_RESULT -> {
// 处理录制状态查询结果
val isRecording1 = intent.getBooleanExtra("is_recording_1", false)
val isRecording2 = intent.getBooleanExtra("is_recording_2", false)
val isRecording3 = intent.getBooleanExtra("is_recording_3", false)
// 更新本地状态
this@CameraFragment.isRecording1.set(isRecording1)
this@CameraFragment.isRecording2.set(isRecording2)
this@CameraFragment.isRecording3.set(isRecording3)
// 同步UI状态
syncRecordingButtonStates()
}
}
}
}
5. 总结
通过本项目实践,我们总结了以下关键要点:
- 依赖配置:在使用AndroidUSBCamera库时,可能需要使用Liferay仓库而非JitPack
- UI组件选择:必须使用库提供的[AspectRatioTextureView](file:///C:/Users/xxx/AndroidStudioProjects/UVC3Camera/app/libs/AndroidUSBCamera-3.3.3/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt#L43-L236)而非普通TextureView
- 状态管理:合理使用原子操作和广播机制确保状态同步
- 后台执行:通过前台服务确保录制任务在后台持续运行
- 异常处理:完善的异常处理机制确保应用稳定性
这个项目为多摄像头应用开发提供了完整的解决方案,可以作为类似需求的参考实现。通过AndroidUSBCamera库,我们可以快速构建功能完善的UVC摄像头应用,大大减少了开发工作量。

浙公网安备 33010602011771号