Android端如何利用CameraX的Image analysis保存带帧时间戳的视频?
用CameraX Image Analysis保存带时间戳帧的实现方法
完全可以通过Image Analysis组件实现带时间戳的帧保存,核心是利用CameraX提供的硬件级帧时间戳,配合后台线程完成文件存储,具体步骤和代码示例如下:
1. 获取帧的硬件时间戳
在ImageAnalysis.Analyzer的analyze方法中,通过ImageProxy.image?.timestamp可直接获取相机传感器提供的纳秒级时间戳,准确度极高。若image为空,可降级使用System.nanoTime()作为备选。
2. 帧数据转换与保存
CameraX默认输出YUV格式帧,需先转换为JPEG等可保存格式。注意不能在analyze方法内直接执行IO操作,否则会阻塞帧处理线程、降低录制帧率,建议用后台线程(如Coroutine IO调度器、单线程Executor)完成保存。
代码示例
自定义帧分析器
class TimestampedFrameAnalyzer : ImageAnalysis.Analyzer { private val yuvConverter = YuvToRgbConverter(ApplicationProvider.getApplicationContext()) private var outputBitmap: Bitmap? = null override fun analyze(imageProxy: ImageProxy) { val sourceImage = imageProxy.image ?: run { imageProxy.close() return } // 按需初始化Bitmap(仅尺寸变化时创建) if (outputBitmap == null || outputBitmap?.width != sourceImage.width || outputBitmap?.height != sourceImage.height) { outputBitmap = Bitmap.createBitmap(sourceImage.width, sourceImage.height, Bitmap.Config.ARGB_8888) } // YUV转Bitmap outputBitmap?.let { bitmap -> yuvConverter.yuvToRgb(sourceImage, bitmap) // 纳秒转毫秒,方便文件名使用 val timestampMs = sourceImage.timestamp / 1_000_000 // 后台线程保存帧 saveFrameToFile(timestampMs, bitmap) } // 必须关闭ImageProxy,否则CameraX会停止输出新帧 imageProxy.close() } private fun saveFrameToFile(timestampMs: Long, bitmap: Bitmap) { CoroutineScope(Dispatchers.IO).launch { val saveDir = ApplicationProvider.getApplicationContext<Context>().getExternalFilesDir(Environment.DIRECTORY_PICTURES) val frameFile = File(saveDir, "recording_frame_${timestampMs}.jpg") try { FileOutputStream(frameFile).use { outputStream -> bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream) } } catch (e: IOException) { e.printStackTrace() } } } }
配置并绑定ImageAnalysis用例
// 构建ImageAnalysis用例 val imageAnalysis = ImageAnalysis.Builder() // 选择背压策略:STRATEGY_KEEP_ONLY_LATEST适合实时录制(丢弃旧帧);STRATEGY_BLOCK_PRODUCER会阻塞等待处理 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() // 指定专用线程池运行分析器,避免阻塞主线程 imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), TimestampedFrameAnalyzer()) // 绑定到Camera生命周期 val cameraProvider = ProcessCameraProvider.getInstance(context).get() val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis)
注意事项
- 时间戳单位:
Image.timestamp返回纳秒,转换为毫秒(除以1000000)更适合作为文件名或时间记录。 - 性能优化:若无需Bitmap,直接将YUV数据编码为JPEG可减少内存开销、提升处理速度。
- 补充说明:其实Video Capture组件也能获取帧时间戳(通过MediaCodec或MediaRecorder回调),但Image Analysis在逐帧自定义处理上更灵活,适合需要精细化操作的场景。
内容的提问来源于stack exchange,提问作者normidar
相关产品推荐
相关产品推荐

