You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 07:52:34