VNC-151 Build fixes

This commit is contained in:
El 2025-06-25 04:14:03 +05:00
parent 37e26e3c6e
commit 2caa285168
No known key found for this signature in database
GPG key ID: 205388FEB607950A
8 changed files with 58 additions and 334 deletions

View file

@ -68,7 +68,8 @@ RUN \
xorg-server-common \
xorg-server-dev \
xtrans \
ffmpeg-dev
ffmpeg-dev \
libva-dev
ENV SCRIPTS_DIR=/tmp/scripts

View file

@ -6,4 +6,5 @@ source_dir=$(dirname "$0")
"${source_dir}"/build-libjpeg-turbo
"${source_dir}"/build-webp
"${source_dir}"/build-tbb
"${source_dir}"/build-cpuid
"${source_dir}"/build-cpuid
"${source_dir}"/build-fmt

26
builder/scripts/build-fmt Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
build_and_install() {
cmake -S . -B build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -GNinja
ninja -C build install
}
prepare_source() {
DIR=fmt
cd /tmp
[ -d ./${DIR} ] && rm -rf ./${DIR}
mkdir ${DIR}
LIBCPUID_RELEASE=$(curl -sL "https://api.github.com/repos/fmtlib/fmt/releases/latest" \
| grep '"tag_name":' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
curl -Ls "https://github.com/fmtlib/fmt/archive/${LIBCPUID_RELEASE}.tar.gz" | \
tar xzvf - -C ${DIR}/ --strip-components=1
cd ${DIR}
}
prepare_source
build_and_install

View file

@ -136,8 +136,9 @@ endif ()
find_package(PkgConfig REQUIRED)
pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil libswscale)
pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil libswscale libva)
pkg_check_modules(CPUID REQUIRED libcpuid)
pkg_check_modules(FMT REQUIRED fmt)
find_package(TBB)
if (TBB_FOUND)
@ -160,7 +161,7 @@ target_include_directories(rfb PRIVATE
${CPUID_INCLUDE_DIRS}
)
target_link_libraries(rfb PUBLIC ${RFB_LIBRARIES} tinyxml2_objs ${TBB_LIBRARIES} ${CPUID_LIBRARIES})
target_link_libraries(rfb PUBLIC ${RFB_LIBRARIES} tinyxml2_objs ${TBB_LIBRARIES} ${FFMPEG_LIBRARIES} ${CPUID_LIBRARIES} ${FMT_LIBRARIES})
if (UNIX)
libtool_create_control_file(rfb)

View file

@ -1,247 +0,0 @@
/* Copyright (C) 2024 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
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <unistd.h>
#include <rdr/OutStream.h>
#include <rfb/encodings.h>
#include <rfb/LogWriter.h>
#include <rfb/SConnection.h>
#include <rfb/ServerCore.h>
#include <rfb/PixelBuffer.h>
#include <rfb/KasmVideoEncoder.h>
#include <rfb/KasmVideoConstants.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/hwcontext_vaapi.h>
}
using namespace rfb;
static LogWriter vlog("KasmVideoEncoder");
KasmVideoEncoder::KasmVideoEncoder(SConnection *conn) : Encoder(conn, encodingKasmVideo,
static_cast<EncoderFlags>(
EncoderUseNativePF | EncoderLossy), -1) {
}
bool KasmVideoEncoder::isSupported() {
return conn->cp.supportsEncoding(encodingKasmVideo);
}
bool init_codec(int w, int h, int fps, const AVCodec *codec, h264_t *h264) {
h264->frame = av_frame_alloc();
if (!h264->frame) {
vlog.error("Can't allocate AVFrame");
avcodec_free_context(&h264->ctx);
return false;
}
h264->frame->format = hw_accel ? AV_PIX_FMT_VAAPI : AV_PIX_FMT_RGB24;
h264->frame->width = w;
h264->frame->height = h;
if (av_image_alloc(h264->frame->data, h264->frame->linesize, h264->frame->width, h264->frame->height,
AV_PIX_FMT_NV12, 32) < 0) {
vlog.error("Failed to allocate image");
avcodec_free_context(&h264->ctx);
av_frame_free(&h264->frame);
return false;
}
h264->ctx->width = w;
h264->ctx->height = h;
h264->ctx->time_base = (AVRational){1, static_cast<int>(fps)};
h264->ctx->gop_size = 10;
h264->ctx->max_b_frames = 0;
h264->ctx->pix_fmt = hw_accel ? AV_PIX_FMT_VAAPI : AV_PIX_FMT_YUV420P;
if (avcodec_open2(h264->ctx, codec, nullptr) < 0) {
vlog.error("Failed to open codec");
avcodec_free_context(&h264->ctx);
av_frame_free(&h264->frame);
return false;
}
return true;
}
bool create_software_encoder(int width, int height, int fps, h264_t *h264) {
const auto *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
h264->ctx = avcodec_alloc_context3(codec);
if (!h264->ctx) {
vlog.error("Can't allocate AVCodecContext");
return false;
}
if (!init_codec(width, height, fps, codec, h264))
return false;
return true;
}
bool try_create_vaapi_encoder(int width, int height, int fps, h264_t *h264, AVBufferRef *hw_device_ctx) {
av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, render_path, nullptr, 0);
if (!hw_device_ctx) {
vlog.error("Failed to create VAAPI device context");
return false;
}
const auto *codec = avcodec_find_encoder_by_name("h264_vaapi");
h264->ctx = avcodec_alloc_context3(codec);
if (!h264->ctx) {
vlog.error("Can't allocate AVCodecContext");
av_buffer_unref(&hw_device_ctx);
return false;
}
h264->ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
if (!h264->ctx->hw_device_ctx) {
vlog.error("Failed to reference VAAPI device context");
avcodec_free_context(&h264->ctx);
av_buffer_unref(&hw_device_ctx);
return false;
}
if (!init_codec(width, height, fps, codec, h264))
return false;
return true;
}
static void init_h264(h264_t *h264,
const uint32_t w, const uint32_t h, const uint32_t fps,
const uint32_t bitrate) {
AVBufferRef *hw_device_ctx{};
bool success{};
if (!hw_accel) {
success = create_software_encoder(w, h, fps, h264);
} else {
success = try_create_vaapi_encoder(w, h, fps, h264, hw_device_ctx);
if (!success) {
hw_accel = false;
if (success = create_software_encoder(w, h, fps, h264); !success) {
vlog.error("Failed to create software encoder");
return;
}
}
}
if (!h264->ctx) {
vlog.error("Can't allocate AVFrame");
avcodec_free_context(&h264->ctx);
}
}
static void deinit_h264(h264_t *h264) {
if (h264->ctx && h264->ctx->hw_device_ctx) {
av_buffer_unref(&h264->ctx->hw_device_ctx);
}
avcodec_free_context(&h264->ctx);
av_frame_free(&h264->frame);
}
void KasmVideoEncoder::writeRect(const PixelBuffer *pb, const Palette &palette) {
int stride;
const rdr::U8 *buffer = pb->getBuffer(pb->getRect(), &stride);
// compress
AVFrame *pFrame = av_frame_alloc();
if (!pFrame) {
vlog.error("Can't allocate AVFrame");
return;
}
rdr::OutStream *os = conn->getOutStream(conn->cp.supportsUdp);
if (!strcmp(Server::videoCodec, "h264")) {
os->writeU8(kasmVideoH264 << 4);
if (!init) {
init_h264(&h264, pb->getRect().width(), pb->getRect().height(), Server::frameRate,
Server::videoBitrate);
init = true;
sw = pb->getRect().width();
sh = pb->getRect().height();
} else if (sw != static_cast<uint32_t>(pb->getRect().width()) || sh != static_cast<uint32_t>(pb->getRect().
height())) {
deinit_h264(&h264);
init_h264(&h264, pb->getRect().width(), pb->getRect().height(), Server::frameRate,
Server::videoBitrate);
sw = pb->getRect().width();
sh = pb->getRect().height();
}
pFrame->format = hw_accel ? AV_PIX_FMT_VAAPI : AV_PIX_FMT_YUV420P;
pFrame->width = pb->getRect().width(); // Use the width from the PixelBuffer
pFrame->height = pb->getRect().height(); // Use the height from the PixelBuffer
if (av_image_fill_arrays(pFrame->data, pFrame->linesize, buffer, AV_PIX_FMT_BGR24, pb->getRect().width(),
pb->getRect().height(), 1) < 0) {
vlog.error("Can't fill image arrays");
av_frame_free(&pFrame);
return;
}
int ret = avcodec_send_frame(h264.ctx, pFrame);
if (ret < 0) {
vlog.error("Error sending frame to codec");
return;
}
while (ret >= 0) {
AVPacket pkt;
ret = avcodec_receive_packet(h264.ctx, &pkt);
if (ret < 0) {
vlog.error("Error receiving packet from codec");
break;
}
writeCompact(pkt.size + 1, os);
os->writeBytes(&pkt.data[0], pkt.size);
av_packet_unref(&pkt);
}
} else {
vlog.error("Unknown test videoCodec %s", static_cast<const char *>(Server::videoCodec));
}
av_frame_free(&pFrame);
}
void KasmVideoEncoder::writeSkipRect() {
auto *os = conn->getOutStream(conn->cp.supportsUdp);
os->writeU8(kasmVideoSkip << 4);
}
void KasmVideoEncoder::writeCompact(rdr::U32 value, rdr::OutStream *os) {
// Copied from TightEncoder as it's overkill to inherit just for this
rdr::U8 b = value & 0x7F;
if (value <= 0x7F) {
os->writeU8(b);
} else {
os->writeU8(b | 0x80);
b = value >> 7 & 0x7F;
if (value <= 0x3FFF) {
os->writeU8(b);
} else {
os->writeU8(b | 0x80);
os->writeU8(value >> 14 & 0xFF);
}
}
}
void KasmVideoEncoder::writeSolidRect(int width, int height,
const PixelFormat &pf,
const rdr::U8 *colour) {
// nop
}

View file

@ -1,61 +0,0 @@
/* Copyright (C) 2024 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
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#ifndef __RFB_KASMVIDEOENCODER_H__
#define __RFB_KASMVIDEOENCODER_H__
#include <rfb/Encoder.h>
#include <cstdint>
extern "C" {
#include <libavcodec/avcodec.h>
}
struct h264_t {
AVCodecContext *ctx;
AVFrame *frame;
AVPacket pkt;
};
namespace rfb {
class KasmVideoEncoder : public Encoder {
public:
explicit KasmVideoEncoder(SConnection *conn);
~KasmVideoEncoder() override = default;
bool isSupported() override;
void writeRect(const PixelBuffer *pb, const Palette &palette) override;
virtual void writeSkipRect();
void writeSolidRect(int width, int height,
const PixelFormat &pf,
const rdr::U8 *colour) override;
protected:
static void writeCompact(rdr::U32 value, rdr::OutStream *os);
private:
bool init{false};
uint32_t sw{0}, sh{0};
h264_t h264{};
};
}
#endif

View file

@ -77,7 +77,8 @@
#include <unistd.h>
#include <wordexp.h>
#include "KasmVideoConstants.h"
#include <fmt/core.h>
#include "encoders/KasmVideoConstants.h"
using namespace rfb;
@ -239,8 +240,8 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
if (watermarkData)
sendWatermark = true;
if (VideoEncoders::to_string(VideoEncoders::Codecs::H264) != Server::videoCodec.getValueStr())
throw std::invalid_argument(std::format("Unknown test videoCodec {}", Server::videoCodec.getValueStr()));
if (SupportedVideoEncoders::to_string(SupportedVideoEncoders::Codecs::H264) != Server::videoCodec.getValueStr())
throw std::invalid_argument(fmt::format("Unknown test videoCodec {}", Server::videoCodec.getValueStr()));
if (Server::selfBench)
SelfBench();

View file

@ -17,9 +17,9 @@
*/
#pragma once
#include <string_view>
#include <array>
#include <fcntl.h>
#include <cstdint>
#include <string_view>
namespace rfb {
// Compression control
@ -28,24 +28,9 @@ namespace rfb {
static constexpr int GroupOfPictureSize = 10; // interval between I-frames
static auto render_path = "/dev/dri/renderD128";
inline const char *render_path = "/dev/dri/renderD128";
inline bool is_acceleration_available() {
if (access(render_path, R_OK | W_OK) != 0)
return false;
const int fd = open(render_path, O_RDWR);
if (fd < 0)
return false;
close(fd);
return true;
}
inline static bool hw_accel = is_acceleration_available();
struct VideoEncoders {
struct SupportedVideoEncoders {
enum class Codecs : uint8_t
{
H264
@ -57,4 +42,21 @@ namespace rfb {
return CodecNames[static_cast<uint8_t>(codec)];
}
};
struct KasmVideoEncoders {
enum class Encoder : uint8_t
{
h264_vaapi,
h264_nvenc,
h264_software
};
static inline std::array<std::string_view, 3> EncoderNames = {"h264_vaapi", "h264_nvenc", "libx264"};
static std::string_view to_string(Encoder encoder) {
return EncoderNames[static_cast<uint8_t>(encoder)];
}
};
} // namespace rfb