fix(camera): preserve UVC recording timing
This commit is contained in:
parent
5f4e3897ac
commit
bb6c627fad
@ -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. 写入文件尾(完成封装)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user