perf(camera): avoid unnecessary UVC BGR conversion
This commit is contained in:
parent
69b96cb60b
commit
400327269a
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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_() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user