From bb6c627fad9683e67b266e741bebf99f60963bb4 Mon Sep 17 00:00:00 2001 From: linbo <1034003879@qq.com> Date: Wed, 12 Aug 2026 10:37:18 +0800 Subject: [PATCH] fix(camera): preserve UVC recording timing --- .../camera/uvc_camera/src/uvc_camera.cpp | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp b/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp index 362a6c94..4086c473 100644 --- a/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp +++ b/cmvr-es/devices/camera/uvc_camera/src/uvc_camera.cpp @@ -3,6 +3,8 @@ // Created by xtkuang on 2025/5/30. // +#include + #include "../include/uvc_camera.h" using namespace std; @@ -361,7 +363,9 @@ void UVCCamera::startRecording(const std::string &video_path) { codecpar->codec_id = codec_id; codecpar->width = width_; codecpar->height = height_; - stream->time_base = {1, fps_}; // 时间基:1/fps(每帧间隔1个时间单位) + // Use a fine-grained time base because the actual UVC frame rate can + // differ significantly from the configured frame rate. + stream->time_base = {1, 1'000'000}; // 4. 复制编码器的extradata(如H.264的SPS/PPS,确保播放器能解析) if (rgbEncoder_ && rgbEncoder_->codec_context && rgbEncoder_->codec_context->extradata) { @@ -665,6 +669,8 @@ void UVCCamera::recording_worker_() { is_recording_running = true; const int frame_interval = 1000 / fps_; bool is_first_key = false; + int64_t first_capture_monotonic_ns = 0; + int64_t last_packet_pts = AV_NOPTS_VALUE; try { //保证当前采集线程正常运行 while (state_.is_recording && is_streaming_running) { @@ -707,25 +713,33 @@ void UVCCamera::recording_worker_() { packet_->size = static_cast(frame_data.rgbFrame.size()); packet_->stream_index = stream_->index; // 指定流索引 - // 设置时间戳(递增) - packet_->pts = frame_count_++; - packet_->dts = packet_->pts; - packet_->duration = 1; // 每帧持续1个时间单位(根据time_base) + // Preserve real capture time so dropped/late frames do not speed up playback. + if (first_capture_monotonic_ns == 0) { + first_capture_monotonic_ns = frame_data.capture_monotonic_ns; + } + const int64_t elapsed_ns = std::max( + 0, frame_data.capture_monotonic_ns - first_capture_monotonic_ns); + int64_t packet_pts = av_rescale_q( + elapsed_ns, AVRational{1, 1'000'000'000}, stream_->time_base); + if (last_packet_pts != AV_NOPTS_VALUE && packet_pts <= last_packet_pts) { + packet_pts = last_packet_pts + 1; + } + packet_->pts = packet_pts; + packet_->dts = packet_pts; + packet_->duration = av_rescale_q( + 1, AVRational{1, fps_}, stream_->time_base); + last_packet_pts = packet_pts; + ++frame_count_; // 标记关键帧 if (frame_data.bKey) { packet_->flags |= AV_PKT_FLAG_KEY; } - // 转换时间基 - av_packet_rescale_ts(packet_, {1, fps_}, stream_->time_base); - // 写入帧 if (av_interleaved_write_frame(format_context_, packet_) < 0) { CMVR_LOG(ERROR) << "写入第" << frame_count_ << "帧失败"; } - - std::this_thread::sleep_for(std::chrono::milliseconds(33)); } // 7. 写入文件尾(完成封装)