perf(camera): avoid unnecessary UVC BGR conversion

This commit is contained in:
linbo 2026-08-12 17:40:43 +08:00
parent 69b96cb60b
commit 400327269a
4 changed files with 307 additions and 145 deletions

View File

@ -19,6 +19,7 @@ struct FfmpegEncoderInfo {
bool bRunning = false; bool bRunning = false;
AVCodecContext* codec_context = nullptr; AVCodecContext* codec_context = nullptr;
AVFrame* frame = nullptr; AVFrame* frame = nullptr;
AVFrame* transfer_frame = nullptr;
AVPacket* packet = nullptr; AVPacket* packet = nullptr;
SwsContext* sws_context = nullptr; SwsContext* sws_context = nullptr;
@ -42,6 +43,11 @@ public:
std::vector<uint8_t>& encoded_frame, std::vector<uint8_t>& encoded_frame,
bool& is_key, bool& is_key,
const CameraStreamEncodeOptions& options = {}); const CameraStreamEncodeOptions& options = {});
static bool encode(std::shared_ptr<FfmpegEncoderInfo>& encoder,
const AVFrame* frame,
std::vector<uint8_t>& encoded_frame,
bool& is_key);
}; };
} // namespace cmvr::device } // namespace cmvr::device

View File

@ -7,6 +7,7 @@
#include <libavutil/opt.h> #include <libavutil/opt.h>
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
#include <libavutil/hwcontext.h>
#include <opencv2/imgproc.hpp> #include <opencv2/imgproc.hpp>
#include "common/base/logging/logger.h" #include "common/base/logging/logger.h"
@ -81,6 +82,55 @@ AVPixelFormat sourcePixelFormat(const cv::Mat& frame)
return AV_PIX_FMT_NONE; return AV_PIX_FMT_NONE;
} }
bool encodePreparedFrame(FfmpegEncoderInfo& encoder,
std::vector<uint8_t>& encoded_frame,
bool& is_key)
{
encoder.frame->pts = encoder.frame_pts++;
int ret = avcodec_send_frame(encoder.codec_context, encoder.frame);
if (ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_strerror(ret, errbuf, sizeof(errbuf));
CMVR_LOG(ERROR) << "[CameraStreamEncoder] error sending frame to encoder: " << errbuf;
return false;
}
while (true) {
ret = avcodec_receive_packet(encoder.codec_context, encoder.packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
if (ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_strerror(ret, errbuf, sizeof(errbuf));
CMVR_LOG(ERROR) << "[CameraStreamEncoder] error receiving packet from encoder: " << errbuf;
return false;
}
if (encoder.packet->flags & AV_PKT_FLAG_KEY) {
is_key = true;
}
encoded_frame.reserve(encoded_frame.size() + encoder.packet->size);
encoded_frame.insert(encoded_frame.end(),
encoder.packet->data,
encoder.packet->data + encoder.packet->size);
av_packet_unref(encoder.packet);
}
if (encoded_frame.size() < 4) {
return false;
}
const bool has_start_code =
(encoded_frame[0] == 0 && encoded_frame[1] == 0 && encoded_frame[2] == 1) ||
(encoded_frame[0] == 0 && encoded_frame[1] == 0 && encoded_frame[2] == 0 && encoded_frame[3] == 1);
if (!has_start_code) {
CMVR_LOG(ERROR) << "[CameraStreamEncoder] invalid frame: no NALU start code";
return false;
}
return true;
}
} // namespace } // namespace
FfmpegEncoderInfo::~FfmpegEncoderInfo() FfmpegEncoderInfo::~FfmpegEncoderInfo()
@ -89,6 +139,10 @@ FfmpegEncoderInfo::~FfmpegEncoderInfo()
av_frame_free(&frame); av_frame_free(&frame);
frame = nullptr; frame = nullptr;
} }
if (transfer_frame) {
av_frame_free(&transfer_frame);
transfer_frame = nullptr;
}
if (packet) { if (packet) {
av_packet_free(&packet); av_packet_free(&packet);
packet = nullptr; packet = nullptr;
@ -277,49 +331,102 @@ bool CameraStreamEncoder::encode(std::shared_ptr<FfmpegEncoderInfo>& encoder,
return false; return false;
} }
encoder->frame->pts = encoder->frame_pts++; return encodePreparedFrame(*encoder, encoded_frame, is_key);
int ret = avcodec_send_frame(encoder->codec_context, encoder->frame); }
if (ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0}; bool CameraStreamEncoder::encode(std::shared_ptr<FfmpegEncoderInfo>& encoder,
av_strerror(ret, errbuf, sizeof(errbuf)); const AVFrame* frame,
CMVR_LOG(ERROR) << "[CameraStreamEncoder] error sending frame to encoder: " << errbuf; std::vector<uint8_t>& encoded_frame,
bool& is_key)
{
encoded_frame.clear();
is_key = false;
if (!encoder || !encoder->codec_context || !encoder->frame || !encoder->packet || !frame) {
return false; return false;
} }
while (true) { const AVFrame* source_frame = frame;
ret = avcodec_receive_packet(encoder->codec_context, encoder->packet); if (frame->hw_frames_ctx) {
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { if (!encoder->transfer_frame) {
break; encoder->transfer_frame = av_frame_alloc();
} }
if (ret < 0) { if (!encoder->transfer_frame) {
return false;
}
av_frame_unref(encoder->transfer_frame);
const int transfer_ret = av_hwframe_transfer_data(encoder->transfer_frame, frame, 0);
if (transfer_ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0}; char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_strerror(ret, errbuf, sizeof(errbuf)); av_strerror(transfer_ret, errbuf, sizeof(errbuf));
CMVR_LOG(ERROR) << "[CameraStreamEncoder] error receiving packet from encoder: " << errbuf; CMVR_LOG(ERROR) << "[CameraStreamEncoder] failed to download hardware frame: " << errbuf;
break; return false;
} }
const int props_ret = av_frame_copy_props(encoder->transfer_frame, frame);
if (encoder->packet->flags & AV_PKT_FLAG_KEY) { if (props_ret < 0) {
is_key = true; char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_strerror(props_ret, errbuf, sizeof(errbuf));
CMVR_LOG(ERROR) << "[CameraStreamEncoder] failed to copy hardware frame properties: " << errbuf;
return false;
} }
encoded_frame.reserve(encoded_frame.size() + encoder->packet->size); source_frame = encoder->transfer_frame;
encoded_frame.insert(encoded_frame.end(),
encoder->packet->data,
encoder->packet->data + encoder->packet->size);
av_packet_unref(encoder->packet);
} }
if (encoded_frame.size() < 4) { const auto source_format = static_cast<AVPixelFormat>(source_frame->format);
if (source_format == AV_PIX_FMT_NONE) {
CMVR_LOG(ERROR) << "[CameraStreamEncoder] source frame has no pixel format";
return false; return false;
} }
const bool has_start_code = const int writable_ret = av_frame_make_writable(encoder->frame);
(encoded_frame[0] == 0 && encoded_frame[1] == 0 && encoded_frame[2] == 1) || if (writable_ret < 0) {
(encoded_frame[0] == 0 && encoded_frame[1] == 0 && encoded_frame[2] == 0 && encoded_frame[3] == 1); char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
if (!has_start_code) { av_strerror(writable_ret, errbuf, sizeof(errbuf));
CMVR_LOG(ERROR) << "[CameraStreamEncoder] invalid frame: no NALU start code"; CMVR_LOG(ERROR) << "[CameraStreamEncoder] frame is not writable: " << errbuf;
return false; return false;
} }
return true;
int copy_ret = 0;
if (source_frame->width == encoder->codec_context->width
&& source_frame->height == encoder->codec_context->height
&& source_format == encoder->codec_context->pix_fmt) {
copy_ret = av_frame_copy(encoder->frame, source_frame);
} else {
encoder->sws_context = sws_getCachedContext(
encoder->sws_context,
source_frame->width,
source_frame->height,
source_format,
encoder->codec_context->width,
encoder->codec_context->height,
encoder->codec_context->pix_fmt,
SWS_BILINEAR,
nullptr,
nullptr,
nullptr);
if (!encoder->sws_context) {
CMVR_LOG(ERROR) << "[CameraStreamEncoder] failed to create native frame conversion context";
return false;
}
const int scaled_rows = sws_scale(
encoder->sws_context,
source_frame->data,
source_frame->linesize,
0,
source_frame->height,
encoder->frame->data,
encoder->frame->linesize);
copy_ret = scaled_rows == encoder->codec_context->height
? 0
: scaled_rows < 0 ? scaled_rows : AVERROR_INVALIDDATA;
}
if (copy_ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_strerror(copy_ret, errbuf, sizeof(errbuf));
CMVR_LOG(ERROR) << "[CameraStreamEncoder] failed to copy native frame: " << errbuf;
return false;
}
return encodePreparedFrame(*encoder, encoded_frame, is_key);
} }
} // namespace cmvr::device } // namespace cmvr::device

View File

@ -52,10 +52,11 @@ namespace cmvr::device {
bool open_capture_(); bool open_capture_();
void close_capture_(); void close_capture_();
void capture_worker_(); void capture_worker_();
bool wait_for_capture_frame_(cv::Mat& frame, bool wait_for_capture_frame_(AVFrame* frame,
uint64_t& last_sequence, uint64_t& last_sequence,
int64_t& capture_monotonic_ns, int64_t& capture_monotonic_ns,
int64_t& capture_utc_ns); int64_t& capture_utc_ns);
bool convert_capture_frame_to_bgr_(const AVFrame* frame, cv::Mat& bgr_frame);
static int interrupt_capture_(void* opaque); static int interrupt_capture_(void* opaque);
int fps_; int fps_;
@ -85,8 +86,9 @@ namespace cmvr::device {
int capture_video_stream_index_ = -1; int capture_video_stream_index_ = -1;
std::atomic<bool> capture_running_{false}; std::atomic<bool> capture_running_{false};
std::mutex capture_frame_mutex_; std::mutex capture_frame_mutex_;
std::mutex capture_conversion_mutex_;
std::condition_variable capture_frame_cv_; std::condition_variable capture_frame_cv_;
cv::Mat latest_capture_frame_; AVFrame* latest_capture_frame_ = nullptr;
uint64_t latest_capture_sequence_ = 0; uint64_t latest_capture_sequence_ = 0;
int64_t latest_capture_monotonic_ns_ = 0; int64_t latest_capture_monotonic_ns_ = 0;
int64_t latest_capture_utc_ns_ = 0; int64_t latest_capture_utc_ns_ = 0;

View File

@ -234,11 +234,11 @@ bool UVCCamera::start() {
return false; return false;
} }
cv::Mat first_frame; AVFrame* first_frame = av_frame_alloc();
uint64_t first_sequence = 0; uint64_t first_sequence = 0;
int64_t first_capture_monotonic_ns = 0; int64_t first_capture_monotonic_ns = 0;
int64_t first_capture_utc_ns = 0; int64_t first_capture_utc_ns = 0;
if (!wait_for_capture_frame_(first_frame, if (!first_frame || !wait_for_capture_frame_(first_frame,
first_sequence, first_sequence,
first_capture_monotonic_ns, first_capture_monotonic_ns,
first_capture_utc_ns)) { first_capture_utc_ns)) {
@ -253,8 +253,10 @@ bool UVCCamera::start() {
? "timed out waiting for the first camera frame" ? "timed out waiting for the first camera frame"
: capture_error; : capture_error;
CMVR_LOG(ERROR) << "[UVCCamera] (start): " << state_.error_message; CMVR_LOG(ERROR) << "[UVCCamera] (start): " << state_.error_message;
av_frame_free(&first_frame);
return false; return false;
} }
av_frame_free(&first_frame);
state_.is_opened = true; state_.is_opened = true;
return true; return true;
} }
@ -314,10 +316,16 @@ void UVCCamera::getRGBImage(cv::Mat& color, Rs2Intrinsics& intrinsics)
int64_t capture_monotonic_ns = 0; int64_t capture_monotonic_ns = 0;
int64_t capture_utc_ns = 0; int64_t capture_utc_ns = 0;
if (!wait_for_capture_frame_(color, AVFrame* capture_frame = av_frame_alloc();
requested_sequence, const bool received_frame = capture_frame
capture_monotonic_ns, && wait_for_capture_frame_(capture_frame,
capture_utc_ns) || color.empty()) { requested_sequence,
capture_monotonic_ns,
capture_utc_ns);
const bool converted_frame = received_frame
&& convert_capture_frame_to_bgr_(capture_frame, color);
av_frame_free(&capture_frame);
if (!converted_frame || color.empty()) {
std::string capture_error; std::string capture_error;
{ {
std::lock_guard capture_lock(capture_frame_mutex_); std::lock_guard capture_lock(capture_frame_mutex_);
@ -344,7 +352,7 @@ bool UVCCamera::open_capture_()
{ {
std::lock_guard frame_lock(capture_frame_mutex_); std::lock_guard frame_lock(capture_frame_mutex_);
latest_capture_frame_.release(); av_frame_free(&latest_capture_frame_);
latest_capture_sequence_ = 0; latest_capture_sequence_ = 0;
latest_capture_monotonic_ns_ = 0; latest_capture_monotonic_ns_ = 0;
latest_capture_utc_ns_ = 0; latest_capture_utc_ns_ = 0;
@ -511,6 +519,10 @@ void UVCCamera::close_capture_()
if (capture_format_context_) { if (capture_format_context_) {
avformat_close_input(&capture_format_context_); avformat_close_input(&capture_format_context_);
} }
{
std::lock_guard frame_lock(capture_frame_mutex_);
av_frame_free(&latest_capture_frame_);
}
capture_video_stream_index_ = -1; capture_video_stream_index_ = -1;
} }
@ -518,9 +530,8 @@ void UVCCamera::capture_worker_()
{ {
AVPacket* input_packet = av_packet_alloc(); AVPacket* input_packet = av_packet_alloc();
AVFrame* decoded_frame = av_frame_alloc(); AVFrame* decoded_frame = av_frame_alloc();
AVFrame* software_frame = av_frame_alloc();
uint64_t discarded_frame_count = 0; uint64_t discarded_frame_count = 0;
if (!input_packet || !decoded_frame || !software_frame) { if (!input_packet || !decoded_frame) {
{ {
std::lock_guard frame_lock(capture_frame_mutex_); std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to allocate FFmpeg capture frame resources"; capture_error_ = "failed to allocate FFmpeg capture frame resources";
@ -529,7 +540,6 @@ void UVCCamera::capture_worker_()
capture_frame_cv_.notify_all(); capture_frame_cv_.notify_all();
av_packet_free(&input_packet); av_packet_free(&input_packet);
av_frame_free(&decoded_frame); av_frame_free(&decoded_frame);
av_frame_free(&software_frame);
return; return;
} }
@ -605,88 +615,19 @@ void UVCCamera::capture_worker_()
break; break;
} }
AVFrame* source_frame = decoded_frame;
if (decoded_frame->hw_frames_ctx) {
av_frame_unref(software_frame);
const int transfer_result = av_hwframe_transfer_data(
software_frame, decoded_frame, 0);
if (transfer_result < 0) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to download QSV camera frame: "
+ ffmpeg_error_string(transfer_result);
capture_running_.store(false, std::memory_order_relaxed);
break;
}
av_frame_copy_props(software_frame, decoded_frame);
source_frame = software_frame;
}
const auto decoded_format = static_cast<AVPixelFormat>(source_frame->format);
const auto source_format = normalize_deprecated_yuv_format(decoded_format);
const bool source_full_range = source_frame->color_range == AVCOL_RANGE_JPEG
|| source_format != decoded_format;
capture_sws_context_ = sws_getCachedContext(
capture_sws_context_,
source_frame->width,
source_frame->height,
source_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;
}
const int* color_coefficients = sws_getCoefficients(
swscale_colorspace(source_frame->colorspace));
const int color_result = sws_setColorspaceDetails(
capture_sws_context_,
color_coefficients,
source_full_range ? 1 : 0,
color_coefficients,
1,
0,
1 << 16,
1 << 16);
if (color_result < 0) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to configure camera color conversion: "
+ ffmpeg_error_string(color_result);
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_,
source_frame->data,
source_frame->linesize,
0,
source_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_monotonic = std::chrono::steady_clock::now();
const auto capture_utc = std::chrono::system_clock::now(); const auto capture_utc = std::chrono::system_clock::now();
AVFrame* cached_frame = av_frame_clone(decoded_frame);
if (!cached_frame) {
std::lock_guard frame_lock(capture_frame_mutex_);
capture_error_ = "failed to retain QSV camera frame";
capture_running_.store(false, std::memory_order_relaxed);
break;
}
{ {
std::lock_guard frame_lock(capture_frame_mutex_); std::lock_guard frame_lock(capture_frame_mutex_);
latest_capture_frame_ = std::move(bgr_frame); av_frame_free(&latest_capture_frame_);
latest_capture_frame_ = cached_frame;
++latest_capture_sequence_; ++latest_capture_sequence_;
latest_capture_monotonic_ns_ = std::chrono::duration_cast<std::chrono::nanoseconds>( latest_capture_monotonic_ns_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
capture_monotonic.time_since_epoch()).count(); capture_monotonic.time_since_epoch()).count();
@ -703,14 +644,16 @@ void UVCCamera::capture_worker_()
capture_frame_cv_.notify_all(); capture_frame_cv_.notify_all();
av_packet_free(&input_packet); av_packet_free(&input_packet);
av_frame_free(&decoded_frame); av_frame_free(&decoded_frame);
av_frame_free(&software_frame);
} }
bool UVCCamera::wait_for_capture_frame_(cv::Mat& frame, bool UVCCamera::wait_for_capture_frame_(AVFrame* frame,
uint64_t& last_sequence, uint64_t& last_sequence,
int64_t& capture_monotonic_ns, int64_t& capture_monotonic_ns,
int64_t& capture_utc_ns) int64_t& capture_utc_ns)
{ {
if (!frame) {
return false;
}
std::unique_lock frame_lock(capture_frame_mutex_); std::unique_lock frame_lock(capture_frame_mutex_);
const auto timeout = std::chrono::milliseconds( const auto timeout = std::chrono::milliseconds(
std::max(500, fps_ > 0 ? 3000 / fps_ : 500)); std::max(500, fps_ > 0 ? 3000 / fps_ : 500));
@ -718,17 +661,112 @@ bool UVCCamera::wait_for_capture_frame_(cv::Mat& frame,
return latest_capture_sequence_ > last_sequence return latest_capture_sequence_ > last_sequence
|| !capture_running_.load(std::memory_order_relaxed); || !capture_running_.load(std::memory_order_relaxed);
}); });
if (!ready || latest_capture_sequence_ <= last_sequence || latest_capture_frame_.empty()) { if (!ready || latest_capture_sequence_ <= last_sequence || !latest_capture_frame_) {
return false; return false;
} }
frame = latest_capture_frame_; av_frame_unref(frame);
if (av_frame_ref(frame, latest_capture_frame_) < 0) {
return false;
}
last_sequence = latest_capture_sequence_; last_sequence = latest_capture_sequence_;
capture_monotonic_ns = latest_capture_monotonic_ns_; capture_monotonic_ns = latest_capture_monotonic_ns_;
capture_utc_ns = latest_capture_utc_ns_; capture_utc_ns = latest_capture_utc_ns_;
return true; return true;
} }
bool UVCCamera::convert_capture_frame_to_bgr_(const AVFrame* frame, cv::Mat& bgr_frame)
{
bgr_frame.release();
if (!frame) {
return false;
}
std::lock_guard conversion_lock(capture_conversion_mutex_);
AVFrame* software_frame = av_frame_alloc();
if (!software_frame) {
return false;
}
const AVFrame* source_frame = frame;
int result = 0;
if (frame->hw_frames_ctx) {
result = av_hwframe_transfer_data(software_frame, frame, 0);
if (result >= 0) {
result = av_frame_copy_props(software_frame, frame);
}
if (result < 0) {
CMVR_LOG(ERROR) << "[UVCCamera] failed to prepare BGR source frame: "
<< ffmpeg_error_string(result);
av_frame_free(&software_frame);
return false;
}
source_frame = software_frame;
}
const auto decoded_format = static_cast<AVPixelFormat>(source_frame->format);
const auto source_format = normalize_deprecated_yuv_format(decoded_format);
const bool source_full_range = source_frame->color_range == AVCOL_RANGE_JPEG
|| source_format != decoded_format;
capture_sws_context_ = sws_getCachedContext(
capture_sws_context_,
source_frame->width,
source_frame->height,
source_format,
width_,
height_,
AV_PIX_FMT_BGR24,
SWS_BILINEAR,
nullptr,
nullptr,
nullptr);
if (!capture_sws_context_) {
CMVR_LOG(ERROR) << "[UVCCamera] failed to create BGR conversion context";
av_frame_free(&software_frame);
return false;
}
const int* color_coefficients = sws_getCoefficients(
swscale_colorspace(source_frame->colorspace));
result = sws_setColorspaceDetails(
capture_sws_context_,
color_coefficients,
source_full_range ? 1 : 0,
color_coefficients,
1,
0,
1 << 16,
1 << 16);
if (result < 0) {
CMVR_LOG(ERROR) << "[UVCCamera] failed to configure BGR color conversion: "
<< ffmpeg_error_string(result);
av_frame_free(&software_frame);
return false;
}
bgr_frame.create(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_,
source_frame->data,
source_frame->linesize,
0,
source_frame->height,
destination_data,
destination_linesize);
av_frame_free(&software_frame);
if (converted_rows != height_) {
CMVR_LOG(ERROR) << "[UVCCamera] BGR conversion returned " << converted_rows
<< " rows, expected " << height_;
bgr_frame.release();
return false;
}
return true;
}
void UVCCamera::getDepthImage(cv::Mat& depth, Rs2Intrinsics& intrinsics) { void UVCCamera::getDepthImage(cv::Mat& depth, Rs2Intrinsics& intrinsics) {
state_.is_error = true; state_.is_error = true;
state_.error_message = "getDepthImage unsupported usage"; state_.error_message = "getDepthImage unsupported usage";
@ -960,14 +998,20 @@ void UVCCamera::resumeRecording() {
} }
void UVCCamera::streaming_worker_() { void UVCCamera::streaming_worker_() {
AVFrame* frame = av_frame_alloc();
if (!frame) {
is_streaming_running = false;
state_.is_error = true;
state_.error_message = "failed to allocate streaming frame";
CMVR_LOG(ERROR) << "[UVCCamera]streaming_worker_: " << state_.error_message;
return;
}
try { try {
// 计算理论上每帧之间的间隔时间(毫秒) // 计算理论上每帧之间的间隔时间(毫秒)
const int frame_interval = 1000 / fps_; const int frame_interval = 1000 / fps_;
bool success = false; bool success = false;
is_streaming_running = true; is_streaming_running = true;
cv::Mat frame;
uint64_t capture_sequence = 0; uint64_t capture_sequence = 0;
int64_t frame_capture_monotonic_ns = 0; int64_t frame_capture_monotonic_ns = 0;
int64_t frame_capture_utc_ns = 0; int64_t frame_capture_utc_ns = 0;
@ -993,7 +1037,7 @@ void UVCCamera::streaming_worker_() {
if (!wait_for_capture_frame_(frame, if (!wait_for_capture_frame_(frame,
capture_sequence, capture_sequence,
frame_capture_monotonic_ns, frame_capture_monotonic_ns,
frame_capture_utc_ns) || frame.empty()) { frame_capture_utc_ns)) {
std::string capture_error; std::string capture_error;
{ {
std::lock_guard capture_lock(capture_frame_mutex_); std::lock_guard capture_lock(capture_frame_mutex_);
@ -1011,31 +1055,33 @@ void UVCCamera::streaming_worker_() {
// 以下为编码部分,用于流模式 // 以下为编码部分,用于流模式
StreamFrameData frame_data; StreamFrameData frame_data;
// 保存图像数据 // 保存图像数据
{ cv::Mat bgr_to_encode;
frame_data.rgbImage = frame;
}
const auto copy_end_time = std::chrono::steady_clock::now(); const auto copy_end_time = std::chrono::steady_clock::now();
// rgb图像编码 // rgb图像编码
cv::Mat rgb_to_encode = frame_data.rgbImage; if (enable_stream_timestamp_) {
if (encode_width_ > 0 && encode_height_ > 0 && success = convert_capture_frame_to_bgr_(frame, bgr_to_encode);
(frame_data.rgbImage.cols != encode_width_ || frame_data.rgbImage.rows != encode_height_)) { if (success && encode_width_ > 0 && encode_height_ > 0 &&
cv::resize(frame_data.rgbImage, (bgr_to_encode.cols != encode_width_ || bgr_to_encode.rows != encode_height_)) {
rgb_to_encode, cv::resize(bgr_to_encode,
cv::Size(encode_width_, encode_height_), bgr_to_encode,
0.0, cv::Size(encode_width_, encode_height_),
0.0, 0.0,
cv::INTER_LINEAR); 0.0,
cv::INTER_LINEAR);
}
} }
const auto resize_end_time = std::chrono::steady_clock::now(); const auto resize_end_time = std::chrono::steady_clock::now();
CameraStreamEncodeOptions encode_options; if (enable_stream_timestamp_) {
encode_options.draw_timestamp = enable_stream_timestamp_; CameraStreamEncodeOptions encode_options;
success = CameraStreamEncoder::encode(rgbEncoder_, encode_options.draw_timestamp = true;
rgb_to_encode, success = success && CameraStreamEncoder::encode(
frame_data.rgbFrame, rgbEncoder_, bgr_to_encode, frame_data.rgbFrame, frame_data.bKey, encode_options);
frame_data.bKey, } else {
encode_options); success = CameraStreamEncoder::encode(
rgbEncoder_, frame, frame_data.rgbFrame, frame_data.bKey);
}
const auto encode_end_time = std::chrono::steady_clock::now(); const auto encode_end_time = std::chrono::steady_clock::now();
if (success) { if (success) {
@ -1123,6 +1169,7 @@ void UVCCamera::streaming_worker_() {
state_.error_message = e.what(); state_.error_message = e.what();
CMVR_LOG(ERROR) << "[UVCCamera]streaming_worker_ error:" << state_.error_message; CMVR_LOG(ERROR) << "[UVCCamera]streaming_worker_ error:" << state_.error_message;
} }
av_frame_free(&frame);
} }
void UVCCamera::recording_worker_() { void UVCCamera::recording_worker_() {