如何使用Camera2获取基于System.currentTimeMillis()的纳秒/毫秒级图像捕获精准时间
用Camera2获取对齐System.currentTimeMillis()的精准捕获时间(纳秒/毫秒级)
嘿,这个需求我刚好在之前的项目里折腾过,Camera2本身提供的传感器时间是基于开机时长的,要转到System.currentTimeMillis()的UTC基准,核心就是搞定两个时间体系的偏移转换,下面给你说清楚具体怎么做:
核心原理先搞懂
首先得明确两个关键时间的区别:
System.currentTimeMillis():从1970年1月1日UTC到现在的毫秒数,会受系统时间调整(比如用户手动改时间、NTP同步)影响SystemClock.elapsedRealtimeNanos():从设备开机到现在的纳秒数,完全不受系统时间变化影响,Camera2的SENSOR_TIMESTAMP用的就是这个基准
我们要做的就是计算两个基准之间的偏移量,然后把传感器捕获时间转换成UTC时间。
具体实现步骤
1. 计算时间偏移量
在相机初始化完成、开始捕获图像之前,尽快计算一次偏移量——这个偏移量就是把开机时长基准转成UTC基准的关键值:
private long timeOffsetNs; // 相机准备就绪后调用这个方法 private void calculateTimeOffset() { // 当前UTC时间(毫秒)转成纳秒 long currentUtcNs = System.currentTimeMillis() * 1000000L; // 当前设备开机时长(纳秒) long elapsedNanos = SystemClock.elapsedRealtimeNanos(); // 偏移量 = UTC纳秒 - 开机时长纳秒 timeOffsetNs = currentUtcNs - elapsedNanos; }
2. 在捕获回调中获取并转换时间
Camera2的捕获结果回调里能拿到传感器实际捕获图像的时间,我们用这个时间加上偏移量,就能得到对齐System.currentTimeMillis()的精准时间:
// 自定义CaptureCallback,在捕获完成时处理时间 private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); // 获取传感器实际捕获的时间(纳秒级,开机时长基准) Long sensorCaptureNs = result.get(CaptureResult.SENSOR_TIMESTAMP); if (sensorCaptureNs != null) { // 转换为UTC基准的纳秒时间 long utcCaptureNs = sensorCaptureNs + timeOffsetNs; // 转成毫秒级(和System.currentTimeMillis()完全对齐) long utcCaptureMs = utcCaptureNs / 1000000L; // 这里就可以用你需要的精度时间了 Log.d("CameraCapture", "捕获时间(UTC毫秒): " + utcCaptureMs + " | 纳秒: " + utcCaptureNs); } } };
3. 处理系统时间变化的情况
如果你的应用需要长期运行,系统时间可能会被手动修改或者NTP同步调整,这时候之前的偏移量就不准了,需要监听系统时间变化并重新计算偏移量:
private BroadcastReceiver timeChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 系统时间或时区变化时,重新计算偏移量 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction()) || Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { calculateTimeOffset(); } } }; // 在Activity/Fragment的onCreate里注册广播 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ...其他初始化代码 IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); registerReceiver(timeChangeReceiver, filter); } // 在onDestroy里注销广播,避免内存泄漏 @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(timeChangeReceiver); }
关键注意事项
- 优先用SENSOR_TIMESTAMP:不要用
Image.getTimestamp(),这个是图像数据写入到ImageReader的时间,比实际捕获时间有延迟,精度和准确性都不如传感器直接上报的时间 - 精度保障:
SENSOR_TIMESTAMP本身是纳秒级的,转换后的utcCaptureNs也保持了纳秒精度,完全满足你的需求;毫秒级直接取整即可 - 硬件差异:部分低端设备的传感器时间精度可能达不到纳秒级,但至少能保证毫秒级,也符合要求
内容的提问来源于stack exchange,提问作者karatuno
相关产品推荐
相关产品推荐

