mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2026-01-23 02:14:29 +00:00
rebase and fix conflicts
This commit is contained in:
commit
38eb2c6110
56 changed files with 6855 additions and 406 deletions
|
|
@ -29,6 +29,7 @@ stages:
|
|||
- pwd
|
||||
- apk add bash
|
||||
- mkdir -p "$GITLAB_SHARED_DIND_DIR" && chmod 777 "$GITLAB_SHARED_DIND_DIR"
|
||||
- docker login --username $DOCKER_HUB_USERNAME --password $DOCKER_HUB_PASSWORD
|
||||
|
||||
.prepare_www: &prepare_www
|
||||
- tar -zxf output/www/kasm_www.tar.gz -C builder/
|
||||
|
|
@ -527,9 +528,14 @@ build_fedora_fortyone_arm:
|
|||
test:
|
||||
stage: test
|
||||
tags:
|
||||
- oci-fixed-amd
|
||||
- kasmvnc-x86
|
||||
before_script:
|
||||
- *prepare_build
|
||||
artifacts:
|
||||
reports:
|
||||
junit:
|
||||
- SelfBench.xml
|
||||
- Benchmark.xml
|
||||
script:
|
||||
- bash builder/test-vncserver
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ npm run build # <-- only run this on subsequent changes to front-end code
|
|||
cd ..
|
||||
# build dependencies
|
||||
sudo builder/scripts/build-webp
|
||||
sudo builder/scripts/build-build-libjpeg-turbo
|
||||
sudo builder/scripts/build-libjpeg-turbo
|
||||
# Build KasmVNC
|
||||
builder/build.sh
|
||||
```
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ include_directories(${CMAKE_BINARY_DIR})
|
|||
|
||||
include(cmake/StaticBuild.cmake)
|
||||
|
||||
add_subdirectory(third_party)
|
||||
add_subdirectory(common)
|
||||
|
||||
if(WIN32)
|
||||
|
|
@ -239,11 +240,12 @@ else()
|
|||
endif()
|
||||
|
||||
if(ENABLE_NLS)
|
||||
add_subdirectory(po)
|
||||
add_subdirectory(po)
|
||||
endif()
|
||||
|
||||
#############add_subdirectory(tests)
|
||||
|
||||
if (TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
include(cmake/BuildPackages.cmake)
|
||||
|
||||
|
|
|
|||
|
|
@ -44,38 +44,41 @@ if [[ "${XORG_VER}" == 21* ]]; then
|
|||
else
|
||||
XORG_PATCH=$(echo "$XORG_VER" | grep -Po '^\d.\d+' | sed 's#\.##')
|
||||
fi
|
||||
wget --no-check-certificate https://www.x.org/archive/individual/xserver/xorg-server-${XORG_VER}.tar.gz
|
||||
|
||||
TARBALL="xorg-server-${XORG_VER}.tar.gz"
|
||||
|
||||
if [ ! -f "$TARBALL" ]; then
|
||||
wget --no-check-certificate https://www.x.org/archive/individual/xserver/"$TARBALL"
|
||||
fi
|
||||
|
||||
#git clone https://kasmweb@bitbucket.org/kasmtech/kasmvnc.git
|
||||
#cd kasmvnc
|
||||
#git checkout dynjpeg
|
||||
cd /src
|
||||
|
||||
# We only want the server, so FLTK and manual tests aren't useful.
|
||||
# Alternatively, install fltk 1.3 and its dev packages.
|
||||
sed -i -e '/find_package(FLTK/s@^@#@' \
|
||||
-e '/add_subdirectory(tests/s@^@#@' \
|
||||
CMakeLists.txt
|
||||
|
||||
cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo . -DBUILD_VIEWER:BOOL=OFF \
|
||||
-DENABLE_GNUTLS:BOOL=OFF
|
||||
make -j5
|
||||
make -j"$(nproc)"
|
||||
|
||||
tar -C unix/xserver -xf /tmp/xorg-server-${XORG_VER}.tar.gz --strip-components=1
|
||||
if [ ! -d unix/xserver/include ]; then
|
||||
tar -C unix/xserver -xf /tmp/"$TARBALL" --strip-components=1
|
||||
|
||||
cd unix/xserver
|
||||
# Apply patches
|
||||
patch -Np1 -i ../xserver${XORG_PATCH}.patch
|
||||
case "$XORG_VER" in
|
||||
1.20.*)
|
||||
patch -s -p0 < ../CVE-2022-2320-v1.20.patch
|
||||
if [ -f ../xserver120.7.patch ]; then
|
||||
patch -Np1 -i ../xserver120.7.patch
|
||||
fi ;;
|
||||
1.19.*)
|
||||
patch -s -p0 < ../CVE-2022-2320-v1.19.patch
|
||||
;;
|
||||
esac
|
||||
cd unix/xserver
|
||||
# Apply patches
|
||||
patch -Np1 -i ../xserver"${XORG_PATCH}".patch
|
||||
case "$XORG_VER" in
|
||||
1.20.*)
|
||||
patch -s -p0 < ../CVE-2022-2320-v1.20.patch
|
||||
if [ -f ../xserver120.7.patch ]; then
|
||||
patch -Np1 -i ../xserver120.7.patch
|
||||
fi ;;
|
||||
1.19.*)
|
||||
patch -s -p0 < ../CVE-2022-2320-v1.19.patch
|
||||
;;
|
||||
esac
|
||||
else
|
||||
cd unix/xserver
|
||||
fi
|
||||
|
||||
autoreconf -i
|
||||
# Configuring Xorg is long and has many distro-specific paths.
|
||||
|
|
@ -110,23 +113,27 @@ fi
|
|||
--with-sha1=libcrypto \
|
||||
--with-xkb-bin-directory=/usr/bin \
|
||||
--with-xkb-output=/var/lib/xkb \
|
||||
--with-xkb-path=/usr/share/X11/xkb ${CONFIG_OPTIONS}
|
||||
--with-xkb-path=/usr/share/X11/xkb "${CONFIG_OPTIONS}"
|
||||
|
||||
# remove array bounds errors for new versions of GCC
|
||||
find . -name "Makefile" -exec sed -i 's/-Werror=array-bounds//g' {} \;
|
||||
make -j5
|
||||
|
||||
|
||||
make -j"$(nproc)"
|
||||
|
||||
# modifications for the servertarball
|
||||
cd /src
|
||||
mkdir -p xorg.build/bin
|
||||
mkdir -p xorg.build/lib
|
||||
cd xorg.build/bin/
|
||||
ln -sf /src/unix/xserver/hw/vnc/Xvnc Xvnc
|
||||
ln -sfn /src/unix/xserver/hw/vnc/Xvnc Xvnc
|
||||
cd ..
|
||||
mkdir -p man/man1
|
||||
touch man/man1/Xserver.1
|
||||
cp /src/unix/xserver/hw/vnc/Xvnc.man man/man1/Xvnc.1
|
||||
|
||||
mkdir -p lib
|
||||
|
||||
cd lib
|
||||
if [ -d /usr/lib/x86_64-linux-gnu/dri ]; then
|
||||
ln -sfn /usr/lib/x86_64-linux-gnu/dri dri
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ RUN \
|
|||
bash \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
nasm \
|
||||
coreutils \
|
||||
curl \
|
||||
eudev-dev \
|
||||
|
|
@ -66,7 +67,8 @@ RUN \
|
|||
xorgproto \
|
||||
xorg-server-common \
|
||||
xorg-server-dev \
|
||||
xtrans
|
||||
xtrans \
|
||||
ffmpeg-dev
|
||||
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ RUN \
|
|||
bash \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
nasm \
|
||||
coreutils \
|
||||
curl \
|
||||
eudev-dev \
|
||||
|
|
@ -66,7 +67,8 @@ RUN \
|
|||
xorgproto \
|
||||
xorg-server-common \
|
||||
xorg-server-dev \
|
||||
xtrans
|
||||
xtrans \
|
||||
ffmpeg-dev
|
||||
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ RUN \
|
|||
bash \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
nasm \
|
||||
coreutils \
|
||||
curl \
|
||||
eudev-dev \
|
||||
|
|
@ -66,7 +67,8 @@ RUN \
|
|||
xorgproto \
|
||||
xorg-server-common \
|
||||
xorg-server-dev \
|
||||
xtrans
|
||||
xtrans \
|
||||
ffmpeg-dev
|
||||
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
FROM alpine:3.21
|
||||
|
||||
RUN apk add shadow bash
|
||||
RUN apk add abuild sudo less
|
||||
RUN apk add abuild sudo less ffmpeg-dev
|
||||
|
||||
ENV HOME /src/alpine
|
||||
WORKDIR $HOME/kasmvncserver
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ RUN \
|
|||
bash \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
nasm \
|
||||
coreutils \
|
||||
curl \
|
||||
eudev-dev \
|
||||
|
|
@ -66,7 +67,8 @@ RUN \
|
|||
xorgproto \
|
||||
xorg-server-common \
|
||||
xorg-server-dev \
|
||||
xtrans
|
||||
xtrans \
|
||||
ffmpeg-dev
|
||||
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ RUN apt-get update && \
|
|||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
RUN apt-get update && apt-get -y install ninja-build cmake nasm git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev \
|
||||
libxcursor-dev libavformat-dev libswscale-dev
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ RUN apt-get update && \
|
|||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
RUN apt-get update && apt-get -y install ninja-build nasm git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev \
|
||||
libxcursor-dev libavformat-dev libswscale-dev
|
||||
|
||||
RUN CMAKE_URL="https://cmake.org/files/v3.22/cmake-3.22.0" && \
|
||||
ARCH=$(arch) && \
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ RUN apt-get update && \
|
|||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install ninja-build cmake nasm git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ RUN \
|
|||
byacc \
|
||||
bzip2 \
|
||||
cmake \
|
||||
nasm \
|
||||
diffutils \
|
||||
doxygen \
|
||||
file \
|
||||
|
|
@ -71,7 +72,9 @@ RUN \
|
|||
xorg-x11-server-common \
|
||||
xorg-x11-server-devel \
|
||||
xorg-x11-xtrans-devel \
|
||||
xsltproc
|
||||
xsltproc \
|
||||
libavformat-free-devel \
|
||||
libswscale-free-devel
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ RUN \
|
|||
byacc \
|
||||
bzip2 \
|
||||
cmake \
|
||||
nasm \
|
||||
diffutils \
|
||||
doxygen \
|
||||
file \
|
||||
|
|
@ -72,7 +73,9 @@ RUN \
|
|||
xorg-x11-server-common \
|
||||
xorg-x11-server-devel \
|
||||
xorg-x11-xtrans-devel \
|
||||
xsltproc
|
||||
xsltproc \
|
||||
libavformat-free-devel \
|
||||
libswscale-free-devel
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ RUN \
|
|||
byacc \
|
||||
bzip2 \
|
||||
cmake \
|
||||
nasm \
|
||||
diffutils \
|
||||
doxygen \
|
||||
file \
|
||||
|
|
@ -71,7 +72,9 @@ RUN \
|
|||
xorg-x11-server-common \
|
||||
xorg-x11-server-devel \
|
||||
xorg-x11-xtrans-devel \
|
||||
xsltproc
|
||||
xsltproc \
|
||||
libavformat-free-devel \
|
||||
libswscale-free-devel
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ RUN apt-get update && \
|
|||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install gcc g++ curl
|
||||
RUN apt-get update && apt-get -y install cmake git libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
RUN apt-get update && apt-get -y install ninja-build cmake nasm git libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev \
|
||||
libxcursor-dev libavformat-dev libswscale-dev
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -8,9 +8,13 @@ ENV XORG_VER 1.20.3
|
|||
RUN zypper install -ny \
|
||||
bdftopcf \
|
||||
bigreqsproto-devel \
|
||||
ninja \
|
||||
cmake \
|
||||
nasm \
|
||||
curl \
|
||||
ffmpeg-4-libavcodec-devel \
|
||||
ffmpeg-4-libswscale-devel \
|
||||
ffmpeg-4-libavformat-devel \
|
||||
fonttosfnt \
|
||||
font-util \
|
||||
gcc14 \
|
||||
|
|
@ -49,7 +53,6 @@ RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 140 \
|
|||
--slave /usr/bin/g++ g++ /usr/bin/g++-14 \
|
||||
--slave /usr/bin/gcov gcov /usr/bin/gcov-14
|
||||
|
||||
|
||||
RUN useradd -u 1000 docker && \
|
||||
groupadd -g 1000 docker && \
|
||||
usermod -a -G docker docker
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ RUN \
|
|||
dnf install -y \
|
||||
bzip2-devel \
|
||||
ca-certificates \
|
||||
ninja-build \
|
||||
cmake \
|
||||
nasm \
|
||||
dnf-plugins-core \
|
||||
gcc \
|
||||
gcc-c++ \
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ RUN \
|
|||
dnf install -y \
|
||||
bzip2-devel \
|
||||
ca-certificates \
|
||||
ninja-build \
|
||||
cmake \
|
||||
nasm \
|
||||
dnf-plugins-core \
|
||||
gcc \
|
||||
gcc-c++ \
|
||||
|
|
@ -41,6 +43,7 @@ RUN dnf install -y --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-
|
|||
# Install from new repos
|
||||
RUN dnf install -y \
|
||||
giflib-devel \
|
||||
ffmpeg-devel \
|
||||
lbzip2 \
|
||||
libXfont2-devel \
|
||||
libxkbfile-devel \
|
||||
|
|
|
|||
|
|
@ -12,13 +12,12 @@ RUN apt-get update && \
|
|||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git vim wget curl
|
||||
RUN apt-get update && apt-get -y install libtbb-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
RUN apt-get update && apt-get -y install ninja-build nasm git vim wget curl
|
||||
RUN apt-get update && apt-get -y install libtbb-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev \
|
||||
libxcursor-dev libavformat-dev libswscale-dev
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
RUN $SCRIPTS_DIR/build-webp
|
||||
RUN $SCRIPTS_DIR/build-libjpeg-turbo
|
||||
|
||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||
|
||||
|
|
@ -35,6 +34,9 @@ RUN ARCH=$(arch) && \
|
|||
(echo y; echo n) | bash cmake.sh --prefix=/usr/local --skip-license && \
|
||||
rm cmake.sh
|
||||
|
||||
RUN $SCRIPTS_DIR/build-webp
|
||||
RUN $SCRIPTS_DIR/build-libjpeg-turbo
|
||||
|
||||
COPY --chown=docker:docker . /src/
|
||||
|
||||
USER docker
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y vim less
|
|||
RUN apt-get update && apt-get install -y python3-pip
|
||||
RUN apt-get update && apt-get install -y strace silversearcher-ag xfonts-base
|
||||
RUN apt-get update && apt-get install -y cinnamon
|
||||
RUN apt-get update && apt-get install -y mate
|
||||
RUN apt-get update && apt-get install -y mate wget
|
||||
|
||||
RUN useradd -m docker
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ RUN apt-get update && \
|
|||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
RUN apt-get update && apt-get -y install ninja-build cmake nasm git libgnutls28-dev vim wget tightvncserver curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev \
|
||||
libxcursor-dev libavformat-dev libswscale-dev
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -14,15 +14,58 @@ COPY builder/conf/nginx_kasm.conf /etc/nginx/conf.d/
|
|||
|
||||
RUN sed -i 's$# deb-src$deb-src$' /etc/apt/sources.list && \
|
||||
apt update && \
|
||||
apt install -y socat vim wget tightvncserver curl inotify-tools sudo \
|
||||
libxfont-dev libgnutls28-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev pkg-config libfreetype6-dev \
|
||||
libxtst-dev xutils-dev libpixman-1-dev libxshmfence-dev libxcvt-dev libxkbfile-dev x11proto-dev libgbm-dev \
|
||||
cmake git autoconf automake libtool && \
|
||||
apt install -y \
|
||||
ninja-build \
|
||||
gdb \
|
||||
valgrind \
|
||||
rsync \
|
||||
dos2unix \
|
||||
socat \
|
||||
sudo \
|
||||
libxfont-dev \
|
||||
cmake \
|
||||
nasm \
|
||||
git \
|
||||
libgnutls28-dev \
|
||||
vim \
|
||||
wget \
|
||||
tightvncserver \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libtiff-dev \
|
||||
libgif-dev \
|
||||
libavformat-dev \
|
||||
libavcodec-dev \
|
||||
libswscale-dev \
|
||||
libssl-dev \
|
||||
libxrandr-dev \
|
||||
libxcursor-dev \
|
||||
pkg-config \
|
||||
libfreetype6-dev \
|
||||
libxtst-dev \
|
||||
autoconf \
|
||||
automake \
|
||||
libtool \
|
||||
xutils-dev \
|
||||
libpixman-1-dev \
|
||||
libxshmfence-dev \
|
||||
libxcvt-dev \
|
||||
libxkbfile-dev \
|
||||
x11proto-dev \
|
||||
libgbm-dev \
|
||||
inotify-tools && \
|
||||
echo "kasm-user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
RUN apt install -y nodejs nginx
|
||||
|
||||
|
||||
COPY builder/scripts/build-webp /tmp
|
||||
COPY builder/scripts/build-libjpeg-turbo /tmp
|
||||
COPY builder/common.sh /tmp
|
||||
|
||||
RUN chmod +x /tmp/build-webp && /tmp/build-webp
|
||||
RUN chmod +x /tmp/build-libjpeg-turbo && /tmp/build-libjpeg-turbo
|
||||
|
||||
USER 1000
|
||||
|
||||
WORKDIR /src
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ RUN apt-get update && \
|
|||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libgnutls28-dev vim wget curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
|
||||
RUN apt-get update && apt-get -y install ninja-build cmake nasm git libgnutls28-dev vim wget curl
|
||||
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev \
|
||||
libxcursor-dev libavformat-dev libswscale-dev
|
||||
|
||||
ENV SCRIPTS_DIR=/tmp/scripts
|
||||
COPY builder/scripts $SCRIPTS_DIR
|
||||
|
|
|
|||
|
|
@ -3,20 +3,8 @@
|
|||
set -euo pipefail
|
||||
|
||||
build_and_install() {
|
||||
export MAKEFLAGS=-j`nproc`
|
||||
export CFLAGS="-fpic"
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -G"Unix Makefiles"
|
||||
make
|
||||
make install
|
||||
}
|
||||
|
||||
install_build_dependencies() {
|
||||
install_packages cmake gcc
|
||||
ensure_libjpeg_is_fast
|
||||
}
|
||||
|
||||
ensure_libjpeg_is_fast() {
|
||||
install_packages nasm
|
||||
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja .
|
||||
ninja -C build install
|
||||
}
|
||||
|
||||
prepare_libjpeg_source() {
|
||||
|
|
@ -32,6 +20,5 @@ prepare_libjpeg_source() {
|
|||
source_dir=$(dirname "$0")
|
||||
. "$source_dir/common.sh"
|
||||
|
||||
install_build_dependencies
|
||||
prepare_libjpeg_source
|
||||
build_and_install
|
||||
|
|
|
|||
|
|
@ -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,84 +1,84 @@
|
|||
include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR} ${PNG_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
||||
|
||||
set(RFB_SOURCES
|
||||
Blacklist.cxx
|
||||
Congestion.cxx
|
||||
CConnection.cxx
|
||||
CMsgHandler.cxx
|
||||
CMsgReader.cxx
|
||||
CMsgWriter.cxx
|
||||
CSecurityPlain.cxx
|
||||
CSecurityStack.cxx
|
||||
CSecurityVeNCrypt.cxx
|
||||
CSecurityVncAuth.cxx
|
||||
ComparingUpdateTracker.cxx
|
||||
Configuration.cxx
|
||||
ConnParams.cxx
|
||||
CopyRectDecoder.cxx
|
||||
Cursor.cxx
|
||||
DecodeManager.cxx
|
||||
Decoder.cxx
|
||||
d3des.c
|
||||
EncCache.cxx
|
||||
EncodeManager.cxx
|
||||
Encoder.cxx
|
||||
HextileDecoder.cxx
|
||||
HextileEncoder.cxx
|
||||
JpegCompressor.cxx
|
||||
JpegDecompressor.cxx
|
||||
KeyRemapper.cxx
|
||||
LogWriter.cxx
|
||||
Logger.cxx
|
||||
Logger_file.cxx
|
||||
Logger_stdio.cxx
|
||||
Password.cxx
|
||||
PixelBuffer.cxx
|
||||
PixelFormat.cxx
|
||||
RREEncoder.cxx
|
||||
RREDecoder.cxx
|
||||
RawDecoder.cxx
|
||||
RawEncoder.cxx
|
||||
Region.cxx
|
||||
SConnection.cxx
|
||||
SMsgHandler.cxx
|
||||
SMsgReader.cxx
|
||||
SMsgWriter.cxx
|
||||
ServerCore.cxx
|
||||
Security.cxx
|
||||
SecurityServer.cxx
|
||||
SecurityClient.cxx
|
||||
SelfBench.cxx
|
||||
SSecurityPlain.cxx
|
||||
SSecurityStack.cxx
|
||||
SSecurityVncAuth.cxx
|
||||
SSecurityVeNCrypt.cxx
|
||||
ScaleFilters.cxx
|
||||
Timer.cxx
|
||||
TightDecoder.cxx
|
||||
TightEncoder.cxx
|
||||
TightJPEGEncoder.cxx
|
||||
TightWEBPEncoder.cxx
|
||||
TightQOIEncoder.cxx
|
||||
UpdateTracker.cxx
|
||||
VNCSConnectionST.cxx
|
||||
VNCServerST.cxx
|
||||
ZRLEEncoder.cxx
|
||||
ZRLEDecoder.cxx
|
||||
Watermark.cxx
|
||||
cpuid.cxx
|
||||
encodings.cxx
|
||||
util.cxx
|
||||
xxhash.c)
|
||||
benchmark.cxx
|
||||
Blacklist.cxx
|
||||
Congestion.cxx
|
||||
CConnection.cxx
|
||||
CMsgHandler.cxx
|
||||
CMsgReader.cxx
|
||||
CMsgWriter.cxx
|
||||
CSecurityPlain.cxx
|
||||
CSecurityStack.cxx
|
||||
CSecurityVeNCrypt.cxx
|
||||
CSecurityVncAuth.cxx
|
||||
ComparingUpdateTracker.cxx
|
||||
Configuration.cxx
|
||||
ConnParams.cxx
|
||||
CopyRectDecoder.cxx
|
||||
Cursor.cxx
|
||||
DecodeManager.cxx
|
||||
Decoder.cxx
|
||||
d3des.c
|
||||
EncCache.cxx
|
||||
EncodeManager.cxx
|
||||
Encoder.cxx
|
||||
HextileDecoder.cxx
|
||||
HextileEncoder.cxx
|
||||
JpegCompressor.cxx
|
||||
JpegDecompressor.cxx
|
||||
KeyRemapper.cxx
|
||||
LogWriter.cxx
|
||||
Logger.cxx
|
||||
Logger_file.cxx
|
||||
Logger_stdio.cxx
|
||||
Password.cxx
|
||||
PixelBuffer.cxx
|
||||
PixelFormat.cxx
|
||||
RREEncoder.cxx
|
||||
RREDecoder.cxx
|
||||
RawDecoder.cxx
|
||||
RawEncoder.cxx
|
||||
Region.cxx
|
||||
SConnection.cxx
|
||||
SMsgHandler.cxx
|
||||
SMsgReader.cxx
|
||||
SMsgWriter.cxx
|
||||
ServerCore.cxx
|
||||
Security.cxx
|
||||
SecurityServer.cxx
|
||||
SecurityClient.cxx
|
||||
SelfBench.cxx
|
||||
SSecurityPlain.cxx
|
||||
SSecurityStack.cxx
|
||||
SSecurityVncAuth.cxx
|
||||
SSecurityVeNCrypt.cxx
|
||||
ScaleFilters.cxx
|
||||
Timer.cxx
|
||||
TightDecoder.cxx
|
||||
TightEncoder.cxx
|
||||
TightJPEGEncoder.cxx
|
||||
TightWEBPEncoder.cxx
|
||||
TightQOIEncoder.cxx
|
||||
UpdateTracker.cxx
|
||||
VNCSConnectionST.cxx
|
||||
VNCServerST.cxx
|
||||
ZRLEEncoder.cxx
|
||||
ZRLEDecoder.cxx
|
||||
Watermark.cxx
|
||||
cpuid.cxx
|
||||
encodings.cxx
|
||||
util.cxx
|
||||
xxhash.c
|
||||
ffmpeg.cxx
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} Logger_syslog.cxx)
|
||||
endif()
|
||||
if (UNIX)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} Logger_syslog.cxx)
|
||||
endif ()
|
||||
|
||||
if(WIN32)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/win)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} WinPasswdValidator.cxx)
|
||||
endif(WIN32)
|
||||
if (WIN32)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/win)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} WinPasswdValidator.cxx)
|
||||
endif (WIN32)
|
||||
|
||||
set(RFB_LIBRARIES ${JPEG_LIBRARIES} ${PNG_LIBRARIES} os rdr Xregion)
|
||||
|
||||
|
|
@ -88,49 +88,64 @@ if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_
|
|||
set(RFB_LIBRARIES ${RFB_LIBRARIES} tbb)
|
||||
endif ()
|
||||
|
||||
if(HAVE_PAM)
|
||||
set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx
|
||||
UnixPasswordValidator.h pam.c pam.h)
|
||||
set(RFB_LIBRARIES ${RFB_LIBRARIES} ${PAM_LIBS})
|
||||
endif()
|
||||
if (HAVE_PAM)
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
UnixPasswordValidator.cxx
|
||||
UnixPasswordValidator.h pam.c pam.h)
|
||||
set(RFB_LIBRARIES ${RFB_LIBRARIES} ${PAM_LIBS})
|
||||
endif ()
|
||||
|
||||
if(GNUTLS_FOUND)
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
CSecurityTLS.cxx
|
||||
SSecurityTLS.cxx
|
||||
)
|
||||
set(RFB_LIBRARIES
|
||||
${RFB_LIBRARIES}
|
||||
${GNUTLS_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
if (GNUTLS_FOUND)
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
CSecurityTLS.cxx
|
||||
SSecurityTLS.cxx
|
||||
)
|
||||
set(RFB_LIBRARIES
|
||||
${RFB_LIBRARIES}
|
||||
${GNUTLS_LIBRARIES}
|
||||
)
|
||||
endif ()
|
||||
|
||||
# SSE2
|
||||
|
||||
set(SSE2_SOURCES
|
||||
scale_sse2.cxx)
|
||||
scale_sse2.cxx)
|
||||
|
||||
set(SCALE_DUMMY_SOURCES
|
||||
scale_dummy.cxx)
|
||||
scale_dummy.cxx)
|
||||
|
||||
if(COMPILER_SUPPORTS_SSE2)
|
||||
set_source_files_properties(${SSE2_SOURCES} PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -msse2)
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
${SSE2_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
${SCALE_DUMMY_SOURCES}
|
||||
)
|
||||
endif()
|
||||
if (COMPILER_SUPPORTS_SSE2)
|
||||
set_source_files_properties(${SSE2_SOURCES} PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -msse2)
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
${SSE2_SOURCES}
|
||||
)
|
||||
else ()
|
||||
set(RFB_SOURCES
|
||||
${RFB_SOURCES}
|
||||
${SCALE_DUMMY_SOURCES}
|
||||
)
|
||||
endif ()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil libswscale)
|
||||
|
||||
add_library(rfb STATIC ${RFB_SOURCES})
|
||||
|
||||
target_link_libraries(rfb ${RFB_LIBRARIES})
|
||||
target_include_directories(rfb PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/common
|
||||
${JPEG_INCLUDE_DIR}
|
||||
${PNG_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd
|
||||
${CMAKE_SOURCE_DIR}/third_party/tinyxml2
|
||||
${FFMPEG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
libtool_create_control_file(rfb)
|
||||
endif()
|
||||
target_link_libraries(rfb PRIVATE ${RFB_LIBRARIES} tinyxml2_objs)
|
||||
|
||||
if (UNIX)
|
||||
libtool_create_control_file(rfb)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -360,7 +360,6 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
|||
int nRects;
|
||||
Region changed, cursorRegion;
|
||||
struct timeval start;
|
||||
unsigned screenArea;
|
||||
|
||||
updates++;
|
||||
if (conn->cp.supportsUdp)
|
||||
|
|
@ -390,16 +389,7 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
|||
memset(&webpstats, 0, sizeof(codecstats_t));
|
||||
|
||||
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
|
||||
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
|
||||
|
||||
screenArea = pb->getRect().width() * pb->getRect().height();
|
||||
screenArea *= 1024;
|
||||
screenArea /= 256 * 256;
|
||||
screenArea *= webpBenchResult;
|
||||
// Encoding the entire screen would take this many 1024*msecs, worst case
|
||||
|
||||
// Calculate how many us we can send webp for, before switching to jpeg
|
||||
webpFallbackUs = rate * rate / screenArea;
|
||||
webpFallbackUs = (1000 * 1000 / rfb::Server::frameRate) * (static_cast<double>(Server::webpEncodingTime) / 100.0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -883,7 +873,7 @@ void EncodeManager::findSolidRect(const Rect& rect, Region *changed,
|
|||
void EncodeManager::checkWebpFallback(const timeval *start) {
|
||||
// Have we taken too long for the frame? If so, drop from WEBP to JPEG
|
||||
if (start && activeEncoders[encoderFullColour] == encoderTightWEBP && !webpTookTooLong.load(std::memory_order_relaxed)) {
|
||||
const auto us = msSince(start) * 1024;
|
||||
const auto us = msSince(start) * 1000;
|
||||
if (us > webpFallbackUs)
|
||||
webpTookTooLong.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@
|
|||
#include <string.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/Security.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/SMsgReader.h>
|
||||
#include <rfb/SMsgWriter.h>
|
||||
|
|
@ -274,19 +272,16 @@ void SConnection::writeConnFailedFromScratch(const char* msg,
|
|||
os->flush();
|
||||
}
|
||||
|
||||
void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
{
|
||||
int i;
|
||||
|
||||
preferredEncoding = encodingRaw;
|
||||
for (i = 0;i < nEncodings;i++) {
|
||||
if (EncodeManager::supported(encodings[i])) {
|
||||
preferredEncoding = encodings[i];
|
||||
break;
|
||||
void SConnection::setEncodings(int nEncodings, const rdr::S32 *encodings) {
|
||||
preferredEncoding = encodingRaw;
|
||||
for (int i = 0; i < nEncodings; i++) {
|
||||
if (EncodeManager::supported(encodings[i])) {
|
||||
preferredEncoding = encodings[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SMsgHandler::setEncodings(nEncodings, encodings);
|
||||
SMsgHandler::setEncodings(nEncodings, encodings);
|
||||
}
|
||||
|
||||
void SConnection::clearBinaryClipboard()
|
||||
|
|
|
|||
|
|
@ -26,27 +26,32 @@
|
|||
#include <rfb/TightWEBPEncoder.h>
|
||||
#include <rfb/util.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
using namespace rfb;
|
||||
static LogWriter vlog("SelfBench");
|
||||
|
||||
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
|
||||
|
||||
#define RUNS 64
|
||||
static constexpr uint32_t RUNS = 64;
|
||||
|
||||
#define W 1600
|
||||
#define H 1200
|
||||
static constexpr uint32_t WIDTH = 1600;
|
||||
static constexpr uint32_t HEIGHT = 1200;
|
||||
|
||||
void SelfBench() {
|
||||
tinyxml2::XMLDocument doc;
|
||||
|
||||
unsigned i, runs;
|
||||
struct timeval start;
|
||||
auto *test_suit = doc.NewElement("testsuite");
|
||||
test_suit->SetAttribute("name", "SelfBench");
|
||||
|
||||
ManagedPixelBuffer f1(pfRGBX, W, H);
|
||||
ManagedPixelBuffer f2(pfRGBX, W, H);
|
||||
ManagedPixelBuffer screen(pfRGBX, W, H);
|
||||
doc.InsertFirstChild(test_suit);
|
||||
|
||||
ManagedPixelBuffer f1(pfRGBX, WIDTH, HEIGHT);
|
||||
ManagedPixelBuffer f2(pfRGBX, WIDTH, HEIGHT);
|
||||
ManagedPixelBuffer screen(pfRGBX, WIDTH, HEIGHT);
|
||||
|
||||
int stride;
|
||||
rdr::U8 *f1ptr = f1.getBufferRW(f1.getRect(), &stride);
|
||||
|
|
@ -56,7 +61,7 @@ void SelfBench() {
|
|||
rdr::U8 * const f1orig = f1ptr;
|
||||
rdr::U8 * const f2orig = f2ptr;
|
||||
|
||||
for (i = 0; i < W * H * 4; i += 4) {
|
||||
for (uint32_t i = 0; i < WIDTH * HEIGHT * 4; i += 4) {
|
||||
f1ptr[0] = rand();
|
||||
f1ptr[1] = rand();
|
||||
f1ptr[2] = rand();
|
||||
|
|
@ -74,124 +79,116 @@ void SelfBench() {
|
|||
// Encoding
|
||||
std::vector<uint8_t> vec;
|
||||
|
||||
TightJPEGEncoder jpeg(NULL);
|
||||
TightJPEGEncoder jpeg(nullptr);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
uint32_t test_cases {};
|
||||
uint64_t total_time {};
|
||||
|
||||
auto benchmark = [&doc, &test_suit, &test_cases, &total_time](const char *name, uint32_t runs, auto func) {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
for (uint32_t i = 0; i < runs; i++) {
|
||||
func(i);
|
||||
}
|
||||
|
||||
++test_cases;
|
||||
auto value = elapsedMs(now);
|
||||
double junit_value = value / 1000.;
|
||||
total_time += value;
|
||||
|
||||
vlog.info("%s took %lu ms (%u runs)", name, value, runs);
|
||||
auto *test_case = doc.NewElement("testcase");
|
||||
test_case->SetAttribute("name", name);
|
||||
test_case->SetAttribute("time", junit_value);
|
||||
test_case->SetAttribute("runs", runs);
|
||||
test_case->SetAttribute("classname", "KasmVNC");
|
||||
test_suit->InsertEndChild(test_case);
|
||||
};
|
||||
|
||||
benchmark("Jpeg compression at quality 8", RUNS, [&jpeg, &vec, &f1](uint32_t) {
|
||||
jpeg.compressOnly(&f1, 8, vec, false);
|
||||
}
|
||||
vlog.info("Jpeg compression at quality 8 took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
benchmark("Jpeg compression at quality 4", RUNS, [&jpeg, &vec, &f1](uint32_t) {
|
||||
jpeg.compressOnly(&f1, 4, vec, false);
|
||||
}
|
||||
vlog.info("Jpeg compression at quality 4 took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
TightWEBPEncoder webp(nullptr);
|
||||
|
||||
TightWEBPEncoder webp(NULL);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS / 8;
|
||||
for (i = 0; i < runs; i++) {
|
||||
benchmark("Webp compression at quality 8", RUNS / 8, [&webp,&f1, &vec](uint32_t) {
|
||||
webp.compressOnly(&f1, 8, vec, false);
|
||||
}
|
||||
vlog.info("Webp compression at quality 8 took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS / 4;
|
||||
for (i = 0; i < runs; i++) {
|
||||
benchmark("Webp compression at quality 4", RUNS / 4, [&webp, &f1, &vec](uint32_t) {
|
||||
webp.compressOnly(&f1, 4, vec, false);
|
||||
}
|
||||
vlog.info("Webp compression at quality 4 took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
// Scaling
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
PixelBuffer *pb = nearestScale(&f1, W * 0.8, H * 0.8, 0.8);
|
||||
benchmark("Nearest scaling to 80%", RUNS, [&f1](uint32_t) {
|
||||
PixelBuffer *pb = nearestScale(&f1, WIDTH * 0.8, HEIGHT * 0.8, 0.8);
|
||||
delete pb;
|
||||
}
|
||||
vlog.info("Nearest scaling to 80%% took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
PixelBuffer *pb = nearestScale(&f1, W * 0.4, H * 0.4, 0.4);
|
||||
benchmark("Nearest scaling to 40%", RUNS, [&f1](uint32_t) {
|
||||
PixelBuffer *pb = nearestScale(&f1, WIDTH * 0.4, HEIGHT * 0.4, 0.4);
|
||||
delete pb;
|
||||
}
|
||||
vlog.info("Nearest scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
PixelBuffer *pb = bilinearScale(&f1, W * 0.8, H * 0.8, 0.8);
|
||||
benchmark("Bilinear scaling to 80%", RUNS, [&f1](uint32_t) {
|
||||
PixelBuffer *pb = bilinearScale(&f1, WIDTH * 0.8, HEIGHT * 0.8, 0.8);
|
||||
delete pb;
|
||||
}
|
||||
vlog.info("Bilinear scaling to 80%% took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
PixelBuffer *pb = bilinearScale(&f1, W * 0.4, H * 0.4, 0.4);
|
||||
benchmark("Bilinear scaling to 40%", RUNS, [&f1](uint32_t) {
|
||||
PixelBuffer *pb = bilinearScale(&f1, WIDTH * 0.4, HEIGHT * 0.4, 0.4);
|
||||
delete pb;
|
||||
}
|
||||
vlog.info("Bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
PixelBuffer *pb = progressiveBilinearScale(&f1, W * 0.8, H * 0.8, 0.8);
|
||||
delete pb;
|
||||
}
|
||||
vlog.info("Progressive bilinear scaling to 80%% took %u ms (%u runs)", msSince(&start), runs);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
PixelBuffer *pb = progressiveBilinearScale(&f1, W * 0.4, H * 0.4, 0.4);
|
||||
benchmark("Progressive bilinear scaling to 80%", RUNS, [&f1](uint32_t) {
|
||||
PixelBuffer *pb = progressiveBilinearScale(&f1, WIDTH * 0.8, HEIGHT * 0.8, 0.8);
|
||||
delete pb;
|
||||
}
|
||||
vlog.info("Progressive bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
|
||||
});
|
||||
benchmark("Progressive bilinear scaling to 40%", RUNS, [&f1](uint32_t) {
|
||||
PixelBuffer *pb = progressiveBilinearScale(&f1, WIDTH * 0.4, HEIGHT * 0.4, 0.4);
|
||||
delete pb;
|
||||
});
|
||||
|
||||
// Analysis
|
||||
ComparingUpdateTracker *comparer = new ComparingUpdateTracker(&screen);
|
||||
auto *comparer = new ComparingUpdateTracker(&screen);
|
||||
Region cursorReg;
|
||||
|
||||
Server::detectScrolling.setParam(false);
|
||||
Server::detectHorizontal.setParam(false);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4);
|
||||
comparer->compare(true, cursorReg);
|
||||
}
|
||||
vlog.info("Analysis took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs);
|
||||
benchmark("Analysis (incl. memcpy overhead)", RUNS,
|
||||
[&screenptr, &comparer, &cursorReg, f1orig, f2orig](uint32_t i) {
|
||||
memcpy(screenptr, i % 2 ? f1orig : f2orig, WIDTH * HEIGHT * 4);
|
||||
comparer->compare(true, cursorReg);
|
||||
});
|
||||
|
||||
Server::detectScrolling.setParam(true);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS;
|
||||
for (i = 0; i < runs; i++) {
|
||||
memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4);
|
||||
comparer->compare(false, cursorReg);
|
||||
}
|
||||
vlog.info("Analysis w/ scroll detection took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs);
|
||||
benchmark("Analysis w/ scroll detection (incl. memcpy overhead)", RUNS,
|
||||
[&screenptr, &comparer, &cursorReg, f1orig, f2orig](uint32_t i) {
|
||||
memcpy(screenptr, i % 2 ? f1orig : f2orig, WIDTH * HEIGHT * 4);
|
||||
comparer->compare(false, cursorReg);
|
||||
});
|
||||
|
||||
Server::detectHorizontal.setParam(true);
|
||||
delete comparer;
|
||||
comparer = new ComparingUpdateTracker(&screen);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
runs = RUNS / 2;
|
||||
for (i = 0; i < runs; i++) {
|
||||
memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4);
|
||||
comparer->compare(false, cursorReg);
|
||||
}
|
||||
vlog.info("Analysis w/ horizontal scroll detection took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs);
|
||||
benchmark("Analysis w/ horizontal scroll detection (incl. memcpy overhead)", RUNS / 2,
|
||||
[&screenptr, &comparer, &cursorReg, f1orig, f2orig](uint32_t i) {
|
||||
memcpy(screenptr, i % 2 ? f1orig : f2orig, WIDTH * HEIGHT * 4);
|
||||
comparer->compare(false, cursorReg);
|
||||
});
|
||||
|
||||
test_suit->SetAttribute("tests", test_cases);
|
||||
test_suit->SetAttribute("failures", 0);
|
||||
test_suit->SetAttribute("time", total_time);
|
||||
|
||||
doc.SaveFile("SelfBench.xml");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,15 +32,15 @@ rfb::IntParameter rfb::Server::idleTimeout
|
|||
0, 0);
|
||||
rfb::IntParameter rfb::Server::maxDisconnectionTime
|
||||
("MaxDisconnectionTime",
|
||||
"Terminate when no client has been connected for s seconds",
|
||||
"Terminate when no client has been connected for s seconds",
|
||||
0, 0);
|
||||
rfb::IntParameter rfb::Server::maxConnectionTime
|
||||
("MaxConnectionTime",
|
||||
"Terminate when a client has been connected for s seconds",
|
||||
"Terminate when a client has been connected for s seconds",
|
||||
0, 0);
|
||||
rfb::IntParameter rfb::Server::maxIdleTime
|
||||
("MaxIdleTime",
|
||||
"Terminate after s seconds of user inactivity",
|
||||
"Terminate after s seconds of user inactivity",
|
||||
0, 0);
|
||||
rfb::IntParameter rfb::Server::clientWaitTimeMillis
|
||||
("ClientWaitTimeMillis",
|
||||
|
|
@ -117,6 +117,16 @@ rfb::BoolParameter rfb::Server::selfBench
|
|||
("SelfBench",
|
||||
"Run self-benchmarks and exit.",
|
||||
false);
|
||||
rfb::StringParameter rfb::Server::benchmark(
|
||||
"Benchmark",
|
||||
"Run extended benchmarks and exit.",
|
||||
"");
|
||||
|
||||
rfb::StringParameter rfb::Server::benchmarkResults(
|
||||
"BenchmarkResults",
|
||||
"The file to save becnhmark results to.",
|
||||
"Benchmark.xml");
|
||||
|
||||
rfb::IntParameter rfb::Server::dynamicQualityMin
|
||||
("DynamicQualityMin",
|
||||
"The minimum dynamic JPEG quality, 0 = low, 9 = high",
|
||||
|
|
@ -271,19 +281,24 @@ rfb::IntParameter rfb::Server::udpFullFrameFrequency
|
|||
("udpFullFrameFrequency",
|
||||
"Send a full frame every N frames for clients using UDP. 0 to disable",
|
||||
0, 0, 1000);
|
||||
|
||||
|
||||
rfb::IntParameter rfb::Server::udpPort
|
||||
("udpPort",
|
||||
"Which port to use for UDP. Default same as websocket",
|
||||
0, 0, 65535);
|
||||
|
||||
static void bandwidthPreset() {
|
||||
rfb::Server::dynamicQualityMin.setParam(2);
|
||||
rfb::Server::dynamicQualityMax.setParam(9);
|
||||
rfb::Server::treatLossless.setParam(8);
|
||||
rfb::Server::dynamicQualityMin.setParam(2);
|
||||
rfb::Server::dynamicQualityMax.setParam(9);
|
||||
rfb::Server::treatLossless.setParam(8);
|
||||
}
|
||||
|
||||
rfb::PresetParameter rfb::Server::preferBandwidth
|
||||
("PreferBandwidth",
|
||||
"Set various options for lower bandwidth use. The default is off, aka to prefer quality.",
|
||||
false, bandwidthPreset);
|
||||
|
||||
rfb::IntParameter rfb::Server::webpEncodingTime
|
||||
("webpEncodingTime",
|
||||
"Percentage of time allotted for encoding a frame, that can be used for encoding rects in webp.",
|
||||
30, 0, 100);
|
||||
|
|
|
|||
|
|
@ -28,72 +28,71 @@
|
|||
#include <rfb/util.h>
|
||||
|
||||
namespace rfb {
|
||||
|
||||
class Server {
|
||||
public:
|
||||
|
||||
static IntParameter idleTimeout;
|
||||
static IntParameter maxDisconnectionTime;
|
||||
static IntParameter maxConnectionTime;
|
||||
static IntParameter maxIdleTime;
|
||||
static IntParameter clientWaitTimeMillis;
|
||||
static IntParameter compareFB;
|
||||
static IntParameter frameRate;
|
||||
static IntParameter dynamicQualityMin;
|
||||
static IntParameter dynamicQualityMax;
|
||||
static IntParameter treatLossless;
|
||||
static IntParameter scrollDetectLimit;
|
||||
static IntParameter rectThreads;
|
||||
static IntParameter DLP_ClipSendMax;
|
||||
static IntParameter DLP_ClipAcceptMax;
|
||||
static IntParameter DLP_ClipDelay;
|
||||
static IntParameter DLP_KeyRateLimit;
|
||||
static IntParameter DLP_WatermarkRepeatSpace;
|
||||
static IntParameter DLP_WatermarkFontSize;
|
||||
static IntParameter DLP_WatermarkTimeOffset;
|
||||
static IntParameter DLP_WatermarkTimeOffsetMinutes;
|
||||
static IntParameter DLP_WatermarkTextAngle;
|
||||
static StringParameter DLP_ClipLog;
|
||||
static StringParameter DLP_Region;
|
||||
static StringParameter DLP_Clip_Types;
|
||||
static StringParameter DLP_WatermarkImage;
|
||||
static StringParameter DLP_WatermarkLocation;
|
||||
static StringParameter DLP_WatermarkTint;
|
||||
static StringParameter DLP_WatermarkText;
|
||||
static StringParameter DLP_WatermarkFont;
|
||||
static BoolParameter DLP_RegionAllowClick;
|
||||
static BoolParameter DLP_RegionAllowRelease;
|
||||
static IntParameter jpegVideoQuality;
|
||||
static IntParameter webpVideoQuality;
|
||||
static StringParameter maxVideoResolution;
|
||||
static IntParameter videoTime;
|
||||
static IntParameter videoOutTime;
|
||||
static IntParameter videoArea;
|
||||
static IntParameter videoScaling;
|
||||
static IntParameter udpFullFrameFrequency;
|
||||
static IntParameter udpPort;
|
||||
static StringParameter kasmPasswordFile;
|
||||
static StringParameter publicIP;
|
||||
static StringParameter stunServer;
|
||||
static BoolParameter printVideoArea;
|
||||
static BoolParameter protocol3_3;
|
||||
static BoolParameter alwaysShared;
|
||||
static BoolParameter neverShared;
|
||||
static BoolParameter disconnectClients;
|
||||
static BoolParameter acceptKeyEvents;
|
||||
static BoolParameter acceptPointerEvents;
|
||||
static BoolParameter acceptCutText;
|
||||
static BoolParameter sendCutText;
|
||||
static BoolParameter acceptSetDesktopSize;
|
||||
static BoolParameter queryConnect;
|
||||
static BoolParameter detectScrolling;
|
||||
static BoolParameter detectHorizontal;
|
||||
static BoolParameter ignoreClientSettingsKasm;
|
||||
static BoolParameter selfBench;
|
||||
static PresetParameter preferBandwidth;
|
||||
};
|
||||
|
||||
class Server {
|
||||
public:
|
||||
static IntParameter idleTimeout;
|
||||
static IntParameter maxDisconnectionTime;
|
||||
static IntParameter maxConnectionTime;
|
||||
static IntParameter maxIdleTime;
|
||||
static IntParameter clientWaitTimeMillis;
|
||||
static IntParameter compareFB;
|
||||
static IntParameter frameRate;
|
||||
static IntParameter dynamicQualityMin;
|
||||
static IntParameter dynamicQualityMax;
|
||||
static IntParameter treatLossless;
|
||||
static IntParameter scrollDetectLimit;
|
||||
static IntParameter rectThreads;
|
||||
static IntParameter DLP_ClipSendMax;
|
||||
static IntParameter DLP_ClipAcceptMax;
|
||||
static IntParameter DLP_ClipDelay;
|
||||
static IntParameter DLP_KeyRateLimit;
|
||||
static IntParameter DLP_WatermarkRepeatSpace;
|
||||
static IntParameter DLP_WatermarkFontSize;
|
||||
static IntParameter DLP_WatermarkTimeOffset;
|
||||
static IntParameter DLP_WatermarkTimeOffsetMinutes;
|
||||
static IntParameter DLP_WatermarkTextAngle;
|
||||
static StringParameter DLP_ClipLog;
|
||||
static StringParameter DLP_Region;
|
||||
static StringParameter DLP_Clip_Types;
|
||||
static StringParameter DLP_WatermarkImage;
|
||||
static StringParameter DLP_WatermarkLocation;
|
||||
static StringParameter DLP_WatermarkTint;
|
||||
static StringParameter DLP_WatermarkText;
|
||||
static StringParameter DLP_WatermarkFont;
|
||||
static BoolParameter DLP_RegionAllowClick;
|
||||
static BoolParameter DLP_RegionAllowRelease;
|
||||
static IntParameter jpegVideoQuality;
|
||||
static IntParameter webpVideoQuality;
|
||||
static StringParameter maxVideoResolution;
|
||||
static IntParameter videoTime;
|
||||
static IntParameter videoOutTime;
|
||||
static IntParameter videoArea;
|
||||
static IntParameter videoScaling;
|
||||
static IntParameter udpFullFrameFrequency;
|
||||
static IntParameter udpPort;
|
||||
static StringParameter kasmPasswordFile;
|
||||
static StringParameter publicIP;
|
||||
static StringParameter stunServer;
|
||||
static BoolParameter printVideoArea;
|
||||
static BoolParameter protocol3_3;
|
||||
static BoolParameter alwaysShared;
|
||||
static BoolParameter neverShared;
|
||||
static BoolParameter disconnectClients;
|
||||
static BoolParameter acceptKeyEvents;
|
||||
static BoolParameter acceptPointerEvents;
|
||||
static BoolParameter acceptCutText;
|
||||
static BoolParameter sendCutText;
|
||||
static BoolParameter acceptSetDesktopSize;
|
||||
static BoolParameter queryConnect;
|
||||
static BoolParameter detectScrolling;
|
||||
static BoolParameter detectHorizontal;
|
||||
static BoolParameter ignoreClientSettingsKasm;
|
||||
static BoolParameter selfBench;
|
||||
static StringParameter benchmark;
|
||||
static StringParameter benchmarkResults;
|
||||
static PresetParameter preferBandwidth;
|
||||
static IntParameter webpEncodingTime;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // __RFB_SERVER_CORE_H__
|
||||
|
||||
#endif // __RFB_SERVER_CORE_H__
|
||||
|
|
@ -25,6 +25,12 @@
|
|||
#include <rfb/TightJPEGEncoder.h>
|
||||
#include <rfb/TightConstants.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
struct TightJPEGConfiguration {
|
||||
|
|
|
|||
|
|
@ -259,13 +259,14 @@ void TightWEBPEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
|
|||
WebPMemoryWriterClear(&wrt);
|
||||
}
|
||||
|
||||
// How many milliseconds would it take to encode a 256x256 block at quality 8
|
||||
// How many milliseconds would it take to encode a 256x256 block at quality 5
|
||||
rdr::U32 TightWEBPEncoder::benchmark() const
|
||||
{
|
||||
rdr::U8* buffer;
|
||||
struct timeval start;
|
||||
int stride, i;
|
||||
const uint8_t quality = 8, method = 2;
|
||||
// the minimum WebP quality settings used in KasmVNC
|
||||
const uint8_t quality = 5, method = 0;
|
||||
WebPConfig cfg;
|
||||
WebPPicture pic;
|
||||
WebPMemoryWriter wrt;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||
losslessTimer(this), kbdLogTimer(this), binclipTimer(this),
|
||||
server(server_), updates(false),
|
||||
updateRenderedCursor(false), removeRenderedCursor(false),
|
||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||
continuousUpdates(false), encodeManager(this, &VNCServerST::encCache),
|
||||
needsPermCheck(false), pointerEventTime(0),
|
||||
clientHasCursor(false),
|
||||
accessRights(AccessDefault), startTime(time(0)), frameTracking(false),
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@
|
|||
// otherwise blacklisted connections might be "forgotten".
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <network/GetAPI.h>
|
||||
#include <network/Udp.h>
|
||||
|
|
@ -73,6 +73,8 @@
|
|||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#include <wordexp.h>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
|
|
@ -82,6 +84,8 @@ EncCache VNCServerST::encCache;
|
|||
|
||||
void SelfBench();
|
||||
|
||||
void benchmark(std::string_view, std::string_view);
|
||||
|
||||
//
|
||||
// -=- VNCServerST Implementation
|
||||
//
|
||||
|
|
@ -128,13 +132,13 @@ static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest,
|
|||
|
||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
||||
blockCounter(0), pb(0), blackedpb(0), ledState(ledUnknown),
|
||||
name(strDup(name_)), pointerClient(0), clipboardClient(0),
|
||||
comparer(0), cursor(new Cursor(0, 0, Point(), NULL)),
|
||||
blockCounter(0), pb(nullptr), blackedpb(nullptr), ledState(ledUnknown),
|
||||
name(strDup(name_)), pointerClient(nullptr), clipboardClient(nullptr),
|
||||
comparer(nullptr), cursor(new Cursor(0, 0, Point(), nullptr)),
|
||||
renderedCursorInvalid(false),
|
||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||
queryConnectionHandler(nullptr), keyRemapper(&KeyRemapper::defInstance),
|
||||
lastConnectionTime(0), disableclients(false),
|
||||
frameTimer(this), apimessager(NULL), trackingFrameStats(0),
|
||||
frameTimer(this), apimessager(nullptr), trackingFrameStats(0),
|
||||
clipboardId(0), sendWatermark(false)
|
||||
{
|
||||
lastUserInputTime = lastDisconnectTime = time(0);
|
||||
|
|
@ -198,7 +202,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
|||
}
|
||||
}
|
||||
|
||||
DLPRegion.enabled = 1;
|
||||
DLPRegion.enabled = true;
|
||||
}
|
||||
|
||||
kasmpasswdpath[0] = '\0';
|
||||
|
|
@ -223,11 +227,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[0]) {
|
||||
auto *file_name = Server::benchmark.getValueStr();
|
||||
if (!std::filesystem::exists(file_name))
|
||||
throw Exception("Benchmarking video file does not exist");
|
||||
benchmark(file_name, Server::benchmarkResults.getValueStr());
|
||||
}
|
||||
}
|
||||
|
||||
VNCServerST::~VNCServerST()
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ namespace rfb {
|
|||
// the connection.
|
||||
enum queryResult { ACCEPT, REJECT, PENDING };
|
||||
struct QueryConnectionHandler {
|
||||
virtual ~QueryConnectionHandler() {}
|
||||
virtual ~QueryConnectionHandler() = default;
|
||||
virtual queryResult queryConnection(network::Socket* sock,
|
||||
const char* userName,
|
||||
char** reason) = 0;
|
||||
|
|
|
|||
396
common/rfb/benchmark.cxx
Normal file
396
common/rfb/benchmark.cxx
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
/* Copyright 2015 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||
* Copyright (C) 2015 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2025 Kasm Technologies Corp
|
||||
*
|
||||
* 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_view>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <numeric>
|
||||
#include <tinyxml2.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "ServerCore.h"
|
||||
#include <cmath>
|
||||
|
||||
#include "EncCache.h"
|
||||
#include "EncodeManager.h"
|
||||
#include "SConnection.h"
|
||||
#include "screenTypes.h"
|
||||
#include "SMsgWriter.h"
|
||||
#include "UpdateTracker.h"
|
||||
#include "rdr/BufferedInStream.h"
|
||||
#include "rdr/OutStream.h"
|
||||
#include "ffmpeg.h"
|
||||
|
||||
namespace benchmarking {
|
||||
class MockBufferStream final : public rdr::BufferedInStream {
|
||||
bool fillBuffer(size_t maxSize, bool wait) override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MockStream final : public rdr::OutStream {
|
||||
public:
|
||||
MockStream() {
|
||||
offset = 0;
|
||||
ptr = buf;
|
||||
end = buf + sizeof(buf);
|
||||
}
|
||||
|
||||
private:
|
||||
void overrun(size_t needed) override {
|
||||
assert(end >= ptr);
|
||||
if (needed > static_cast<size_t>(end - ptr))
|
||||
flush();
|
||||
}
|
||||
|
||||
public:
|
||||
size_t length() override {
|
||||
flush();
|
||||
return offset;
|
||||
}
|
||||
|
||||
void flush() override {
|
||||
offset += ptr - buf;
|
||||
ptr = buf;
|
||||
}
|
||||
|
||||
private:
|
||||
ptrdiff_t offset;
|
||||
rdr::U8 buf[8192]{};
|
||||
};
|
||||
|
||||
class MockSConnection final : public rfb::SConnection {
|
||||
public:
|
||||
MockSConnection() {
|
||||
setStreams(nullptr, &out);
|
||||
|
||||
setWriter(new rfb::SMsgWriter(&cp, &out, &udps));
|
||||
}
|
||||
|
||||
~MockSConnection() override = default;
|
||||
|
||||
void writeUpdate(const rfb::UpdateInfo &ui, const rfb::PixelBuffer *pb) {
|
||||
cache.clear();
|
||||
|
||||
manager.clearEncodingTime();
|
||||
if (!ui.is_empty()) {
|
||||
manager.writeUpdate(ui, pb, nullptr);
|
||||
} else {
|
||||
rfb::Region region{pb->getRect()};
|
||||
manager.writeLosslessRefresh(region, pb, nullptr, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
void setDesktopSize(int fb_width, int fb_height,
|
||||
const rfb::ScreenSet &layout) override {
|
||||
cp.width = fb_width;
|
||||
cp.height = fb_height;
|
||||
cp.screenLayout = layout;
|
||||
|
||||
writer()->writeExtendedDesktopSize(rfb::reasonServer, 0, cp.width, cp.height,
|
||||
cp.screenLayout);
|
||||
}
|
||||
|
||||
void sendStats(const bool toClient) override {
|
||||
}
|
||||
|
||||
[[nodiscard]] bool canChangeKasmSettings() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void udpUpgrade(const char *resp) override {
|
||||
}
|
||||
|
||||
void udpDowngrade(const bool) override {
|
||||
}
|
||||
|
||||
void subscribeUnixRelay(const char *name) override {
|
||||
}
|
||||
|
||||
void unixRelay(const char *name, const rdr::U8 *buf, const unsigned len) override {
|
||||
}
|
||||
|
||||
void handleFrameStats(rdr::U32 all, rdr::U32 render) override {
|
||||
}
|
||||
|
||||
[[nodiscard]] auto getJpegStats() const {
|
||||
return manager.jpegstats;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto getWebPStats() const {
|
||||
return manager.webpstats;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto bytes() { return out.length(); }
|
||||
[[nodiscard]] auto udp_bytes() { return udps.length(); }
|
||||
|
||||
protected:
|
||||
MockStream out{};
|
||||
MockStream udps{};
|
||||
|
||||
EncCache cache{};
|
||||
EncodeManager manager{this, &cache};
|
||||
};
|
||||
|
||||
class MockCConnection final : public MockTestConnection {
|
||||
public:
|
||||
explicit MockCConnection(const std::vector<rdr::S32> &encodings, rfb::ManagedPixelBuffer *pb) {
|
||||
setStreams(&in, nullptr);
|
||||
|
||||
// Need to skip the initial handshake and ServerInit
|
||||
setState(RFBSTATE_NORMAL);
|
||||
// That also means that the reader and writer weren't set up
|
||||
setReader(new rfb::CMsgReader(this, &in));
|
||||
auto &pf = pb->getPF();
|
||||
CMsgHandler::setPixelFormat(pf);
|
||||
|
||||
MockCConnection::setDesktopSize(pb->width(), pb->height());
|
||||
|
||||
cp.setPF(pf);
|
||||
|
||||
sc.cp.setPF(pf);
|
||||
sc.setEncodings(std::size(encodings), encodings.data());
|
||||
|
||||
setFramebuffer(pb);
|
||||
}
|
||||
|
||||
void setCursor(int width, int height, const rfb::Point &hotspot, const rdr::U8 *data,
|
||||
const bool resizing) override {
|
||||
}
|
||||
|
||||
~MockCConnection() override = default;
|
||||
|
||||
struct stats_t {
|
||||
EncodeManager::codecstats_t jpeg_stats;
|
||||
EncodeManager::codecstats_t webp_stats;
|
||||
uint64_t bytes;
|
||||
uint64_t udp_bytes;
|
||||
};
|
||||
|
||||
[[nodiscard]] stats_t getStats() {
|
||||
return {
|
||||
sc.getJpegStats(),
|
||||
sc.getWebPStats(),
|
||||
sc.bytes(),
|
||||
sc.udp_bytes()
|
||||
};
|
||||
}
|
||||
|
||||
void setDesktopSize(int w, int h) override {
|
||||
CConnection::setDesktopSize(w, h);
|
||||
|
||||
if (screen_layout.num_screens())
|
||||
screen_layout.remove_screen(0);
|
||||
|
||||
screen_layout.add_screen(rfb::Screen(0, 0, 0, w, h, 0));
|
||||
}
|
||||
|
||||
void setNewFrame(const AVFrame *frame) override {
|
||||
auto *pb = getFramebuffer();
|
||||
const int width = pb->width();
|
||||
const int height = pb->height();
|
||||
const rfb::Rect rect(0, 0, width, height);
|
||||
|
||||
int dstStride{};
|
||||
auto *buffer = pb->getBufferRW(rect, &dstStride);
|
||||
|
||||
const rfb::PixelFormat &pf = pb->getPF();
|
||||
|
||||
// Source data and stride from FFmpeg
|
||||
const auto *srcData = frame->data[0];
|
||||
const int srcStride = frame->linesize[0] / 3; // Convert bytes to pixels
|
||||
|
||||
// Convert from the RGB format to the PixelBuffer's format
|
||||
pf.bufferFromRGB(buffer, srcData, width, srcStride, height);
|
||||
|
||||
// Commit changes
|
||||
pb->commitBufferRW(rect);
|
||||
}
|
||||
|
||||
void framebufferUpdateStart() override {
|
||||
updates.clear();
|
||||
}
|
||||
|
||||
void framebufferUpdateEnd() override {
|
||||
const rfb::PixelBuffer *pb = getFramebuffer();
|
||||
|
||||
rfb::UpdateInfo ui;
|
||||
const rfb::Region clip(pb->getRect());
|
||||
|
||||
updates.add_changed(pb->getRect());
|
||||
|
||||
updates.getUpdateInfo(&ui, clip);
|
||||
sc.writeUpdate(ui, pb);
|
||||
}
|
||||
|
||||
void dataRect(const rfb::Rect &r, int encoding) override {
|
||||
}
|
||||
|
||||
void setColourMapEntries(int, int, rdr::U16 *) override {
|
||||
}
|
||||
|
||||
void bell() override {
|
||||
}
|
||||
|
||||
void serverCutText(const char *, rdr::U32) override {
|
||||
}
|
||||
|
||||
void serverCutText(const char *str) override {
|
||||
}
|
||||
|
||||
protected:
|
||||
MockBufferStream in;
|
||||
rfb::ScreenSet screen_layout;
|
||||
rfb::SimpleUpdateTracker updates;
|
||||
MockSConnection sc;
|
||||
};
|
||||
}
|
||||
|
||||
void report(std::vector<uint64_t> &totals, std::vector<uint64_t> &timings,
|
||||
std::vector<benchmarking::MockCConnection::stats_t> &stats, const std::string_view results_file) {
|
||||
auto totals_sum = std::accumulate(totals.begin(), totals.end(), 0.);
|
||||
auto totals_avg = totals_sum / static_cast<double>(totals.size());
|
||||
|
||||
auto variance = 0.;
|
||||
for (auto t: totals)
|
||||
variance += (static_cast<double>(t) - totals_avg) * (static_cast<double>(t) - totals_avg);
|
||||
|
||||
variance /= static_cast<double>(totals.size());
|
||||
auto stddev = std::sqrt(variance);
|
||||
|
||||
const auto sum = std::accumulate(timings.begin(), timings.end(), 0.);
|
||||
const auto size = timings.size();
|
||||
const auto average = sum / static_cast<double>(size);
|
||||
|
||||
double median{};
|
||||
|
||||
std::sort(timings.begin(), timings.end());
|
||||
if (size % 2 == 0)
|
||||
median = static_cast<double>(timings[size / 2]);
|
||||
else
|
||||
median = static_cast<double>(timings[size / 2 - 1] + timings[size / 2]) / 2.;
|
||||
|
||||
vlog.info("Mean time encoding frame: %f ms", average);
|
||||
vlog.info("Median time encoding frame: %f ms", median);
|
||||
vlog.info("Total time (mean): %f ms", totals_avg);
|
||||
vlog.info("Total time (stddev): %f ms", stddev);
|
||||
|
||||
uint32_t jpeg_sum{}, jpeg_rects{}, webp_sum{}, webp_rects{};
|
||||
uint64_t bytes{};
|
||||
|
||||
for (const auto &item: stats) {
|
||||
jpeg_sum += item.jpeg_stats.ms;
|
||||
jpeg_rects += item.jpeg_stats.rects;
|
||||
webp_sum += item.webp_stats.ms;
|
||||
webp_rects += item.webp_stats.rects;
|
||||
bytes += item.bytes;
|
||||
}
|
||||
|
||||
auto jpeg_ms = jpeg_sum / static_cast<double>(stats.size());
|
||||
vlog.info("JPEG stats: %f ms", jpeg_ms);
|
||||
jpeg_rects /= stats.size();
|
||||
vlog.info("JPEG stats: %u rects", jpeg_rects);
|
||||
auto webp_ms = webp_sum / static_cast<double>(stats.size());
|
||||
webp_rects /= stats.size();
|
||||
bytes /= stats.size();
|
||||
vlog.info("WebP stats: %f ms", webp_ms);
|
||||
vlog.info("WebP stats: %u rects", webp_rects);
|
||||
vlog.info("Total bytes sent: %lu bytes", bytes);
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
|
||||
auto *test_suit = doc.NewElement("testsuite");
|
||||
test_suit->SetAttribute("name", "Benchmark");
|
||||
|
||||
doc.InsertFirstChild(test_suit);
|
||||
auto total_tests{0};
|
||||
|
||||
auto add_benchmark_item = [&doc, &test_suit, &total_tests](const char *name, auto time_value, auto other_value) {
|
||||
auto *test_case = doc.NewElement("testcase");
|
||||
test_case->SetAttribute("name", name);
|
||||
test_case->SetAttribute("file", other_value);
|
||||
test_case->SetAttribute("time", time_value);
|
||||
test_case->SetAttribute("runs", 1);
|
||||
test_case->SetAttribute("classname", "KasmVNC");
|
||||
|
||||
test_suit->InsertEndChild(test_case);
|
||||
|
||||
++total_tests;
|
||||
};
|
||||
|
||||
constexpr auto mult = 1 / 1000.;
|
||||
add_benchmark_item("Average time encoding frame, ms", average * mult, "");
|
||||
add_benchmark_item("Median time encoding frame, ms", median * mult, "");
|
||||
add_benchmark_item("Total time encoding, ms", 0, totals_avg);
|
||||
add_benchmark_item("Total time encoding, stddev", 0, stddev);
|
||||
add_benchmark_item("Mean JPEG stats, ms", jpeg_ms, "");
|
||||
add_benchmark_item("Mean JPEG stats, rects", 0., jpeg_rects);
|
||||
add_benchmark_item("Mean WebP stats, ms", webp_ms, "");
|
||||
add_benchmark_item("Mean WebP stats, rects", 0, webp_rects);
|
||||
|
||||
add_benchmark_item("Data sent, KBs", 0, bytes / 1024);
|
||||
|
||||
doc.SaveFile(results_file.data());
|
||||
}
|
||||
|
||||
void benchmark(std::string_view path, const std::string_view results_file) {
|
||||
try {
|
||||
vlog.info("Benchmarking with video file %s", path.data());
|
||||
FFmpegFrameFeeder frame_feeder{};
|
||||
frame_feeder.open(path);
|
||||
|
||||
static const rfb::PixelFormat pf{32, 24, false, true, 0xFF, 0xFF, 0xFF, 0, 8, 16};
|
||||
std::vector<rdr::S32> encodings{
|
||||
std::begin(benchmarking::default_encodings), std::end(benchmarking::default_encodings)
|
||||
};
|
||||
|
||||
constexpr auto runs = 20;
|
||||
std::vector<uint64_t> totals(runs, 0);
|
||||
std::vector<benchmarking::MockCConnection::stats_t> stats(runs);
|
||||
std::vector<uint64_t> timings{};
|
||||
auto [width, height] = frame_feeder.get_frame_dimensions();
|
||||
|
||||
for (int run = 0; run < runs; ++run) {
|
||||
auto *pb = new rfb::ManagedPixelBuffer{pf, width, height};
|
||||
benchmarking::MockCConnection connection{encodings, pb};
|
||||
|
||||
vlog.info("RUN %d. Reading frames...", run);
|
||||
auto play_stats = frame_feeder.play(&connection);
|
||||
vlog.info("RUN %d. Done reading frames...", run);
|
||||
|
||||
timings.insert(timings.end(), play_stats.timings.begin(), play_stats.timings.end());
|
||||
|
||||
totals[run] = play_stats.total;
|
||||
stats[run] = connection.getStats();
|
||||
vlog.info("JPEG stats: %u ms", stats[run].jpeg_stats.ms);
|
||||
vlog.info("WebP stats: %u ms", stats[run].webp_stats.ms);
|
||||
vlog.info("RUN %d. Bytes sent %lu..", run, stats[run].bytes);
|
||||
}
|
||||
|
||||
if (!timings.empty())
|
||||
report(totals, timings, stats, results_file);
|
||||
|
||||
exit(0);
|
||||
} catch (std::exception &e) {
|
||||
vlog.error("Benchmarking failed: %s", e.what());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
67
common/rfb/benchmark.h
Normal file
67
common/rfb/benchmark.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* Copyright (C) 2025 Kasm Technologies Corp
|
||||
*
|
||||
* 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 "CConnection.h"
|
||||
#include "CMsgReader.h"
|
||||
#include "LogWriter.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/frame.h>
|
||||
}
|
||||
|
||||
static rfb::LogWriter vlog("Benchmarking");
|
||||
|
||||
namespace benchmarking {
|
||||
using namespace rfb;
|
||||
|
||||
class MockTestConnection : public CConnection {
|
||||
public:
|
||||
virtual void setNewFrame(const AVFrame *frame) = 0;
|
||||
};
|
||||
|
||||
static constexpr rdr::S32 default_encodings[] = {
|
||||
|
||||
encodingTight,
|
||||
encodingHextile,
|
||||
encodingRRE,
|
||||
encodingRaw,
|
||||
pseudoEncodingQualityLevel0 + 6,
|
||||
pseudoEncodingCompressLevel0 + 2,
|
||||
pseudoEncodingDesktopSize,
|
||||
pseudoEncodingLastRect,
|
||||
pseudoEncodingQEMUKeyEvent,
|
||||
pseudoEncodingExtendedDesktopSize,
|
||||
pseudoEncodingFence,
|
||||
pseudoEncodingContinuousUpdates,
|
||||
pseudoEncodingDesktopName,
|
||||
pseudoEncodingExtendedClipboard,
|
||||
pseudoEncodingWEBP,
|
||||
pseudoEncodingJpegVideoQualityLevel0 + 7,
|
||||
pseudoEncodingWebpVideoQualityLevel0 + 7,
|
||||
pseudoEncodingTreatLosslessLevel0 + 7,
|
||||
pseudoEncodingDynamicQualityMinLevel0 + 4,
|
||||
pseudoEncodingDynamicQualityMaxLevel0 + 9,
|
||||
pseudoEncodingVideoAreaLevel1 - 1 + 65,
|
||||
pseudoEncodingVideoTimeLevel0 + 5,
|
||||
pseudoEncodingVideoOutTimeLevel1 - 1 + 3,
|
||||
pseudoEncodingFrameRateLevel10 - 10 + 60,
|
||||
pseudoEncodingMaxVideoResolution
|
||||
};
|
||||
}
|
||||
210
common/rfb/ffmpeg.cxx
Normal file
210
common/rfb/ffmpeg.cxx
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/* Copyright (C) 2025 Kasm Technologies Corp
|
||||
*
|
||||
* 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 "ffmpeg.h"
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <filesystem>
|
||||
|
||||
FFmpegFrameFeeder::FFmpegFrameFeeder() {
|
||||
static constexpr std::array<std::string_view, 2> paths = {
|
||||
"/usr/lib/",
|
||||
"/usr/lib64"
|
||||
};
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto load_lib = [](auto *lib) {
|
||||
void *handle{};
|
||||
for (const auto &dir: paths) {
|
||||
if (!fs::exists(dir) || !fs::is_directory(dir))
|
||||
continue;
|
||||
|
||||
for (const auto &entry: fs::recursive_directory_iterator(dir)) {
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
|
||||
const std::string filename = entry.path().filename().string();
|
||||
if (filename.find(lib) != std::string::npos) {
|
||||
handle = dlopen(filename.c_str(), RTLD_LAZY);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!handle)
|
||||
throw std::runtime_error("Could not open "s + lib);
|
||||
|
||||
return DlHandlerGuard{handle};
|
||||
};
|
||||
|
||||
// libavformat
|
||||
libavformat = load_lib("libavformat.so");
|
||||
auto handle = libavformat.get();
|
||||
|
||||
avformat_open_input_f = D_LOOKUP_SYM(handle, avformat_open_input);
|
||||
avformat_find_stream_info_f = D_LOOKUP_SYM(handle, avformat_find_stream_info);
|
||||
avcodec_find_decoder_f = D_LOOKUP_SYM(handle, avcodec_find_decoder);
|
||||
avcodec_parameters_to_context_f = D_LOOKUP_SYM(handle, avcodec_parameters_to_context);
|
||||
av_read_frame_f = D_LOOKUP_SYM(handle, av_read_frame);
|
||||
av_seek_frame_f = D_LOOKUP_SYM(handle, av_seek_frame);
|
||||
avformat_close_input_f = D_LOOKUP_SYM(handle, avformat_close_input);
|
||||
|
||||
vlog.info("libavformat.so loaded");
|
||||
|
||||
// libavutil
|
||||
libavutil = load_lib("libavutil.so");
|
||||
handle = libavutil.get();
|
||||
|
||||
av_frame_free_f = D_LOOKUP_SYM(handle, av_frame_free);
|
||||
av_frame_alloc_f = D_LOOKUP_SYM(handle, av_frame_alloc);
|
||||
av_frame_get_buffer_f = D_LOOKUP_SYM(handle, av_frame_get_buffer);
|
||||
|
||||
vlog.info("libavutil.so loaded");
|
||||
|
||||
// libswscale
|
||||
libswscale = load_lib("libswscale.so");
|
||||
handle = libswscale.get();
|
||||
|
||||
sws_freeContext_f = D_LOOKUP_SYM(handle, sws_freeContext);
|
||||
sws_getContext_f = D_LOOKUP_SYM(handle, sws_getContext);
|
||||
sws_scale_f = D_LOOKUP_SYM(handle, sws_scale);
|
||||
|
||||
// libavcodec
|
||||
libavcodec = load_lib("libavcodec.so");
|
||||
handle = libavcodec.get();
|
||||
|
||||
avcodec_open2_f = D_LOOKUP_SYM(handle, avcodec_open2);
|
||||
avcodec_alloc_context3_f = D_LOOKUP_SYM(handle, avcodec_alloc_context3);
|
||||
avcodec_send_packet_f = D_LOOKUP_SYM(handle, avcodec_send_packet);
|
||||
avcodec_receive_frame_f = D_LOOKUP_SYM(handle, avcodec_receive_frame);
|
||||
av_packet_unref_f = D_LOOKUP_SYM(handle, av_packet_unref);
|
||||
avcodec_flush_buffers_f = D_LOOKUP_SYM(handle, avcodec_flush_buffers);
|
||||
avcodec_close_f = D_LOOKUP_SYM(handle, avcodec_close);
|
||||
av_packet_alloc_f = D_LOOKUP_SYM(handle, av_packet_alloc);
|
||||
av_packet_free_f = D_LOOKUP_SYM(handle, av_packet_free);
|
||||
}
|
||||
|
||||
FFmpegFrameFeeder::~FFmpegFrameFeeder() {
|
||||
avformat_close_input_f(&format_ctx);
|
||||
avcodec_close_f(codec_ctx);
|
||||
avcodec_free_context_f(&codec_ctx);
|
||||
}
|
||||
|
||||
void FFmpegFrameFeeder::open(const std::string_view path) {
|
||||
if (avformat_open_input_f(&format_ctx, path.data(), nullptr, nullptr) < 0)
|
||||
throw std::runtime_error("Could not open video file");
|
||||
|
||||
// Find stream info
|
||||
if (avformat_find_stream_info_f(format_ctx, nullptr) < 0)
|
||||
throw std::runtime_error("Could not find stream info");
|
||||
|
||||
// Find video stream
|
||||
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_f(codec_parameters->codec_id);
|
||||
if (!codec)
|
||||
throw std::runtime_error("Codec not found");
|
||||
|
||||
codec_ctx = avcodec_alloc_context3_f(codec);
|
||||
if (!codec_ctx || avcodec_parameters_to_context_f(codec_ctx, codec_parameters) < 0)
|
||||
throw std::runtime_error("Failed to set up codec context");
|
||||
|
||||
if (avcodec_open2_f(codec_ctx, codec, nullptr) < 0)
|
||||
throw std::runtime_error("Could not open codec");
|
||||
}
|
||||
|
||||
FFmpegFrameFeeder::play_stats_t FFmpegFrameFeeder::play(benchmarking::MockTestConnection *connection) const {
|
||||
// Allocate frame and packet
|
||||
const FrameGuard frame{av_frame_alloc_f()};
|
||||
const PacketGuard packet{av_packet_alloc_f()};
|
||||
|
||||
if (!frame || !packet)
|
||||
throw std::runtime_error("Could not allocate frame or packet");
|
||||
|
||||
// Scaling context to convert to RGB24
|
||||
SwsContext *sws_ctx = sws_getContext_f(
|
||||
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");
|
||||
|
||||
const std::unique_ptr<SwsContext, void(*)(SwsContext *)> sws_ctx_guard{sws_ctx, sws_freeContext_f};
|
||||
|
||||
const FrameGuard rgb_frame{av_frame_alloc_f()};
|
||||
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_f(rgb_frame.get(), 0) != 0)
|
||||
throw std::runtime_error("Could not allocate frame data");
|
||||
|
||||
play_stats_t stats{};
|
||||
const auto total_frame_count = get_total_frame_count();
|
||||
stats.timings.reserve(total_frame_count > 0 ? total_frame_count : 2048);
|
||||
|
||||
while (av_read_frame_f(format_ctx, packet.get()) == 0) {
|
||||
if (packet->stream_index == video_stream_idx) {
|
||||
if (avcodec_send_packet_f(codec_ctx, packet.get()) == 0) {
|
||||
while (avcodec_receive_frame_f(codec_ctx, frame.get()) == 0) {
|
||||
// Convert to RGB
|
||||
sws_scale_f(sws_ctx_guard.get(), frame->data, frame->linesize, 0,
|
||||
frame->height,
|
||||
rgb_frame->data, rgb_frame->linesize);
|
||||
|
||||
connection->framebufferUpdateStart();
|
||||
connection->setNewFrame(rgb_frame.get());
|
||||
using namespace std::chrono;
|
||||
|
||||
auto now = high_resolution_clock::now();
|
||||
connection->framebufferUpdateEnd();
|
||||
const auto duration = duration_cast<milliseconds>(high_resolution_clock::now() - now).count();
|
||||
|
||||
//vlog.info("Frame took %lu ms", duration);
|
||||
stats.total += duration;
|
||||
stats.timings.push_back(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
av_packet_unref_f(packet.get());
|
||||
}
|
||||
|
||||
if (av_seek_frame_f(format_ctx, video_stream_idx, 0, AVSEEK_FLAG_BACKWARD) < 0)
|
||||
throw std::runtime_error("Could not seek to start of video");
|
||||
|
||||
avcodec_flush_buffers_f(codec_ctx);
|
||||
|
||||
return stats;
|
||||
}
|
||||
168
common/rfb/ffmpeg.h
Normal file
168
common/rfb/ffmpeg.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/* Copyright (C) 2025 Kasm Technologies Corp
|
||||
*
|
||||
* 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 <dlfcn.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "LogWriter.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include "benchmark.h"
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
#define CONCAT_STR(a, b) a b
|
||||
|
||||
#define D_LOOKUP_SYM(handle, name) \
|
||||
[](auto handle, auto *sym_name) -> auto { \
|
||||
auto *sym = reinterpret_cast<name##_func>(dlsym(handle, sym_name)); \
|
||||
if (!sym) \
|
||||
throw std::runtime_error("Failed to load symbol "s + sym_name); \
|
||||
return sym; \
|
||||
}(handle, STR(name))
|
||||
|
||||
#define DEFINE_GUARD(name, type, deleter) \
|
||||
using name##Guard = std::unique_ptr<type, decltype([](auto *ptr){deleter##_f(&ptr);})>;
|
||||
|
||||
//using SwsContextGuard = std::unique_ptr<SwsContext, SwsContextDeleter>;
|
||||
|
||||
class FFmpegFrameFeeder final {
|
||||
// libavformat
|
||||
using avformat_close_input_func = void(*)(AVFormatContext **);
|
||||
using avformat_open_input_func = int(*)(AVFormatContext **ps, const char *url, const AVInputFormat *fmt,
|
||||
AVDictionary **options);
|
||||
using avformat_find_stream_info_func = int (*)(AVFormatContext *ic, AVDictionary **options);
|
||||
using av_read_frame_func = int (*)(AVFormatContext *s, AVPacket *pkt);
|
||||
using av_seek_frame_func = int (*)(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);
|
||||
|
||||
// libavutil
|
||||
using av_frame_free_func = void (*)(AVFrame **);
|
||||
using av_frame_alloc_func = AVFrame *(*)();
|
||||
using av_frame_get_buffer_func = int (*)(AVFrame *frame, int align);
|
||||
|
||||
// libswscale
|
||||
using sws_freeContext_func = void (*)(SwsContext *);
|
||||
using sws_getContext_func = SwsContext * (*)(int srcW, int srcH, AVPixelFormat srcFormat, int dstW, int dstH,
|
||||
AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter,
|
||||
SwsFilter *dstFilter, const double *param);
|
||||
|
||||
using sws_scale_func = int(*)(SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY,
|
||||
int srcSliceH, uint8_t *const dst[], const int dstStride[]);
|
||||
|
||||
// libavcodec
|
||||
using avcodec_free_context_func = void (*)(AVCodecContext **);
|
||||
using av_packet_free_func = void (*)(AVPacket **);
|
||||
using avcodec_find_decoder_func = const AVCodec * (*)(AVCodecID id);
|
||||
using avcodec_alloc_context3_func = AVCodecContext* (*)(const AVCodec *codec);
|
||||
using avcodec_parameters_to_context_func = int (*)(AVCodecContext *codec, const AVCodecParameters *par);
|
||||
using avcodec_open2_func = int (*)(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
|
||||
using av_packet_alloc_func = AVPacket *(*)();
|
||||
using avcodec_send_packet_func = int(*)(AVCodecContext *avctx, const AVPacket *avpkt);
|
||||
using avcodec_receive_frame_func = int(*)(AVCodecContext *avctx, AVFrame *frame);
|
||||
using av_packet_unref_func = void (*)(AVPacket *pkt);
|
||||
using avcodec_flush_buffers_func = void (*)(AVCodecContext *avctx);
|
||||
using avcodec_close_func = int (*)(AVCodecContext *avctx);
|
||||
|
||||
struct DlHandler {
|
||||
void operator()(void *handle) const {
|
||||
dlclose(handle);
|
||||
}
|
||||
};
|
||||
|
||||
using DlHandlerGuard = std::unique_ptr<void, DlHandler>;
|
||||
|
||||
// libavformat
|
||||
avformat_close_input_func avformat_close_input_f{};
|
||||
avformat_open_input_func avformat_open_input_f{};
|
||||
avformat_find_stream_info_func avformat_find_stream_info_f{};
|
||||
av_read_frame_func av_read_frame_f{};
|
||||
av_seek_frame_func av_seek_frame_f{};
|
||||
|
||||
// libavutil
|
||||
static inline av_frame_free_func av_frame_free_f{};
|
||||
av_frame_alloc_func av_frame_alloc_f{};
|
||||
av_frame_get_buffer_func av_frame_get_buffer_f{};
|
||||
|
||||
// libswscale
|
||||
sws_freeContext_func sws_freeContext_f{};
|
||||
sws_getContext_func sws_getContext_f{};
|
||||
sws_scale_func sws_scale_f{};
|
||||
|
||||
// libavcodec
|
||||
avcodec_free_context_func avcodec_free_context_f{};
|
||||
static inline av_packet_free_func av_packet_free_f{};
|
||||
avcodec_find_decoder_func avcodec_find_decoder_f{};
|
||||
avcodec_alloc_context3_func avcodec_alloc_context3_f{};
|
||||
avcodec_parameters_to_context_func avcodec_parameters_to_context_f{};
|
||||
avcodec_open2_func avcodec_open2_f{};
|
||||
av_packet_alloc_func av_packet_alloc_f{};
|
||||
avcodec_send_packet_func avcodec_send_packet_f{};
|
||||
avcodec_receive_frame_func avcodec_receive_frame_f{};
|
||||
av_packet_unref_func av_packet_unref_f{};
|
||||
avcodec_flush_buffers_func avcodec_flush_buffers_f{};
|
||||
avcodec_close_func avcodec_close_f{};
|
||||
|
||||
rfb::LogWriter vlog{"FFmpeg"};
|
||||
|
||||
DEFINE_GUARD(Frame, AVFrame, av_frame_free)
|
||||
DEFINE_GUARD(Packet, AVPacket, av_packet_free)
|
||||
|
||||
AVFormatContext *format_ctx{};
|
||||
AVCodecContext *codec_ctx{};
|
||||
int video_stream_idx{-1};
|
||||
|
||||
DlHandlerGuard libavformat{};
|
||||
DlHandlerGuard libavutil{};
|
||||
DlHandlerGuard libswscale{};
|
||||
DlHandlerGuard libavcodec{};
|
||||
|
||||
public:
|
||||
FFmpegFrameFeeder();
|
||||
|
||||
~FFmpegFrameFeeder();
|
||||
|
||||
void open(std::string_view path);
|
||||
|
||||
[[nodiscard]] int64_t get_total_frame_count() const {
|
||||
return format_ctx->streams[video_stream_idx]->nb_frames;
|
||||
}
|
||||
|
||||
struct frame_dimensions_t {
|
||||
int width{};
|
||||
int height{};
|
||||
};
|
||||
|
||||
[[nodiscard]] frame_dimensions_t get_frame_dimensions() const {
|
||||
return {codec_ctx->width, codec_ctx->height};
|
||||
}
|
||||
|
||||
struct play_stats_t {
|
||||
uint64_t frames{};
|
||||
uint64_t total{};
|
||||
std::vector<uint64_t> timings;
|
||||
};
|
||||
|
||||
play_stats_t play(benchmarking::MockTestConnection *connection) const;
|
||||
};
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <rfb/util.h>
|
||||
|
|
@ -572,6 +572,11 @@ namespace rfb {
|
|||
return msBetween(then, &now);
|
||||
}
|
||||
|
||||
uint64_t elapsedMs(std::chrono::high_resolution_clock::time_point start)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count();
|
||||
}
|
||||
|
||||
bool isBefore(const struct timeval *first,
|
||||
const struct timeval *second)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,8 +28,9 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
|
||||
struct timeval;
|
||||
|
||||
|
|
@ -127,6 +128,15 @@ namespace rfb {
|
|||
// Returns time elapsed since given moment in milliseconds.
|
||||
unsigned msSince(const struct timeval *then);
|
||||
|
||||
/**
|
||||
* Calculates the number of milliseconds elapsed since a given starting point.
|
||||
*
|
||||
* @param start The starting time point of type `std::chrono::high_resolution_clock::time_point`.
|
||||
*
|
||||
* @return The elapsed time in milliseconds as an unsigned 64-bit integer.
|
||||
*/
|
||||
uint64_t elapsedMs(std::chrono::high_resolution_clock::time_point start);
|
||||
|
||||
// Returns true if first happened before seconds
|
||||
bool isBefore(const struct timeval *first,
|
||||
const struct timeval *second);
|
||||
|
|
|
|||
2
kasmweb
2
kasmweb
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c46b2e13ab1dd7232b28f017fd7e49ca740f5a4
|
||||
Subproject commit 0d8a3c5f07defffe340dd67a1485be5214bec4ee
|
||||
|
|
@ -23,6 +23,18 @@ def write_config(config_text):
|
|||
f.write(config_text)
|
||||
|
||||
|
||||
def clean_locks():
|
||||
tmp = '/tmp'
|
||||
temporary_lock_file = os.path.join(tmp, '.X1-lock')
|
||||
if (os.path.exists(temporary_lock_file)):
|
||||
os.remove(temporary_lock_file)
|
||||
|
||||
temporary_lock_file = os.path.join(tmp, '.X11-unix')
|
||||
temporary_lock_file = os.path.join(temporary_lock_file, 'X1')
|
||||
if (os.path.exists(temporary_lock_file)):
|
||||
os.remove(temporary_lock_file)
|
||||
|
||||
|
||||
def clean_env():
|
||||
clean_kasm_users()
|
||||
|
||||
|
|
@ -30,6 +42,7 @@ def clean_env():
|
|||
vnc_dir = os.path.join(home_dir, ".vnc")
|
||||
Path(vnc_dir).rmtree(ignore_errors=True)
|
||||
|
||||
clean_locks()
|
||||
|
||||
def clean_kasm_users():
|
||||
home_dir = os.environ['HOME']
|
||||
|
|
@ -91,3 +104,4 @@ def kill_xvnc():
|
|||
|
||||
run_cmd('vncserver -kill :1', print_stderr=False)
|
||||
running_xvnc = False
|
||||
clean_locks()
|
||||
|
|
|
|||
15
spec/vncserver_adv_benchmarking_spec.py
Normal file
15
spec/vncserver_adv_benchmarking_spec.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
from mamba import description, context, it, fit, before, after
|
||||
from expects import expect, equal, contain, match
|
||||
from helper.spec_helper import run_cmd, clean_env, kill_xvnc
|
||||
|
||||
with description("Benchmarking"):
|
||||
with before.each:
|
||||
clean_env()
|
||||
with after.each:
|
||||
kill_xvnc()
|
||||
with it("runs benchmarks"):
|
||||
run_cmd("wget --no-check-certificate https://kasmweb-build-artifacts.s3.us-east-1.amazonaws.com/kasmvnc/static/127072-737747495_small.mp4 -O /tmp/video.mp4")
|
||||
completed_process = run_cmd("Xvnc -interface 0.0.0.0 :1 -Benchmark /tmp/video.mp4 -VideoArea 100")
|
||||
command = '''sed -i "s/KasmVNC/$(grep -E '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"') $(grep -E '^VERSION_CODENAME=' /etc/os-release | cut -d= -f2 | tr -d '"')/g" Benchmark.xml'''
|
||||
run_cmd(command)
|
||||
expect(completed_process.returncode).to(equal(0))
|
||||
12
spec/vncserver_benchmarking_spec.py
Normal file
12
spec/vncserver_benchmarking_spec.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from mamba import description, context, it, fit, before, after
|
||||
from expects import expect, equal, contain, match
|
||||
from helper.spec_helper import run_cmd, clean_env, kill_xvnc, clean_locks
|
||||
|
||||
with description("Benchmarking"):
|
||||
with before.each:
|
||||
clean_env()
|
||||
with after.each:
|
||||
kill_xvnc()
|
||||
with it("runs benchmarks"):
|
||||
completed_process = run_cmd("Xvnc -interface 0.0.0.0 :1 -selfBench")
|
||||
expect(completed_process.returncode).to(equal(0))
|
||||
1
third_party/CMakeLists.txt
vendored
Normal file
1
third_party/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
add_subdirectory(tinyxml2)
|
||||
5
third_party/tinyxml2/CMakeLists.txt
vendored
Normal file
5
third_party/tinyxml2/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
add_library(tinyxml2_objs OBJECT tinyxml2.cpp)
|
||||
if (NOT WIN32)
|
||||
set_target_properties(tinyxml2_objs PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
endif ()
|
||||
target_include_directories(tinyxml2_objs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
18
third_party/tinyxml2/LICENSE.txt
vendored
Normal file
18
third_party/tinyxml2/LICENSE.txt
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
3019
third_party/tinyxml2/tinyxml2.cpp
vendored
Normal file
3019
third_party/tinyxml2/tinyxml2.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
2383
third_party/tinyxml2/tinyxml2.h
vendored
Normal file
2383
third_party/tinyxml2/tinyxml2.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -37,7 +37,7 @@ user_session:
|
|||
|
||||
keyboard:
|
||||
remap_keys:
|
||||
# - 0x22->0x40
|
||||
# - 0x22->0x40
|
||||
ignore_numlock: false
|
||||
raw_keyboard: false
|
||||
|
||||
|
|
@ -129,6 +129,7 @@ encoding:
|
|||
logging:
|
||||
level: off
|
||||
scaling_algorithm: progressive_bilinear
|
||||
webp_encoding_time: 30
|
||||
|
||||
compare_framebuffer: auto
|
||||
zrle_zlib_level: auto
|
||||
|
|
|
|||
|
|
@ -2068,6 +2068,15 @@ sub DefineConfigToCLIConversion {
|
|||
$value;
|
||||
}
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'WebpEncodingTime',
|
||||
configKeys => [
|
||||
KasmVNC::ConfigKey->new({
|
||||
name => "encoding.video_encoding_mode.webp_encoding_time",
|
||||
type => KasmVNC::ConfigKey::INT
|
||||
})
|
||||
]
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'CompareFB',
|
||||
configKeys => [
|
||||
|
|
|
|||
|
|
@ -574,6 +574,16 @@ When \fBNoClipboard\fP parameter is set, allowing override of \fBSendCutText\fP
|
|||
and \fBAcceptCutText\fP has no effect.
|
||||
|
||||
Default is \fBdesktop,AcceptPointerEvents,SendCutText,AcceptCutText,SendPrimary,SetPrimary\fP.
|
||||
.
|
||||
TP
|
||||
.B -Benchmark <video_file>
|
||||
Run the built-in benchmarking routines on the specified video file and exit.
|
||||
When this option is used, benchmarking results can be saved to a file specified by the \fB-BenchmarkResults\fP option; otherwise, the results are saved to \fBBenchmark.xml\fP by default.
|
||||
.
|
||||
.TP
|
||||
.B -BenchmarkResults <results_file.xml>
|
||||
Save the benchmarking results to the specified file.
|
||||
Use this option together with \fB-Benchmark\fP to output the report to a custom file.
|
||||
|
||||
.SH USAGE WITH INETD
|
||||
By configuring the \fBinetd\fP(1) service appropriately, Xvnc can be launched
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ static void parseClipTypes()
|
|||
|
||||
vlog.debug("Adding DLP binary mime type %s", m.mime);
|
||||
}
|
||||
vlog.debug("Total %u binary mime types", dlp_mimetypes.size());
|
||||
vlog.debug("Total %lu binary mime types", dlp_mimetypes.size());
|
||||
|
||||
free(origstr);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue