mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2026-07-18 00:45:51 +00:00
VNC-151 Add libyuv support
This commit is contained in:
parent
97f1876e04
commit
ec0bb87d8f
8 changed files with 72 additions and 43 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *>({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue