refactor(camera): capture UVC frames with FFmpeg

This commit is contained in:
linbo 2026-08-12 15:08:04 +08:00
parent bb6c627fad
commit 8850df3983
3 changed files with 408 additions and 126 deletions

View File

@ -2,7 +2,18 @@ add_library(uvc_camera SHARED src/uvc_camera.cpp)
target_include_directories(uvc_camera PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(uvc_camera PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(uvc_camera PUBLIC glog opencv_core opencv_imgproc cmvr_es::proto cmvr_es::device::camera_stream_encoder) target_link_libraries(uvc_camera PUBLIC
glog
opencv_core
opencv_imgproc
avcodec
avdevice
avformat
avutil
swscale
cmvr_es::proto
cmvr_es::device::camera_stream_encoder
)
add_library(cmvr_es::device::uvc_camera ALIAS uvc_camera) add_library(cmvr_es::device::uvc_camera ALIAS uvc_camera)
install(TARGETS uvc_camera LIBRARY DESTINATION lib) install(TARGETS uvc_camera LIBRARY DESTINATION lib)

View File

@ -5,6 +5,9 @@
#ifndef CMVR_ES_UVC_CAMERA_H #ifndef CMVR_ES_UVC_CAMERA_H
#define CMVR_ES_UVC_CAMERA_H #define CMVR_ES_UVC_CAMERA_H
#include <atomic>
#include <condition_variable>
#include "common/base/ring_buffer.h" #include "common/base/ring_buffer.h"
#include "camera/abstract_camera.h" #include "camera/abstract_camera.h"
#include "devices/camera/common/include/camera_stream_encoder.h" #include "devices/camera/common/include/camera_stream_encoder.h"
@ -46,6 +49,14 @@ namespace cmvr::device {
void streaming_worker_(); void streaming_worker_();
void recording_worker_(); void recording_worker_();
void cleanup_recording_resources_(); void cleanup_recording_resources_();
bool open_capture_();
void close_capture_();
void capture_worker_();
bool wait_for_capture_frame_(cv::Mat& frame,
uint64_t& last_sequence,
int64_t& capture_monotonic_ns,
int64_t& capture_utc_ns);
static int interrupt_capture_(void* opaque);
int fps_; int fps_;
int width_; int width_;
@ -53,7 +64,6 @@ namespace cmvr::device {
int encode_width_; int encode_width_;
int encode_height_; int encode_height_;
std::string serial_; std::string serial_;
cv::VideoCapture cap_;
size_t buffer_size_; size_t buffer_size_;
std::string codec_; std::string codec_;
bool enable_stream_timestamp_{false}; bool enable_stream_timestamp_{false};
@ -64,12 +74,22 @@ namespace cmvr::device {
std::string current_video_path_; std::string current_video_path_;
std::mutex ctrl_mtx_{}; std::mutex ctrl_mtx_{};
std::unique_ptr<cv::VideoWriter> video_writer_;
std::shared_ptr<std::thread> stream_thread_; std::shared_ptr<std::thread> stream_thread_;
std::shared_ptr<std::thread> recording_thread_; std::shared_ptr<std::thread> recording_thread_;
std::shared_ptr<std::thread> capture_thread_;
AVFormatContext* capture_format_context_ = nullptr;
std::mutex capture_mutex_; AVCodecContext* capture_decoder_context_ = nullptr;
SwsContext* capture_sws_context_ = nullptr;
int capture_video_stream_index_ = -1;
std::atomic<bool> capture_running_{false};
std::mutex capture_frame_mutex_;
std::condition_variable capture_frame_cv_;
cv::Mat latest_capture_frame_;
uint64_t latest_capture_sequence_ = 0;
int64_t latest_capture_monotonic_ns_ = 0;
int64_t latest_capture_utc_ns_ = 0;
std::string capture_error_;
std::string output_path_; std::string output_path_;
bool is_recording_ = false; bool is_recording_ = false;

View File

@ -4,14 +4,31 @@
// //
#include <algorithm> #include <algorithm>
#include <cstring>
#include "../include/uvc_camera.h" #include "../include/uvc_camera.h"
extern "C" {
#include <libavdevice/avdevice.h>
#include <libavutil/error.h>
}
using namespace std; using namespace std;
using namespace cmvr::device; using namespace cmvr::device;
#define USE_LIST_IMAGE 1 #define USE_LIST_IMAGE 1
namespace {
std::string ffmpeg_error_string(const int error_code)
{
char error_buffer[AV_ERROR_MAX_STRING_SIZE] = {};
av_strerror(error_code, error_buffer, sizeof(error_buffer));
return error_buffer;
}
} // namespace
UVCCamera::UVCCamera(const config::UVCCameraConfig& camera):camera_(camera) UVCCamera::UVCCamera(const config::UVCCameraConfig& camera):camera_(camera)
{ {
id_ = camera_.id(); id_ = camera_.id();
@ -53,6 +70,7 @@ UVCCamera::UVCCamera(const config::UVCCameraConfig& camera):camera_(camera)
} }
UVCCamera::~UVCCamera() { UVCCamera::~UVCCamera() {
stop(); stop();
close_capture_();
if (stream_thread_ && stream_thread_->joinable()) if (stream_thread_ && stream_thread_->joinable())
stream_thread_->join(); stream_thread_->join();
if (recording_thread_ && recording_thread_->joinable()) if (recording_thread_ && recording_thread_->joinable())
@ -72,58 +90,29 @@ bool UVCCamera::init() {
stream_frame_buffer_ = std::make_shared<SPMCRingBuffer<StreamFrameData>>(buffer_size_); stream_frame_buffer_ = std::make_shared<SPMCRingBuffer<StreamFrameData>>(buffer_size_);
stream_frame_buffer_->clear(); stream_frame_buffer_->clear();
if (cap_.isOpened()) {
cap_.release();
}
cap_.open(serial_, cv::CAP_V4L2); // Validate the requested V4L2 mode during initialization. start() reopens
this_thread::sleep_for(chrono::milliseconds(100)); // the device and owns it for the lifetime of the camera session.
if (!open_capture_()) {
if (!cap_.isOpened()) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "Failed to open USB camera at index " + serial_; state_.error_message = capture_error_;
CMVR_LOG(ERROR) << "[UVCCamera] (init)" << state_.error_message; CMVR_LOG(ERROR) << "[UVCCamera] (init): " << state_.error_message;
return false; return false;
} }
close_capture_();
// 设置格式为MJPG
cap_.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
if (!cap_.set(cv::CAP_PROP_FRAME_WIDTH, width_)) {
state_.is_error = true;
state_.error_message = "set width failed";
CMVR_LOG(ERROR) << "[UVCCamera] (init): set width failed";
return false;
}
state_.width = width_;
if (!cap_.set(cv::CAP_PROP_FRAME_HEIGHT, height_)) {
state_.is_error = true;
state_.error_message = "set height failed";
CMVR_LOG(ERROR) << "[UVCCamera] (init): set height failed";
return false;
}
state_.height = height_;
if (!cap_.set(cv::CAP_PROP_FPS, fps_)) {
state_.is_error = true;
state_.error_message = "set fps failed";
CMVR_LOG(ERROR) << "[UVCCamera] (init): set fps failed";
return false;
}
if (!cap_.set(cv::CAP_PROP_BUFFERSIZE, 1)) {
CMVR_LOG(WARNING) << "[UVCCamera] (init): failed to limit capture buffer size";
}
//初始化编码器
// 初始化RGB编码器(示例参数:640x480,30fps,H.264)
if (!CameraStreamEncoder::init(rgbEncoder_, codec_, encode_width_, encode_height_, fps_)) { if (!CameraStreamEncoder::init(rgbEncoder_, codec_, encode_width_, encode_height_, fps_)) {
CMVR_LOG(ERROR) << "[UVCCamera] (start): Failed to init RGB encoder!"; CMVR_LOG(ERROR) << "[UVCCamera] (init): Failed to init RGB encoder!";
state_.is_error = true; state_.is_error = true;
state_.error_message = "Failed to init RGB encoder!"; state_.error_message = "Failed to init RGB encoder!";
return false; return false;
} }
state_.fps = fps_; state_.fps = fps_;
state_.width = width_;
state_.height = height_;
state_.is_initialized = true; state_.is_initialized = true;
state_.is_error = false; state_.is_error = false;
CMVR_LOG(INFO) << "[UVCCamera] (init): UVC camera initialized at index " << serial_; // 记录初始化成功日志 CMVR_LOG(INFO) << "[UVCCamera] (init): UVC camera initialized at " << serial_;
return true; return true;
} }
@ -141,42 +130,45 @@ bool UVCCamera::start() {
return true; return true;
} }
cap_.open(serial_, cv::CAP_V4L2); if (!open_capture_()) {
this_thread::sleep_for(chrono::milliseconds(100));
if (!cap_.isOpened()) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "Failed to open USB camera at index " + serial_; state_.error_message = capture_error_;
CMVR_LOG(ERROR) << "[UVCCamera] (init)" << state_.error_message; CMVR_LOG(ERROR) << "[UVCCamera] (start): " << state_.error_message;
return false; return false;
} }
// 设置格式为MJPG try {
cap_.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G')); capture_thread_ = std::make_shared<std::thread>(&UVCCamera::capture_worker_, this);
} catch (const std::exception& e) {
capture_error_ = std::string("failed to start capture thread: ") + e.what();
close_capture_();
state_.is_error = true;
state_.error_message = capture_error_;
CMVR_LOG(ERROR) << "[UVCCamera] (start): " << state_.error_message;
return false;
}
if (!cap_.set(cv::CAP_PROP_FRAME_WIDTH, width_)) { cv::Mat first_frame;
state_.is_error = true; uint64_t first_sequence = 0;
state_.error_message = "set width failed"; int64_t first_capture_monotonic_ns = 0;
CMVR_LOG(ERROR) << "[UVCCamera] (init): set width failed"; int64_t first_capture_utc_ns = 0;
return false; if (!wait_for_capture_frame_(first_frame,
first_sequence,
first_capture_monotonic_ns,
first_capture_utc_ns)) {
std::string capture_error;
{
std::lock_guard capture_lock(capture_frame_mutex_);
capture_error = capture_error_;
} }
state_.width = width_; close_capture_();
if (!cap_.set(cv::CAP_PROP_FRAME_HEIGHT, height_)) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "set height failed"; state_.error_message = capture_error.empty()
CMVR_LOG(ERROR) << "[UVCCamera] (init): set height failed"; ? "timed out waiting for the first camera frame"
: capture_error;
CMVR_LOG(ERROR) << "[UVCCamera] (start): " << state_.error_message;
return false; return false;
} }
state_.height = height_;
if (!cap_.set(cv::CAP_PROP_FPS, fps_)) {
state_.is_error = true;
state_.error_message = "set fps failed";
CMVR_LOG(ERROR) << "[UVCCamera] (init): set fps failed";
return false;
}
if (!cap_.set(cv::CAP_PROP_BUFFERSIZE, 1)) {
CMVR_LOG(WARNING) << "[UVCCamera] (start): failed to limit capture buffer size";
}
state_.is_opened = true; state_.is_opened = true;
return true; return true;
} }
@ -204,9 +196,7 @@ bool UVCCamera::stop() {
is_streaming_running = false; is_streaming_running = false;
} }
if (cap_.isOpened()) { close_capture_();
cap_.release();
}
state_.is_opened = false; state_.is_opened = false;
return true; return true;
} }
@ -230,39 +220,304 @@ void UVCCamera::getRGBImage(cv::Mat& color, Rs2Intrinsics& intrinsics)
return; return;
} }
uint64_t requested_sequence = 0;
{ {
std::lock_guard capture_lock(capture_mutex_); std::lock_guard capture_lock(capture_frame_mutex_);
if (!state_.is_streaming && !state_.is_recording) { requested_sequence = latest_capture_sequence_;
cap_.release();
if (!cap_.open(serial_, cv::CAP_V4L2)) {
state_.is_error = true;
state_.error_message = "failed to reopen camera for snapshot";
CMVR_LOG(ERROR) << "[UVCCamera] (getRGBImage): " << state_.error_message;
state_.is_opened = false;
return;
} }
cap_.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G')); int64_t capture_monotonic_ns = 0;
cap_.set(cv::CAP_PROP_FRAME_WIDTH, width_); int64_t capture_utc_ns = 0;
cap_.set(cv::CAP_PROP_FRAME_HEIGHT, height_); if (!wait_for_capture_frame_(color,
cap_.set(cv::CAP_PROP_FPS, fps_); requested_sequence,
cap_.set(cv::CAP_PROP_BUFFERSIZE, 1); capture_monotonic_ns,
capture_utc_ns) || color.empty()) {
if (!cap_.grab()) { std::string capture_error;
state_.is_error = true; {
state_.error_message = "failed to discard camera startup frame"; std::lock_guard capture_lock(capture_frame_mutex_);
CMVR_LOG(ERROR) << "[UVCCamera] (getRGBImage): " << state_.error_message; capture_error = capture_error_;
return;
} }
}
if (!cap_.read(color) || color.empty()) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "read color image failed"; state_.error_message = capture_error.empty()
? "timed out waiting for a fresh camera frame"
: capture_error;
CMVR_LOG(ERROR) << "[UVCCamera] (getRGBImage): " << state_.error_message; CMVR_LOG(ERROR) << "[UVCCamera] (getRGBImage): " << state_.error_message;
color.release(); color.release();
}
}
int UVCCamera::interrupt_capture_(void* opaque)
{
const auto* camera = static_cast<const UVCCamera*>(opaque);
return camera && !camera->capture_running_.load(std::memory_order_relaxed);
}
bool UVCCamera::open_capture_()
{
close_capture_();
{
std::lock_guard frame_lock(capture_frame_mutex_);
latest_capture_frame_.release();
latest_capture_sequence_ = 0;
latest_capture_monotonic_ns_ = 0;
latest_capture_utc_ns_ = 0;
capture_error_.clear();
}
auto* input_format = av_find_input_format("v4l2");
if (!input_format) {
capture_error_ = "FFmpeg v4l2 input format is unavailable";
return false;
}
capture_format_context_ = avformat_alloc_context();
if (!capture_format_context_) {
capture_error_ = "failed to allocate FFmpeg capture context";
return false;
}
capture_running_.store(true, std::memory_order_relaxed);
capture_format_context_->interrupt_callback.callback = &UVCCamera::interrupt_capture_;
capture_format_context_->interrupt_callback.opaque = this;
capture_format_context_->flags |= AVFMT_FLAG_NOBUFFER;
AVDictionary* options = nullptr;
const std::string video_size = std::to_string(width_) + "x" + std::to_string(height_);
av_dict_set(&options, "input_format", "mjpeg", 0);
av_dict_set(&options, "video_size", video_size.c_str(), 0);
av_dict_set(&options, "framerate", std::to_string(fps_).c_str(), 0);
int result = avformat_open_input(
&capture_format_context_, serial_.c_str(), input_format, &options);
av_dict_free(&options);
if (result < 0) {
const std::string error =
"failed to open " + serial_ + ": " + ffmpeg_error_string(result);
close_capture_();
capture_error_ = error;
return false;
}
result = avformat_find_stream_info(capture_format_context_, nullptr);
if (result < 0) {
const std::string error =
"failed to read camera stream info: " + ffmpeg_error_string(result);
close_capture_();
capture_error_ = error;
return false;
}
capture_video_stream_index_ = av_find_best_stream(
capture_format_context_, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (capture_video_stream_index_ < 0) {
const std::string error = "camera has no video stream: "
+ ffmpeg_error_string(capture_video_stream_index_);
close_capture_();
capture_error_ = error;
return false;
}
AVStream* video_stream = capture_format_context_->streams[capture_video_stream_index_];
const AVCodec* decoder = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (!decoder) {
const std::string error = "FFmpeg decoder is unavailable for camera input";
close_capture_();
capture_error_ = error;
return false;
}
capture_decoder_context_ = avcodec_alloc_context3(decoder);
if (!capture_decoder_context_) {
const std::string error = "failed to allocate camera decoder context";
close_capture_();
capture_error_ = error;
return false;
}
result = avcodec_parameters_to_context(capture_decoder_context_, video_stream->codecpar);
if (result >= 0) {
result = avcodec_open2(capture_decoder_context_, decoder, nullptr);
}
if (result < 0) {
const std::string error =
"failed to open camera decoder: " + ffmpeg_error_string(result);
close_capture_();
capture_error_ = error;
return false;
}
CMVR_LOG(INFO) << "[UVCCamera] FFmpeg capture opened"
<< ", device=" << serial_
<< ", input_codec=" << avcodec_get_name(video_stream->codecpar->codec_id)
<< ", width=" << capture_decoder_context_->width
<< ", height=" << capture_decoder_context_->height
<< ", requested_fps=" << fps_;
return true;
}
void UVCCamera::close_capture_()
{
capture_running_.store(false, std::memory_order_relaxed);
capture_frame_cv_.notify_all();
if (capture_thread_) {
if (capture_thread_->joinable()) {
capture_thread_->join();
}
capture_thread_.reset();
}
if (capture_sws_context_) {
sws_freeContext(capture_sws_context_);
capture_sws_context_ = nullptr;
}
if (capture_decoder_context_) {
avcodec_free_context(&capture_decoder_context_);
}
if (capture_format_context_) {
avformat_close_input(&capture_format_context_);
}
capture_video_stream_index_ = -1;
}
void UVCCamera::capture_worker_()
{
AVPacket* input_packet = av_packet_alloc();
AVFrame* decoded_frame = av_frame_alloc();
if (!input_packet || !decoded_frame) {
{
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to allocate FFmpeg capture frame resources";
}
capture_running_.store(false, std::memory_order_relaxed);
capture_frame_cv_.notify_all();
av_packet_free(&input_packet);
av_frame_free(&decoded_frame);
return; return;
} }
while (capture_running_.load(std::memory_order_relaxed)) {
int result = av_read_frame(capture_format_context_, input_packet);
if (result == AVERROR(EAGAIN)) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
} }
if (result < 0) {
if (capture_running_.load(std::memory_order_relaxed)) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to read camera packet: " + ffmpeg_error_string(result);
}
break;
}
if (input_packet->stream_index != capture_video_stream_index_) {
av_packet_unref(input_packet);
continue;
}
result = avcodec_send_packet(capture_decoder_context_, input_packet);
av_packet_unref(input_packet);
if (result < 0 && result != AVERROR(EAGAIN)) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to submit camera packet: " + ffmpeg_error_string(result);
break;
}
while (capture_running_.load(std::memory_order_relaxed)) {
result = avcodec_receive_frame(capture_decoder_context_, decoded_frame);
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
break;
}
if (result < 0) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to decode camera frame: " + ffmpeg_error_string(result);
capture_running_.store(false, std::memory_order_relaxed);
break;
}
capture_sws_context_ = sws_getCachedContext(
capture_sws_context_,
decoded_frame->width,
decoded_frame->height,
static_cast<AVPixelFormat>(decoded_frame->format),
width_,
height_,
AV_PIX_FMT_BGR24,
SWS_BILINEAR,
nullptr,
nullptr,
nullptr);
if (!capture_sws_context_) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to create camera pixel conversion context";
capture_running_.store(false, std::memory_order_relaxed);
break;
}
cv::Mat bgr_frame(height_, width_, CV_8UC3);
uint8_t* destination_data[] = {bgr_frame.data, nullptr, nullptr, nullptr};
int destination_linesize[] = {
static_cast<int>(bgr_frame.step[0]), 0, 0, 0
};
const int converted_rows = sws_scale(
capture_sws_context_,
decoded_frame->data,
decoded_frame->linesize,
0,
decoded_frame->height,
destination_data,
destination_linesize);
if (converted_rows != height_) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "camera pixel conversion returned an incomplete frame";
continue;
}
const auto capture_monotonic = std::chrono::steady_clock::now();
const auto capture_utc = std::chrono::system_clock::now();
{
std::lock_guard frame_lock(capture_frame_mutex_);
latest_capture_frame_ = std::move(bgr_frame);
++latest_capture_sequence_;
latest_capture_monotonic_ns_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
capture_monotonic.time_since_epoch()).count();
latest_capture_utc_ns_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
capture_utc.time_since_epoch()).count();
capture_error_.clear();
}
capture_frame_cv_.notify_all();
av_frame_unref(decoded_frame);
}
}
capture_running_.store(false, std::memory_order_relaxed);
capture_frame_cv_.notify_all();
av_packet_free(&input_packet);
av_frame_free(&decoded_frame);
}
bool UVCCamera::wait_for_capture_frame_(cv::Mat& frame,
uint64_t& last_sequence,
int64_t& capture_monotonic_ns,
int64_t& capture_utc_ns)
{
std::unique_lock frame_lock(capture_frame_mutex_);
const auto timeout = std::chrono::milliseconds(
std::max(500, fps_ > 0 ? 3000 / fps_ : 500));
const bool ready = capture_frame_cv_.wait_for(frame_lock, timeout, [&] {
return latest_capture_sequence_ > last_sequence
|| !capture_running_.load(std::memory_order_relaxed);
});
if (!ready || latest_capture_sequence_ <= last_sequence || latest_capture_frame_.empty()) {
return false;
}
frame = latest_capture_frame_;
last_sequence = latest_capture_sequence_;
capture_monotonic_ns = latest_capture_monotonic_ns_;
capture_utc_ns = latest_capture_utc_ns_;
return true;
} }
void UVCCamera::getDepthImage(cv::Mat& depth, Rs2Intrinsics& intrinsics) { void UVCCamera::getDepthImage(cv::Mat& depth, Rs2Intrinsics& intrinsics) {
@ -504,6 +759,9 @@ void UVCCamera::streaming_worker_() {
bool success = false; bool success = false;
is_streaming_running = true; is_streaming_running = true;
cv::Mat frame; cv::Mat frame;
uint64_t capture_sequence = 0;
int64_t frame_capture_monotonic_ns = 0;
int64_t frame_capture_utc_ns = 0;
uint64_t frame_sequence = 0; uint64_t frame_sequence = 0;
const uint64_t stream_epoch = static_cast<uint64_t>( const uint64_t stream_epoch = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::duration_cast<std::chrono::nanoseconds>(
@ -523,14 +781,19 @@ void UVCCamera::streaming_worker_() {
while (state_.is_streaming || state_.is_recording) { while (state_.is_streaming || state_.is_recording) {
// 记录当前帧处理开始时间 // 记录当前帧处理开始时间
const auto frame_start_time = std::chrono::steady_clock::now(); const auto frame_start_time = std::chrono::steady_clock::now();
bool frame_read = false; if (!wait_for_capture_frame_(frame,
capture_sequence,
frame_capture_monotonic_ns,
frame_capture_utc_ns) || frame.empty()) {
std::string capture_error;
{ {
std::lock_guard capture_lock(capture_mutex_); std::lock_guard capture_lock(capture_frame_mutex_);
frame_read = cap_.read(frame); capture_error = capture_error_;
} }
if (!frame_read || frame.empty()) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "failed to read frame"; state_.error_message = capture_error.empty()
? "timed out waiting for camera frame"
: capture_error;
CMVR_LOG(ERROR) << "[UVCCamera]streaming_worker_: " << state_.error_message; CMVR_LOG(ERROR) << "[UVCCamera]streaming_worker_: " << state_.error_message;
break; break;
} }
@ -540,7 +803,7 @@ void UVCCamera::streaming_worker_() {
StreamFrameData frame_data; StreamFrameData frame_data;
// 保存图像数据 // 保存图像数据
{ {
frame.copyTo(frame_data.rgbImage); // 执行深拷贝 frame_data.rgbImage = frame;
} }
const auto copy_end_time = std::chrono::steady_clock::now(); const auto copy_end_time = std::chrono::steady_clock::now();
@ -570,10 +833,9 @@ void UVCCamera::streaming_worker_() {
const uint64_t sequence = frame_sequence++; const uint64_t sequence = frame_sequence++;
frame_data.stream_epoch = stream_epoch; frame_data.stream_epoch = stream_epoch;
frame_data.sequence = sequence; frame_data.sequence = sequence;
frame_data.capture_monotonic_ns = std::chrono::duration_cast<std::chrono::nanoseconds>( frame_data.source_frame_number = capture_sequence;
frame_start_time.time_since_epoch()).count(); frame_data.capture_monotonic_ns = frame_capture_monotonic_ns;
frame_data.capture_utc_ns = std::chrono::duration_cast<std::chrono::nanoseconds>( frame_data.capture_utc_ns = frame_capture_utc_ns;
std::chrono::system_clock::now().time_since_epoch()).count();
frame_data.pts = static_cast<int64_t>(sequence); frame_data.pts = static_cast<int64_t>(sequence);
frame_data.dts = frame_data.pts; frame_data.dts = frame_data.pts;
frame_data.time_base_num = 1; frame_data.time_base_num = 1;
@ -634,17 +896,6 @@ void UVCCamera::streaming_worker_() {
continue; continue;
} }
// 计算从帧开始到现在的总耗时
auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - frame_start_time
).count();
// 计算需要休眠的时间(确保总耗时达到frame_interval)
int sleep_time = frame_interval - total_duration;
// 只有当需要休眠的时间为正数时才休眠
if (sleep_time > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
}
} }
is_streaming_running = false; is_streaming_running = false;