fix(camera): preserve UVC recording timing

This commit is contained in:
linbo 2026-08-12 10:37:18 +08:00
parent 5f4e3897ac
commit bb6c627fad

View File

@ -3,6 +3,8 @@
// Created by xtkuang on 2025/5/30. // Created by xtkuang on 2025/5/30.
// //
#include <algorithm>
#include "../include/uvc_camera.h" #include "../include/uvc_camera.h"
using namespace std; using namespace std;
@ -361,7 +363,9 @@ void UVCCamera::startRecording(const std::string &video_path) {
codecpar->codec_id = codec_id; codecpar->codec_id = codec_id;
codecpar->width = width_; codecpar->width = width_;
codecpar->height = height_; 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,确保播放器能解析) // 4. 复制编码器的extradata(如H.264的SPS/PPS,确保播放器能解析)
if (rgbEncoder_ && rgbEncoder_->codec_context && rgbEncoder_->codec_context->extradata) { if (rgbEncoder_ && rgbEncoder_->codec_context && rgbEncoder_->codec_context->extradata) {
@ -665,6 +669,8 @@ void UVCCamera::recording_worker_() {
is_recording_running = true; is_recording_running = true;
const int frame_interval = 1000 / fps_; const int frame_interval = 1000 / fps_;
bool is_first_key = false; bool is_first_key = false;
int64_t first_capture_monotonic_ns = 0;
int64_t last_packet_pts = AV_NOPTS_VALUE;
try { try {
//保证当前采集线程正常运行 //保证当前采集线程正常运行
while (state_.is_recording && is_streaming_running) { while (state_.is_recording && is_streaming_running) {
@ -707,25 +713,33 @@ void UVCCamera::recording_worker_() {
packet_->size = static_cast<int>(frame_data.rgbFrame.size()); packet_->size = static_cast<int>(frame_data.rgbFrame.size());
packet_->stream_index = stream_->index; // 指定流索引 packet_->stream_index = stream_->index; // 指定流索引
// 设置时间戳(递增) // Preserve real capture time so dropped/late frames do not speed up playback.
packet_->pts = frame_count_++; if (first_capture_monotonic_ns == 0) {
packet_->dts = packet_->pts; first_capture_monotonic_ns = frame_data.capture_monotonic_ns;
packet_->duration = 1; // 每帧持续1个时间单位(根据time_base) }
const int64_t elapsed_ns = std::max<int64_t>(
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) { if (frame_data.bKey) {
packet_->flags |= AV_PKT_FLAG_KEY; 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) { if (av_interleaved_write_frame(format_context_, packet_) < 0) {
CMVR_LOG(ERROR) << "写入第" << frame_count_ << "帧失败"; CMVR_LOG(ERROR) << "写入第" << frame_count_ << "帧失败";
} }
std::this_thread::sleep_for(std::chrono::milliseconds(33));
} }
// 7. 写入文件尾(完成封装) // 7. 写入文件尾(完成封装)