mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2026-01-23 02:14:29 +00:00
KASM-6984 Add benchmark utility with FFmpeg integration for video handling
This commit is contained in:
parent
969996a647
commit
4983bb8be5
5 changed files with 246 additions and 11 deletions
|
|
@ -9,7 +9,7 @@ add_subdirectory(rfb)
|
|||
# because PIC code does not exist on that platform and MinGW complains if -fPIC
|
||||
# is passed (additionally, libvnc is not used on Windows.)
|
||||
|
||||
if(NOT WIN32)
|
||||
set_target_properties(os rdr network Xregion rfb
|
||||
PROPERTIES COMPILE_FLAGS -fPIC)
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
set_target_properties(os rdr network Xregion rfb
|
||||
PROPERTIES COMPILE_FLAGS -fPIC)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
set(RFB_SOURCES
|
||||
benchmark.cxx
|
||||
Blacklist.cxx
|
||||
Congestion.cxx
|
||||
CConnection.cxx
|
||||
|
|
@ -87,7 +88,9 @@ if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_
|
|||
endif ()
|
||||
|
||||
if (HAVE_PAM)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
UnixPasswordValidator.cxx
|
||||
UnixPasswordValidator.h pam.c pam.h)
|
||||
set(RFB_LIBRARIES ${RFB_LIBRARIES} ${PAM_LIBS})
|
||||
endif ()
|
||||
|
|
@ -122,9 +125,14 @@ else ()
|
|||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
${SCALE_DUMMY_SOURCES}
|
||||
benchmark.h
|
||||
)
|
||||
endif ()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil libswscale)
|
||||
|
||||
add_library(rfb STATIC ${RFB_SOURCES})
|
||||
|
||||
target_include_directories(rfb PRIVATE
|
||||
|
|
@ -135,7 +143,7 @@ target_include_directories(rfb PRIVATE
|
|||
${CMAKE_SOURCE_DIR}/third_party/tinyxml2
|
||||
)
|
||||
|
||||
target_link_libraries(rfb PRIVATE ${RFB_LIBRARIES} tinyxml2_objs)
|
||||
target_link_libraries(rfb PRIVATE ${RFB_LIBRARIES} tinyxml2_objs ${FFMPEG_LIBRARIES})
|
||||
|
||||
if (UNIX)
|
||||
libtool_create_control_file(rfb)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#include <wordexp.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
|
|
@ -82,6 +83,8 @@ EncCache VNCServerST::encCache;
|
|||
|
||||
void SelfBench();
|
||||
|
||||
void benchmark(const std::string&);
|
||||
|
||||
//
|
||||
// -=- VNCServerST Implementation
|
||||
//
|
||||
|
|
@ -126,7 +129,7 @@ static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest,
|
|||
*inptr = ptr;
|
||||
}
|
||||
|
||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_, bool a)
|
||||
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
||||
blockCounter(0), pb(0), blackedpb(0), ledState(ledUnknown),
|
||||
name(strDup(name_)), pointerClient(0), clipboardClient(0),
|
||||
|
|
@ -223,11 +226,18 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
|||
|
||||
trackingClient[0] = 0;
|
||||
|
||||
if (watermarkData)
|
||||
sendWatermark = true;
|
||||
if (watermarkData)
|
||||
sendWatermark = true;
|
||||
|
||||
if (Server::selfBench)
|
||||
SelfBench();
|
||||
if (Server::selfBench)
|
||||
SelfBench();
|
||||
|
||||
if (Server::benchmark) {
|
||||
auto *file_name = Server::benchmark.getValueStr();
|
||||
if (std::filesystem::exists(file_name))
|
||||
throw Exception("Benchmarking video file does not exist");
|
||||
benchmark(file_name);
|
||||
}
|
||||
}
|
||||
|
||||
VNCServerST::~VNCServerST()
|
||||
|
|
|
|||
115
common/rfb/benchmark.cxx
Normal file
115
common/rfb/benchmark.cxx
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/* Copyright (C) 2025 Kasm Web
|
||||
*
|
||||
* 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 "benchmark.h"
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include "VNCServer.h"
|
||||
|
||||
static rfb::LogWriter vlog("Benchmarking");
|
||||
|
||||
void benchmark(const std::string &path) {
|
||||
AVFormatContext *format_ctx = nullptr;
|
||||
if (avformat_open_input(&format_ctx, path.c_str(), nullptr, nullptr) < 0)
|
||||
throw std::runtime_error("Could not open video file");
|
||||
|
||||
FormatCtxGuard format_ctx_guard{format_ctx};
|
||||
|
||||
// Find stream info
|
||||
if (avformat_find_stream_info(format_ctx, nullptr) < 0)
|
||||
throw std::runtime_error("Could not find stream info");
|
||||
|
||||
// Find video stream
|
||||
int video_stream_idx = -1;
|
||||
for (uint32_t i = 0; i < format_ctx->nb_streams; ++i) {
|
||||
if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
video_stream_idx = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (video_stream_idx == -1)
|
||||
throw std::runtime_error("No video stream found");
|
||||
|
||||
// Get codec parameters and decoder
|
||||
const auto *codec_parameters = format_ctx->streams[video_stream_idx]->codecpar;
|
||||
const auto *codec = avcodec_find_decoder(codec_parameters->codec_id);
|
||||
if (!codec)
|
||||
throw std::runtime_error("Codec not found");
|
||||
|
||||
CodecCtxGuard codex_ctx_guard{avcodec_alloc_context3(codec)};
|
||||
auto *codec_ctx = codex_ctx_guard.get();
|
||||
|
||||
if (!codec_ctx || avcodec_parameters_to_context(codec_ctx, codec_parameters) < 0)
|
||||
throw std::runtime_error("Failed to set up codec context");
|
||||
|
||||
if (avcodec_open2(codec_ctx, codec, nullptr) < 0)
|
||||
throw std::runtime_error("Could not open codec");
|
||||
|
||||
// Allocate frame and packet
|
||||
FrameGuard frame_guard{av_frame_alloc()};
|
||||
auto *frame = frame_guard.get();
|
||||
|
||||
PacketGuard packet_guard{av_packet_alloc()};
|
||||
auto *packet = packet_guard.get();
|
||||
|
||||
if (!frame || !packet)
|
||||
throw std::runtime_error("Could not allocate frame or packet");
|
||||
|
||||
// Scaling context to convert to RGB24
|
||||
SwsContext *sws_ctx = sws_getContext(
|
||||
codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
|
||||
codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24,
|
||||
SWS_BILINEAR, nullptr, nullptr, nullptr
|
||||
);
|
||||
|
||||
if (!sws_ctx)
|
||||
throw std::runtime_error("Could not create scaling context");
|
||||
|
||||
SwsContextGuard sws_ctx_guard{sws_ctx};
|
||||
|
||||
FrameGuard rgb_frame_guard{av_frame_alloc()};
|
||||
auto *rgb_frame = rgb_frame_guard.get();
|
||||
|
||||
if (!rgb_frame)
|
||||
throw std::runtime_error("Could not allocate frame");
|
||||
|
||||
rgb_frame->format = AV_PIX_FMT_RGB24;
|
||||
rgb_frame->width = codec_ctx->width;
|
||||
rgb_frame->height = codec_ctx->height;
|
||||
|
||||
if (av_frame_get_buffer(rgb_frame, 0) != 0)
|
||||
throw std::runtime_error("Could not allocate frame data");
|
||||
|
||||
while (av_read_frame(format_ctx, packet) == 0) {
|
||||
if (packet->stream_index == video_stream_idx) {
|
||||
if (avcodec_send_packet(codec_ctx, packet) == 0) {
|
||||
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
|
||||
// Convert to RGB
|
||||
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height,
|
||||
rgb_frame->data, rgb_frame->linesize);
|
||||
|
||||
// Save as BMP
|
||||
// saveFrameAsBMP(rgb_frame, ++frameNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
av_packet_unref(packet);
|
||||
}
|
||||
}
|
||||
102
common/rfb/benchmark.h
Normal file
102
common/rfb/benchmark.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/* Copyright (C) 2025 Kasm Web
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Timer.h"
|
||||
#include <memory>
|
||||
#include <rfb/VNCServer.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
struct AVFormatContextDeleter {
|
||||
void operator()(AVFormatContext *ctx) const {
|
||||
avformat_close_input(&ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct AVCodecContextDeleter {
|
||||
void operator()(AVCodecContext *ctx) const {
|
||||
avcodec_free_context(&ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct AVFrameDeleter {
|
||||
void operator()(AVFrame *frame) const {
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
};
|
||||
|
||||
struct SwsContextDeleter {
|
||||
void operator()(SwsContext *ctx) const {
|
||||
sws_freeContext(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct PacketDeleter {
|
||||
void operator()(AVPacket *packet) const {
|
||||
av_packet_free(&packet);
|
||||
}
|
||||
};
|
||||
|
||||
using FormatCtxGuard = std::unique_ptr<AVFormatContext, AVFormatContextDeleter>;
|
||||
using CodecCtxGuard = std::unique_ptr<AVCodecContext, AVCodecContextDeleter>;
|
||||
using FrameGuard = std::unique_ptr<AVFrame, AVFrameDeleter>;
|
||||
using SwsContextGuard = std::unique_ptr<SwsContext, SwsContextDeleter>;
|
||||
using PacketGuard = std::unique_ptr<AVPacket, PacketDeleter>;
|
||||
|
||||
namespace rfb {
|
||||
class BenchmarkServer : public VNCServer, public Timer::Callback {
|
||||
public:
|
||||
bool handleTimeout(Timer *t) override;
|
||||
|
||||
void add_changed(const Region ®ion) override;
|
||||
|
||||
void add_copied(const Region &dest, const Point &delta) override;
|
||||
|
||||
void blockUpdates() override;
|
||||
|
||||
void unblockUpdates() override;
|
||||
|
||||
void setPixelBuffer(PixelBuffer *pb, const ScreenSet &layout) override;
|
||||
|
||||
void setPixelBuffer(PixelBuffer *pb) override;
|
||||
|
||||
void setScreenLayout(const ScreenSet &layout) override;
|
||||
|
||||
[[nodiscard]] PixelBuffer *getPixelBuffer() const override;
|
||||
|
||||
void announceClipboard(bool available) override;
|
||||
|
||||
void bell() override;
|
||||
|
||||
void closeClients(const char *reason) override;
|
||||
|
||||
void setCursor(int width, int height, const Point &hotspot, const rdr::U8 *cursorData, bool resizing) override;
|
||||
|
||||
void setCursorPos(const Point &p, bool warped) override;
|
||||
|
||||
void setName(const char *name) override;
|
||||
|
||||
void setLEDState(unsigned state) override;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue