VNC-151 Add libyuv support

This commit is contained in:
El 2026-01-21 22:59:15 +00:00
parent 97f1876e04
commit ec0bb87d8f
No known key found for this signature in database
GPG key ID: EB3F4C9EA29CDE59
8 changed files with 72 additions and 43 deletions

View file

@ -218,6 +218,7 @@ endif()
set(HAVE_PAM ${ENABLE_PAM})
option(DEBUG_FFMPEG "Debug ffmpeg" OFF)
option(ENABLE_LIBYUV "Enable libyuv conversion support" ON)
# Check for SSE2
check_cxx_compiler_flag(-msse2 COMPILER_SUPPORTS_SSE2)

View file

@ -152,6 +152,14 @@ else ()
set(RFB_LIBRARIES ${RFB_LIBRARIES} ${TBB_LIBRARIES})
endif ()
if (ENABLE_LIBYUV)
find_library(LIBYUV_LIBRARY NAMES yuv)
#pkg_check_modules(LIBYUV libyuv)
if (LIBYUV_FOUND)
set(RFB_LIBRARIES ${RFB_LIBRARIES} ${LIBYUV_LIBRARY})
endif ()
endif ()
add_library(rfb STATIC ${RFB_SOURCES})
target_include_directories(rfb PRIVATE
@ -163,6 +171,7 @@ target_include_directories(rfb PRIVATE
${FFMPEG_INCLUDE_DIRS}
${TBB_INCLUDE_DIRS}
${CPUID_INCLUDE_DIRS}
${LIBYUV_INCLUDE_DIR}
)
set(RFB_LIBRARIES ${RFB_LIBRARIES} tinyxml2_objs ${TBB_LIBRARIES} ${CPUID_LIBRARIES} ${FMT_LIBRARIES} ${VIDEO_ACCELERATION_LIBRARIES})

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2025 Kasm. All Rights Reserved.
*
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -17,11 +17,12 @@
*/
#include "EncoderProbe.h"
#include <fcntl.h>
#include <rfb/LogWriter.h>
#include <string>
#include <unistd.h>
#include <vector>
#include "KasmVideoConstants.h"
#include <rfb/LogWriter.h>
#include "KasmVideoEncoders.h"
extern "C" {
#include <libavcodec/avcodec.h>
@ -33,24 +34,6 @@ extern "C" {
namespace rfb::video_encoders {
static LogWriter vlog("EncoderProbe");
struct EncoderCandidate {
KasmVideoEncoders::Encoder encoder;
AVCodecID codec_id;
AVHWDeviceType hw_type;
};
static std::array<EncoderCandidate, 6> candidates = {
{
//{KasmVideoEncoders::Encoder::h264_nvenc, AV_CODEC_ID_H264, AV_HWDEVICE_TYPE_VAAPI}
//{KasmVideoEncoders::Encoder::av1_vaapi, AV_CODEC_ID_AV1, AV_HWDEVICE_TYPE_VAAPI},
//{KasmVideoEncoders::Encoder::hevc_vaapi, AV_CODEC_ID_HEVC, AV_HWDEVICE_TYPE_VAAPI}, // h265
EncoderCandidate{KasmVideoEncoders::Encoder::h264_ffmpeg_vaapi, AV_CODEC_ID_H264, AV_HWDEVICE_TYPE_VAAPI},
// EncoderCandidate{KasmVideoEncoders::Encoder::h264_software, AV_CODEC_ID_H264, AV_HWDEVICE_TYPE_NONE}
//{KasmVideoEncoders::Encoder::av1_software, AV_CODEC_ID_AV1, AV_HWDEVICE_TYPE_NONE},
//{KasmVideoEncoders::Encoder::h265_software, AV_CODEC_ID_HEVC, AV_HWDEVICE_TYPE_NONE},
}
};
EncoderProbe::EncoderProbe(FFmpeg &ffmpeg_, const std::vector<std::string_view> &parsed_encoders, const char *dri_node) :
ffmpeg(ffmpeg_) {
if (!ffmpeg.is_available()) {
@ -69,7 +52,18 @@ namespace rfb::video_encoders {
const auto encoders = SupportedVideoEncoders::map_encoders(parsed_encoders);
debug_encoders("CLI-specified video codecs", encoders);
available_encoders = probe(dri_node);
static std::array<EncoderCandidate, 4> candidates = {
{
EncoderCandidate{KasmVideoEncoders::Encoder::h264_ffmpeg_vaapi, AV_CODEC_ID_H264, AV_HWDEVICE_TYPE_VAAPI},
EncoderCandidate{KasmVideoEncoders::Encoder::h265_ffmpeg_vaapi, AV_CODEC_ID_HEVC, AV_HWDEVICE_TYPE_VAAPI},
// EncoderCandidate{KasmVideoEncoders::Encoder::av1_ffmpeg_vaapi, AV_CODEC_ID_AV1, AV_HWDEVICE_TYPE_VAAPI},
EncoderCandidate{KasmVideoEncoders::Encoder::h264_software, AV_CODEC_ID_H264, AV_HWDEVICE_TYPE_NONE},
EncoderCandidate{KasmVideoEncoders::Encoder::h265_software, AV_CODEC_ID_HEVC, AV_HWDEVICE_TYPE_NONE},
// EncoderCandidate{KasmVideoEncoders::Encoder::av1_software, AV_CODEC_ID_AV1, AV_HWDEVICE_TYPE_NONE},
}
};
available_encoders = probe(dri_node, candidates);
debug_encoders("Available encoders", available_encoders);
available_encoders = SupportedVideoEncoders::filter_available_encoders(encoders, available_encoders);
@ -83,7 +77,7 @@ namespace rfb::video_encoders {
best_encoder = available_encoders.front();
}
KasmVideoEncoders::Encoders EncoderProbe::probe(const char *dri_node) {
KasmVideoEncoders::Encoders EncoderProbe::probe(const char *dri_node, std::span<EncoderCandidate> candidates) {
KasmVideoEncoders::Encoders result{};
for (const auto &encoder_candidate: candidates) {
const AVCodec *codec = ffmpeg.avcodec_find_encoder_by_name(KasmVideoEncoders::to_string(encoder_candidate.encoder));

View file

@ -18,7 +18,7 @@
#pragma once
#include <vector>
#include "KasmVideoConstants.h"
#include <span>
#include "SupportedVideoEncoders.h"
#include "rfb/ffmpeg.h"
@ -29,8 +29,14 @@ namespace rfb::video_encoders {
std::string drm_device_path;
FFmpeg &ffmpeg;
struct EncoderCandidate {
KasmVideoEncoders::Encoder encoder;
AVCodecID codec_id;
AVHWDeviceType hw_type;
};
explicit EncoderProbe(FFmpeg &ffmpeg, const std::vector<std::string_view> &parsed_encoders, const char *dri_node);
KasmVideoEncoders::Encoders probe(const char *dri_node);
KasmVideoEncoders::Encoders probe(const char *dri_node, std::span<EncoderCandidate> candidates);
public:
EncoderProbe(const EncoderProbe &) = delete;

View file

@ -23,13 +23,13 @@
#include "EncoderProbe.h"
#include "rfb/LogWriter.h"
extern "C" {
#include <libavutil/opt.h>
}
#include "EncoderConfiguration.h"
#include "KasmVideoConstants.h"
#include "rfb/encodings.h"
#include <rfb/encoders/utils.h>
#if defined LIBYUV_CONVERSION
#include <libyuv.h>
#endif
static rfb::LogWriter vlog("FFMPEGVAAPIEncoder");
@ -91,6 +91,7 @@ namespace rfb {
ctx->delay = 0;
ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
//TODO
if (ffmpeg.av_opt_set(ctx->priv_data, "async_depth", "1", 0) < 0) {
vlog.info("Cannot set async_depth");
}
@ -163,15 +164,14 @@ namespace rfb {
vlog.error("Failed to open codec (%s). Error code: %d", ffmpeg.get_error_description(err).c_str(), err);
return false;
}
auto *sws_ctx = ffmpeg.sws_getContext(
width, height, AV_PIX_FMT_RGB32, params.width, params.height, AV_PIX_FMT_NV12, SWS_BILINEAR, nullptr, nullptr, nullptr);
if (!sws_ctx) {
vlog.error("Could not initialize the conversion context");
return false;
}
sws_guard.reset(sws_ctx);
#if defined(FFMPEG_FILTER)
const char* filters = "format=nv12,hwupload"; // Or "scale_vaapi=format=nv12" for explicit VAAPI scaler
ffmpeg.avfilter_graph_parse_ptr(filter_graph, filters, &inputs, &outputs, nullptr);
ffmpeg.av_buffersrc_add_frame_flags(buffersrc_ctx, rgb_frame, AV_BUFFERSRC_FLAG_PUSH);
AVFrame* hw_frame = av_frame_alloc();
ffmpeg.av_buffersink_get_frame_flags(buffersink_ctx, hw_frame, AV_BUFFERSINK_FLAG_NO_REQUEST);
// ffmpeg.avcodec_send_frame(enc_ctx, hw_frame);
#endif
return true;
}
@ -217,14 +217,21 @@ namespace rfb {
frame->pict_type = AV_PICTURE_TYPE_NONE;
}
const uint8_t *src_data[1] = {buffer};
const int src_line_size[1] = {stride * bpp}; // RGB has bpp bytes per pixel
const int src_stride_bytes = stride * bpp;
vlog.debug("render(): width=%d, height=%d, dst_width=%d, dst_height=%d, stride=%d pixels, stride_bytes=%d, bpp=%d",
width, height, dst_width, dst_height, stride, src_stride_bytes, bpp);
int err{};
if (err = ffmpeg.sws_scale(sws_guard.get(), src_data, src_line_size, 0, height, frame->data, frame->linesize); err < 0) {
vlog.error("Error (%s) while scaling image. Error code: %d", ffmpeg.get_error_description(err).c_str(), err);
#if defined(LIBYUV_CONVERSION)
if (err = libyuv::ARGBToNV12(buffer, src_stride_bytes, frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1], dst_width, dst_height); err != 0) {
vlog.error("libyuv::ARGBToNV12 failed with code: %d", err);
return false;
}
#endif
frame->pts = pts++;

View file

@ -32,7 +32,6 @@ class FFMPEGVAAPIEncoder final : public VideoEncoder {
FFmpeg::FrameGuard hw_frame_guard;
FFmpeg::PacketGuard pkt_guard;
FFmpeg::ContextGuard ctx_guard;
FFmpeg::SwsContextGuard sws_guard;
FFmpeg::BufferGuard hw_device_ctx_guard;
FFmpeg::BufferGuard hw_frames_ref_guard;

View file

@ -21,11 +21,15 @@
#include <cassert>
namespace rfb {
static constexpr unsigned int SupportedEncoderCount = 3;
// NOTE: SupportedEncoderCount represents the number of actively supported codec families (H264, H265, AV1)
static constexpr unsigned int SupportedEncoderCount = 5;
// Compression control
static constexpr unsigned int kasmVideoH264 = 0x01 << 4; // H.264 encoding
static constexpr unsigned int kasmVideoH265 = 0x02 << 4; // H.265 encoding
static constexpr unsigned int kasmVideoAV1 = 0x03 << 4; // AV1 encoding
static constexpr unsigned int kasmVideoVP8 = 0x04 << 4; // VP8 encoding (not yet supported)
static constexpr unsigned int kasmVideoVP9 = 0x05 << 4; // VP9 encoding (not yet supported)
static constexpr unsigned int kasmVideoSkip = 0x00 << 4; // Skip frame
static constexpr auto drm_device_paths = std::to_array<const char *>({

View file

@ -26,6 +26,7 @@ extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavfilter/avfilter.h>
}
#define STR_HELPER(x) #x
@ -138,6 +139,10 @@ class FFmpeg final {
using avcodec_close_func = int (*)(AVCodecContext *avctx);
using av_codec_is_encoder_func = int (*)(const AVCodec *codec);
// libavfilter
using avfilter_graph_parse_ptr_func = int (*)(AVFilterGraph *graph, const char *filters, AVFilterInOut **inputs,
AVFilterInOut **outputs, void *log_ctx);
struct DlHandler {
void operator()(void *handle) const {
dlclose(handle);
@ -196,10 +201,14 @@ class FFmpeg final {
avcodec_close_func avcodec_close_f{};
av_codec_is_encoder_func av_codec_is_encoder_f{};
// libavfilter
avfilter_graph_parse_ptr_func avfilter_graph_parse_ptr_f{};
DlHandlerGuard libavformat{};
DlHandlerGuard libavutil{};
DlHandlerGuard libswscale{};
DlHandlerGuard libavcodec{};
DlHandlerGuard libavfilter{};
FFmpeg();
~FFmpeg() = default;