Compare commits
38 Commits
video
...
KASM-1910-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f1f88d6b2 | ||
|
|
2f91fbd235 | ||
|
|
3500489bb8 | ||
|
|
22362e73ad | ||
|
|
0a1f705637 | ||
|
|
02dc1a4b53 | ||
|
|
33dd45b6e5 | ||
|
|
c5d61ca63e | ||
|
|
bb2dc1787d | ||
|
|
2bdfd0f70a | ||
|
|
c9a4319ca7 | ||
|
|
b367d1711d | ||
|
|
159d752795 | ||
|
|
0cb2c0ba9f | ||
|
|
dc21d5f97c | ||
|
|
91eb953f62 | ||
|
|
6f2805e186 | ||
|
|
d80eb39686 | ||
|
|
4b28667e90 | ||
|
|
5341bc6fba | ||
|
|
9ec4cba184 | ||
|
|
643a0cfed6 | ||
|
|
9ca850a108 | ||
|
|
ec6bd697a8 | ||
|
|
544a9fc592 | ||
|
|
7b5838a1ea | ||
|
|
a9b66833a9 | ||
|
|
e208d5bb5f | ||
|
|
f57e6e644b | ||
|
|
c1ed769780 | ||
|
|
fae8aa9051 | ||
|
|
438271d68b | ||
|
|
fb9dd56703 | ||
|
|
32e8d40472 | ||
|
|
811e7cde3a | ||
|
|
72affbfcf4 | ||
|
|
b362110b07 | ||
|
|
29e0e2bd2e |
14
.ci/check_if_build_was_already_uploaded
Executable file
14
.ci/check_if_build_was_already_uploaded
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
check_directory_exists() {
|
||||||
|
local remote_dir="$1"
|
||||||
|
|
||||||
|
curl --output /dev/null --silent --head --fail "$remote_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
S3_URL="https://${S3_BUCKET}.s3.amazonaws.com/${S3_BUILD_DIRECTORY}/";
|
||||||
|
if check_directory_exists "$S3_URL"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -1,16 +1,33 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
is_kasmvnc_package() {
|
||||||
|
local package="$1";
|
||||||
|
|
||||||
|
echo "$package" | grep -E -q 'kasmvncserver_|rpm'
|
||||||
|
}
|
||||||
|
|
||||||
function prepare_upload_filename() {
|
function prepare_upload_filename() {
|
||||||
local package="$1";
|
local package="$1";
|
||||||
|
|
||||||
|
if ! is_kasmvnc_package "$package"; then
|
||||||
|
export upload_filename="$package"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
.ci/detect_os_arch_package_format "$package" > /tmp/os_arch_package_format;
|
.ci/detect_os_arch_package_format "$package" > /tmp/os_arch_package_format;
|
||||||
source /tmp/os_arch_package_format;
|
source /tmp/os_arch_package_format;
|
||||||
detect_release_branch
|
detect_release_branch
|
||||||
|
|
||||||
|
detect_revision "$package" "$OS_ARCH"
|
||||||
|
if [ -n "$REVISION" ]; then
|
||||||
|
REVISION="_${REVISION}"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "$RELEASE_BRANCH" ]; then
|
if [ -n "$RELEASE_BRANCH" ]; then
|
||||||
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}_${OS_ARCH}.${PACKAGE_FORMAT}";
|
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}${REVISION}_${OS_ARCH}.${PACKAGE_FORMAT}";
|
||||||
else
|
else
|
||||||
export SANITIZED_BRANCH="$(echo $CI_COMMIT_REF_NAME | sed 's/\//_/g')";
|
export SANITIZED_BRANCH="$(echo $CI_COMMIT_REF_NAME | sed 's/\//_/g')";
|
||||||
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}_${SANITIZED_BRANCH}_${CI_COMMIT_SHA:0:6}_${OS_ARCH}.${PACKAGE_FORMAT}";
|
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}_${SANITIZED_BRANCH}_${CI_COMMIT_SHA:0:6}${REVISION}_${OS_ARCH}.${PACKAGE_FORMAT}";
|
||||||
fi
|
fi
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,6 +42,7 @@ function upload_to_s3() {
|
|||||||
export BUILD_STATUS="{\"key\":\"doc\", \"state\":\"SUCCESSFUL\", \"name\":\"${upload_filename}\", \"url\":\"${S3_URL}\"}";
|
export BUILD_STATUS="{\"key\":\"doc\", \"state\":\"SUCCESSFUL\", \"name\":\"${upload_filename}\", \"url\":\"${S3_URL}\"}";
|
||||||
curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=build-url&target_url=${S3_URL}";
|
curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=build-url&target_url=${S3_URL}";
|
||||||
};
|
};
|
||||||
|
|
||||||
function prepare_to_run_scripts_and_s3_uploads() {
|
function prepare_to_run_scripts_and_s3_uploads() {
|
||||||
export DEBIAN_FRONTEND=noninteractive;
|
export DEBIAN_FRONTEND=noninteractive;
|
||||||
apt-get update;
|
apt-get update;
|
||||||
@@ -38,3 +56,16 @@ detect_release_branch() {
|
|||||||
export RELEASE_BRANCH=1;
|
export RELEASE_BRANCH=1;
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detect_revision() {
|
||||||
|
local package="$1"
|
||||||
|
local arch="$2"
|
||||||
|
|
||||||
|
REVISION=
|
||||||
|
|
||||||
|
if ! echo "$package" | grep -q '+'; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
REVISION=$(echo "$package" | sed "s/_${arch}.\+//" | sed 's/.\++//')
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
builder/build
|
|
||||||
@@ -6,11 +6,22 @@ variables:
|
|||||||
GITLAB_SHARED_DIND_DIR: /builds/$CI_PROJECT_PATH/shared
|
GITLAB_SHARED_DIND_DIR: /builds/$CI_PROJECT_PATH/shared
|
||||||
GIT_SUBMODULE_STRATEGY: normal
|
GIT_SUBMODULE_STRATEGY: normal
|
||||||
GIT_FETCH_EXTRA_FLAGS: --tags
|
GIT_FETCH_EXTRA_FLAGS: --tags
|
||||||
|
# S3_BUILD_DIRECTORY: kasmvnc/${CI_COMMIT_SHA}
|
||||||
|
S3_BUILD_DIRECTORY: kasmvnc/159d7527955f131e096cf1602b7f9f66cc5d66cb
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
- check_if_build_was_already_uploaded_for_the_commit
|
||||||
- build
|
- build
|
||||||
- upload
|
- upload
|
||||||
|
|
||||||
|
check_if_build_was_already_uploaded_for_the_commit:
|
||||||
|
stage: check_if_build_was_already_uploaded_for_the_commit
|
||||||
|
script:
|
||||||
|
- apk add bash
|
||||||
|
- apk add curl
|
||||||
|
# - TODO: Try uploading and fail if the file .lock exists.
|
||||||
|
- .ci/check_if_build_was_already_uploaded
|
||||||
|
|
||||||
.prepare_build: &prepare_build
|
.prepare_build: &prepare_build
|
||||||
- ls -l
|
- ls -l
|
||||||
- pwd
|
- pwd
|
||||||
@@ -34,6 +45,18 @@ build_ubuntu_bionic:
|
|||||||
paths:
|
paths:
|
||||||
- output/
|
- output/
|
||||||
|
|
||||||
|
build_ubuntu_bionic_libjpeg_turbo:
|
||||||
|
stage: build
|
||||||
|
before_script:
|
||||||
|
- *prepare_build
|
||||||
|
after_script:
|
||||||
|
- *prepare_artfacts
|
||||||
|
script:
|
||||||
|
- bash builder/build-package ubuntu bionic +libjpeg-turbo_latest
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- output/
|
||||||
|
|
||||||
build_ubuntu_focal:
|
build_ubuntu_focal:
|
||||||
stage: build
|
stage: build
|
||||||
before_script:
|
before_script:
|
||||||
@@ -100,10 +123,9 @@ upload:
|
|||||||
before_script:
|
before_script:
|
||||||
- . .ci/upload.sh
|
- . .ci/upload.sh
|
||||||
script:
|
script:
|
||||||
- export S3_BUILD_DIRECTORY="kasmvnc/${CI_COMMIT_SHA}"
|
|
||||||
- prepare_to_run_scripts_and_s3_uploads
|
- prepare_to_run_scripts_and_s3_uploads
|
||||||
- export RELEASE_VERSION=$(.ci/next_release_version "$CI_COMMIT_REF_NAME")
|
- export RELEASE_VERSION=$(.ci/next_release_version "$CI_COMMIT_REF_NAME")
|
||||||
- for package in `find output/ -type f -name 'kasmvncserver_*.deb' -or -name '*.rpm'`; do
|
- for package in `find output/ -type f -name '*.deb' -or -name '*.rpm'`; do
|
||||||
prepare_upload_filename "$package";
|
prepare_upload_filename "$package";
|
||||||
echo;
|
echo;
|
||||||
echo "File to upload $upload_filename";
|
echo "File to upload $upload_filename";
|
||||||
|
|||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,4 +1,4 @@
|
|||||||
[submodule "kasmweb"]
|
[submodule "kasmweb"]
|
||||||
path = kasmweb
|
path = kasmweb
|
||||||
url = https://github.com/kasmtech/noVNC.git
|
url = https://github.com/kasmtech/noVNC.git
|
||||||
branch = video
|
branch = master
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ include(CheckLibraryExists)
|
|||||||
include(CheckTypeSize)
|
include(CheckTypeSize)
|
||||||
include(CheckCSourceCompiles)
|
include(CheckCSourceCompiles)
|
||||||
include(CheckCXXSourceCompiles)
|
include(CheckCXXSourceCompiles)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
include(CheckCSourceRuns)
|
include(CheckCSourceRuns)
|
||||||
|
|
||||||
include(CMakeMacroLibtoolFile)
|
include(CMakeMacroLibtoolFile)
|
||||||
@@ -208,6 +209,9 @@ if(ENABLE_PAM)
|
|||||||
endif()
|
endif()
|
||||||
set(HAVE_PAM ${ENABLE_PAM})
|
set(HAVE_PAM ${ENABLE_PAM})
|
||||||
|
|
||||||
|
# Check for SSE2
|
||||||
|
check_cxx_compiler_flag(-msse2 COMPILER_SUPPORTS_SSE2)
|
||||||
|
|
||||||
# Generate config.h and make sure the source finds it
|
# Generate config.h and make sure the source finds it
|
||||||
configure_file(config.h.in config.h)
|
configure_file(config.h.in config.h)
|
||||||
add_definitions(-DHAVE_CONFIG_H)
|
add_definitions(-DHAVE_CONFIG_H)
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -1,11 +1,11 @@
|
|||||||
# KasmVNC - Linux Web Remote Desktop
|
# KasmVNC - Linux Web Remote Desktop
|
||||||
|
|
||||||
[](https://kasmweb.com)
|
<a href="https://kasmweb.com"><img src="https://kasm-static-content.s3.amazonaws.com/logo_kasm.png" width="300"><a/>
|
||||||
|
|
||||||
[Kasm Technologies LLC](https://www.kasmweb.com) developed Kasm Server, a Containerized Desktop Infrastructure (CDI) solution. Kasm started with TigerVNC and eventually forked it to create KasmVNC. KasmVNC is used within the overal Kasm CDI infrastructure, however, you can use KasmVNC for individual servers. KasmVNC has different goals than TigerVNC:
|
[Kasm Technologies](https://www.kasmweb.com) developed Kasm Workspaces, the Containerized Streaming Platform. Kasm has open-sourced the Workspace docker images, which include containerized [full desktops and apps](https://github.com/kasmtech/workspaces-images) and [base images](https://github.com/kasmtech/workspaces-core-images) intended for developers to create custimized streaming containers. These containers can be used standalone or within the [Kasm Workspaces Platform](https://www.kasmweb.com) which provides a full Enterprise feature set. KasmVNC is used as the streaming tech for our container images, however, you can use KasmVNC for individual servers. While the term VNC is in the name, KasmVNC is not intended to remain compliant with the RFB spec and has different goals than other VNC projects:
|
||||||
|
|
||||||
- Web-based - KasmVNC is designed to provide a web accessible remote desktop. It comes with a web server and websocket server built in. There is no need to install other components. Simply run and navigate to your desktop's URL on the port you specify. While you can still tun on the legacy VNC port, it is disabled by default.
|
- Web-based - KasmVNC is designed to provide a web accessible remote desktop. It comes with a web server and websocket server built in. There is no need to install other components. Simply run and navigate to your desktop's URL on the port you specify. While you can still tun on the legacy VNC port, it is disabled by default.
|
||||||
- Security - KasmVNC defaults to HTTPS and allows for HTTP Basic Auth. VNC Password authentication is limited by specification to 8 characters and is not suffecient for use on an internet accessible remote desktop. Our goal is to create a by default secure, web based experience.
|
- Security - The RFB specification (VNC) limits the password field to 8 characters, so while the client may take a longer password, only the first 8 characters are sent. KasmVNC defaults to HTTPS with HTTP Basic Auth and disables the legacy VNC authentication method which is not sufficiently secure for internet accessible systems.
|
||||||
- Simplicity - KasmVNC aims at being simple to deploy and configure.
|
- Simplicity - KasmVNC aims at being simple to deploy and configure.
|
||||||
|
|
||||||
# New Features!
|
# New Features!
|
||||||
@@ -91,11 +91,13 @@ The options for vncserver:
|
|||||||
| interface | Which interface to bind the web server to. |
|
| interface | Which interface to bind the web server to. |
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com
|
Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com. We have investigated or are working on the following, if you have experience in these fields and would like to help please let us know.
|
||||||
|
|
||||||
We need help, especially in packaging KasmVNC for various operating systems. We would love to have standard debian or RMP packages and host our own repo, however, that all requires a lot of experience, proper testing, and pipeline development for automated builds.
|
Real-time H264 encoding using NVIDIA and Intel encoding technology.
|
||||||
|
|
||||||
We also need help with Windows, which is not currently supported. While KasmVNC can technically be built for Windows 10, it is unusably slow, due to all the changes that occured in Windows since the original Windows support was added in the chain of VNC forked projects.
|
Windows version of KasmVNC. We have been able to get it to compile for Windows and increased the performance, but still not releasable. Experienced Windows developers with a background in cross compiling would help.
|
||||||
|
|
||||||
|
ARM version of KasmVNC, we have had requests for this and at one point we did have an ARM build of KasmVNC but it takes dev cycles to mainain and bring it back to life.
|
||||||
|
|
||||||
### Compiling From Source
|
### Compiling From Source
|
||||||
See the [builder/README.md](https://github.com/kasmtech/KasmVNC/blob/master/builder/README.md). We containerize our build systems to ensure highly repeatable builds.
|
See the [builder/README.md](https://github.com/kasmtech/KasmVNC/blob/master/builder/README.md). We containerize our build systems to ensure highly repeatable builds.
|
||||||
|
|||||||
@@ -3,8 +3,17 @@ Docker CE
|
|||||||
|
|
||||||
# Build a deb/rpm package
|
# Build a deb/rpm package
|
||||||
```
|
```
|
||||||
# builder/build-package <os> <os_codename>
|
# builder/build-package <os> <os_codename> <build_tag>
|
||||||
# os_codename is what "lsb_release -c" outputs, e.g. buster, focal.
|
# os_codename is what "lsb_release -c" outputs, e.g. buster, focal.
|
||||||
|
#
|
||||||
|
# build_tag allows building multiple versions of deb package (rpm not supported)
|
||||||
|
# targeting a single distro release (e.g. Ubuntu Bionic). If build_tag is given,
|
||||||
|
# the package name will include build_tag as part of Debian revision. For
|
||||||
|
# example:
|
||||||
|
# * with build_tag: kasmvncserver_0.9.1~beta-1+libjpeg-turbo-latest_amd64.deb
|
||||||
|
# * without build_tag: kasmvncserver_0.9.1~beta-1_amd64.deb
|
||||||
|
# You need to have .build and .deb.build for the build_tag.
|
||||||
|
#
|
||||||
# Packages will be placed under builder/build/
|
# Packages will be placed under builder/build/
|
||||||
|
|
||||||
builder/build-package ubuntu bionic
|
builder/build-package ubuntu bionic
|
||||||
@@ -70,3 +79,14 @@ packages installed with XFCE.
|
|||||||
```
|
```
|
||||||
builder/test-deb-barebones ubuntu focal
|
builder/test-deb-barebones ubuntu focal
|
||||||
```
|
```
|
||||||
|
# CI development
|
||||||
|
|
||||||
|
S3 upload code is extracted to various files in `.ci`. It's possible to iterate
|
||||||
|
locally by doing stuff like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
bash -c '
|
||||||
|
. .ci/upload.sh;
|
||||||
|
prepare_upload_filename "bionic/kasmvncserver_0.9.1~beta-1+libjpeg-turbo-latest_amd64.deb";
|
||||||
|
echo $upload_filename;'
|
||||||
|
```
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ set -e
|
|||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
. builder/os_ver_cli.sh
|
. builder/os_ver_cli.sh
|
||||||
|
|
||||||
docker build -t debbuilder_${os}:${os_codename} -f \
|
|
||||||
builder/dockerfile.${os}_${os_codename}.deb.build .
|
|
||||||
|
|
||||||
deb_output_dir=$(cd .. && echo $PWD)
|
|
||||||
L_UID=$(id -u) #Ubuntu already has UID env var, but this should work on all Linix systems
|
L_UID=$(id -u) #Ubuntu already has UID env var, but this should work on all Linix systems
|
||||||
L_GID=$(id -g)
|
L_GID=$(id -g)
|
||||||
docker run --rm -v "$deb_output_dir":/src --user $L_UID:$L_GID \
|
|
||||||
debbuilder_${os}:${os_codename} /bin/bash -c \
|
builder_image=debbuilder_${os}:${os_codename}${build_tag_for_images}
|
||||||
|
docker build --build-arg KASMVNC_PACKAGE_DIR="builder/build/${os_codename}" \
|
||||||
|
--build-arg L_UID="$L_UID" \
|
||||||
|
-t "$builder_image" -f \
|
||||||
|
builder/dockerfile.${os}_${os_codename}${build_tag}.deb.build .
|
||||||
|
|
||||||
|
deb_output_dir=$(cd .. && echo $PWD)
|
||||||
|
docker run --rm -v "$deb_output_dir":/src -e BUILD_TAG="$build_tag" \
|
||||||
|
--user "$L_UID:$L_GID" \
|
||||||
|
"$builder_image" /bin/bash -c \
|
||||||
'/src/*/builder/build-deb-inside-docker'
|
'/src/*/builder/build-deb-inside-docker'
|
||||||
|
|||||||
@@ -2,12 +2,31 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
prepare_docker_copy_of_debian_dir_and_cd_to_id() {
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
local mounted_src_dir="$PWD"
|
||||||
|
local docker_src_dir="$HOME/src"
|
||||||
|
|
||||||
|
mkdir "$docker_src_dir"
|
||||||
|
cd "$docker_src_dir"
|
||||||
|
cp -a "$mounted_src_dir/debian/" .
|
||||||
|
ln -s "$mounted_src_dir/builder" .
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_docker_copy_of_debian_dir_and_cd_to_id
|
||||||
|
|
||||||
os=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
|
os=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
|
||||||
os_codename=$(lsb_release -cs)
|
os_codename=$(lsb_release -cs)
|
||||||
os_dir="builder/build/${os_codename}"
|
os_dir="builder/build/${os_codename}"
|
||||||
|
|
||||||
|
if [ "$BUILD_TAG" = "+libjpeg-turbo_latest" ]; then
|
||||||
|
echo 'libjpeg 62 libjpeg-turbo (>= 2.1.1)' > debian/shlibs.local
|
||||||
|
|
||||||
|
debian_revision="$(echo $BUILD_TAG | tr _ -)"
|
||||||
|
sed -i -e "1 s/)/$debian_revision)/" debian/changelog
|
||||||
|
fi
|
||||||
|
|
||||||
dpkg-buildpackage -us -uc -b
|
dpkg-buildpackage -us -uc -b
|
||||||
mkdir -p "$os_dir"
|
mkdir -p "$os_dir"
|
||||||
cp ../*.deb "$os_dir"
|
cp ../*.deb "$os_dir"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ set -e
|
|||||||
|
|
||||||
os="$1"
|
os="$1"
|
||||||
codename="$2"
|
codename="$2"
|
||||||
|
build_tag="$3"
|
||||||
|
|
||||||
detect_package_format() {
|
detect_package_format() {
|
||||||
package_format=rpm
|
package_format=rpm
|
||||||
@@ -12,8 +13,17 @@ detect_package_format() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warn_build_tag_not_supported_for_rpm_and_exit() {
|
||||||
|
if [[ "$build_tag" && "$package_format" = "rpm" ]]; then
|
||||||
|
echo >&2 "<build_tag> isn't supported for rpm"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
detect_package_format
|
detect_package_format
|
||||||
builder/build-tarball "$os" "$codename"
|
warn_build_tag_not_supported_for_rpm_and_exit
|
||||||
builder/build-${package_format} "$os" "$codename"
|
|
||||||
|
builder/build-tarball "$os" "$codename" "$build_tag"
|
||||||
|
builder/build-${package_format} "$os" "$codename" "$build_tag"
|
||||||
|
|||||||
@@ -2,8 +2,18 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
warn_build_tag_not_supported_for_rpm_and_exit() {
|
||||||
|
local build_tag="$1"
|
||||||
|
|
||||||
|
if [[ -n "$build_tag" ]]; then
|
||||||
|
echo >&2 "<build_tag> isn't supported for rpm"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
. builder/os_ver_cli.sh
|
. builder/os_ver_cli.sh
|
||||||
|
warn_build_tag_not_supported_for_rpm_and_exit "$build_tag"
|
||||||
|
|
||||||
docker build -t kasmvnc_rpmbuilder_${os}:${os_codename} -f \
|
docker build -t kasmvnc_rpmbuilder_${os}:${os_codename} -f \
|
||||||
builder/dockerfile.${os}_${os_codename}.rpm.build .
|
builder/dockerfile.${os}_${os_codename}.rpm.build .
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ build_www_dir() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
move_libjpeg_turbo_to_os_specific_dir() {
|
||||||
|
chown $L_UID:$L_GID $shared_with_docker_dir/${os_codename}/*
|
||||||
|
mkdir -p $PWD/builder/build/${os_codename}/
|
||||||
|
mv $shared_with_docker_dir/${os_codename}/libjpeg-turbo*.deb \
|
||||||
|
$PWD/builder/build/${os_codename}/
|
||||||
|
}
|
||||||
|
|
||||||
shared_with_docker_dir=${GITLAB_SHARED_DIND_DIR:-/tmp}
|
shared_with_docker_dir=${GITLAB_SHARED_DIND_DIR:-/tmp}
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
@@ -19,13 +26,21 @@ cd "$(dirname "$0")/.."
|
|||||||
|
|
||||||
build_www_dir
|
build_www_dir
|
||||||
|
|
||||||
docker build -t kasmvncbuilder:$os_codename \
|
builder_image=kasmvncbuilder:$os_codename${build_tag_for_images}
|
||||||
-f builder/dockerfile.${os}_${os_codename}.build .
|
docker build -t "$builder_image" \
|
||||||
|
-f builder/dockerfile.${os}_${os_codename}${build_tag}.build .
|
||||||
mkdir -p builder/build
|
mkdir -p builder/build
|
||||||
docker run -v $shared_with_docker_dir:/build --rm kasmvncbuilder:$os_codename
|
docker run -v $shared_with_docker_dir:/build -e BUILD_TAG="$build_tag" \
|
||||||
|
--rm "$builder_image"
|
||||||
|
|
||||||
L_GID=$(id -g)
|
L_GID=$(id -g)
|
||||||
L_UID=$(id -u)
|
L_UID=$(id -u)
|
||||||
tarball_name="kasmvnc.${os}_${os_codename}.tar.gz"
|
tarball_name="kasmvnc.${os}_${os_codename}.tar.gz"
|
||||||
|
tarball_name_with_build_tag="kasmvnc.${os}_${os_codename}${build_tag}.tar.gz"
|
||||||
chown $L_UID:$L_GID $shared_with_docker_dir/$tarball_name
|
chown $L_UID:$L_GID $shared_with_docker_dir/$tarball_name
|
||||||
mv $shared_with_docker_dir/$tarball_name $PWD/builder/build/
|
mv $shared_with_docker_dir/$tarball_name \
|
||||||
|
$PWD/builder/build/"$tarball_name_with_build_tag"
|
||||||
|
|
||||||
|
if [ "$build_tag" = "+libjpeg-turbo_latest" ]; then
|
||||||
|
move_libjpeg_turbo_to_os_specific_dir
|
||||||
|
fi
|
||||||
|
|||||||
@@ -84,3 +84,7 @@ fi
|
|||||||
make servertarball
|
make servertarball
|
||||||
|
|
||||||
cp kasmvnc*.tar.gz /build/kasmvnc.${KASMVNC_BUILD_OS}_${KASMVNC_BUILD_OS_CODENAME}.tar.gz
|
cp kasmvnc*.tar.gz /build/kasmvnc.${KASMVNC_BUILD_OS}_${KASMVNC_BUILD_OS_CODENAME}.tar.gz
|
||||||
|
if [ "$BUILD_TAG" = "+libjpeg-turbo_latest" ]; then
|
||||||
|
mkdir -p /build/${KASMVNC_BUILD_OS_CODENAME}/
|
||||||
|
cp /libjpeg-turbo/libjpeg*.deb /build/${KASMVNC_BUILD_OS_CODENAME}/
|
||||||
|
fi
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
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 build-dep xorg-server libxfont-dev
|
||||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||||
|
|
||||||
# Additions for webp
|
# Additions for webp
|
||||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -9,4 +9,11 @@ RUN apt-get update && \
|
|||||||
COPY ./debian/control /tmp
|
COPY ./debian/control /tmp
|
||||||
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
||||||
|
|
||||||
USER 1000
|
ARG L_UID
|
||||||
|
RUN if [ "$L_UID" -eq 0 ]; then \
|
||||||
|
useradd -m docker; \
|
||||||
|
else \
|
||||||
|
useradd -m docker -u $L_UID;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER docker
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
|
|||||||
### START CUSTOM STUFF ####
|
### START CUSTOM STUFF ####
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
|
|
||||||
### END CUSTOM STUFF ###
|
### END CUSTOM STUFF ###
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
FROM debian:buster-slim
|
FROM debian:buster-slim
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
RUN apt-get update && apt-get -y install xterm
|
RUN apt-get update && apt-get -y install xterm
|
||||||
|
|
||||||
COPY startup/deb/kasmvncserver-easy-start /usr/local/bin
|
COPY startup/deb/kasmvncserver-easy-start /usr/local/bin
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
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 build-dep xorg-server libxfont-dev
|
||||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||||
|
|
||||||
# Additions for webp
|
# Additions for webp
|
||||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -9,4 +9,11 @@ RUN apt-get update && \
|
|||||||
COPY ./debian/control /tmp
|
COPY ./debian/control /tmp
|
||||||
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
||||||
|
|
||||||
USER 1000
|
ARG L_UID
|
||||||
|
RUN if [ "$L_UID" -eq 0 ]; then \
|
||||||
|
useradd -m docker; \
|
||||||
|
else \
|
||||||
|
useradd -m docker -u $L_UID;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER docker
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
|
|||||||
### START CUSTOM STUFF ####
|
### START CUSTOM STUFF ####
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
|
|
||||||
### END CUSTOM STUFF ###
|
### END CUSTOM STUFF ###
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
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 build-dep xorg-server libxfont-dev
|
||||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||||
|
|
||||||
# Additions for webp
|
# Additions for webp
|
||||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -9,4 +9,11 @@ RUN apt-get update && \
|
|||||||
COPY ./debian/control /tmp
|
COPY ./debian/control /tmp
|
||||||
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
||||||
|
|
||||||
USER 1000
|
ARG L_UID
|
||||||
|
RUN if [ "$L_UID" -eq 0 ]; then \
|
||||||
|
useradd -m docker; \
|
||||||
|
else \
|
||||||
|
useradd -m docker -u $L_UID;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER docker
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
|
|||||||
### START CUSTOM STUFF ####
|
### START CUSTOM STUFF ####
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
|
|
||||||
### END CUSTOM STUFF ###
|
### END CUSTOM STUFF ###
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ RUN cd /tmp/libwebp-1.0.2 && ./configure && make && make install
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
|
|||||||
39
builder/dockerfile.ubuntu_bionic+libjpeg-turbo_latest.build
Normal file
39
builder/dockerfile.ubuntu_bionic+libjpeg-turbo_latest.build
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
FROM ubuntu:18.04
|
||||||
|
|
||||||
|
ENV KASMVNC_BUILD_OS ubuntu
|
||||||
|
ENV KASMVNC_BUILD_OS_CODENAME bionic
|
||||||
|
ENV XORG_VER 1.20.10
|
||||||
|
|
||||||
|
RUN sed -i 's$# deb-src$deb-src$' /etc/apt/sources.list
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get -y install sudo
|
||||||
|
|
||||||
|
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
|
||||||
|
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y cmake nasm gcc
|
||||||
|
RUN git clone https://github.com/libjpeg-turbo/libjpeg-turbo.git
|
||||||
|
RUN export MAKEFLAGS=-j`nproc`; cd libjpeg-turbo && cmake -G"Unix Makefiles" && make deb
|
||||||
|
RUN export MAKEFLAGS=-j`nproc`; cd libjpeg-turbo && cmake -DCMAKE_INSTALL_PREFIX=/usr/local -G"Unix Makefiles" && make && make install
|
||||||
|
|
||||||
|
# Additions for webp
|
||||||
|
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
|
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
|
||||||
|
RUN cd /tmp/libwebp-1.0.2 && \
|
||||||
|
./configure --enable-static --disable-shared && \
|
||||||
|
make && make install
|
||||||
|
|
||||||
|
# Fix for older required libs
|
||||||
|
#RUN cd /tmp && wget http://launchpadlibrarian.net/347526424/libxfont1-dev_1.5.2-4ubuntu2_amd64.deb && \
|
||||||
|
# wget http://launchpadlibrarian.net/347526425/libxfont1_1.5.2-4ubuntu2_amd64.deb && \
|
||||||
|
# dpkg -i libxfont1_1.5.2-4ubuntu2_amd64.deb && \
|
||||||
|
# dpkg -i libxfont1-dev_1.5.2-4ubuntu2_amd64.deb
|
||||||
|
|
||||||
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
|
COPY --chown=docker:docker . /src
|
||||||
|
|
||||||
|
USER docker
|
||||||
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
FROM ubuntu:bionic
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get -y install vim build-essential devscripts equivs
|
||||||
|
|
||||||
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
|
COPY $KASMVNC_PACKAGE_DIR/libjpeg-turbo*deb /tmp
|
||||||
|
RUN apt-get install /tmp/libjpeg-turbo*deb
|
||||||
|
|
||||||
|
# Install build-deps for the package.
|
||||||
|
COPY ./debian/control /tmp
|
||||||
|
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
||||||
|
|
||||||
|
ENV LD_LIBRARY_PATH="/opt/libjpeg-turbo/lib64/:$LD_LIBRARY_PATH"
|
||||||
|
|
||||||
|
ARG L_UID
|
||||||
|
RUN if [ "$L_UID" -eq 0 ]; then \
|
||||||
|
useradd -m docker; \
|
||||||
|
else \
|
||||||
|
useradd -m docker -u $L_UID;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER docker
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
FROM ubuntu:bionic
|
||||||
|
|
||||||
|
ENV DISPLAY=:1 \
|
||||||
|
VNC_PORT=8443 \
|
||||||
|
VNC_RESOLUTION=1280x720 \
|
||||||
|
MAX_FRAME_RATE=24 \
|
||||||
|
VNCOPTIONS="-PreferBandwidth -DynamicQualityMin=4 -DynamicQualityMax=7" \
|
||||||
|
HOME=/home/user \
|
||||||
|
TERM=xterm \
|
||||||
|
STARTUPDIR=/dockerstartup \
|
||||||
|
INST_SCRIPTS=/dockerstartup/install \
|
||||||
|
KASM_RX_HOME=/dockerstartup/kasmrx \
|
||||||
|
DEBIAN_FRONTEND=noninteractive \
|
||||||
|
VNC_COL_DEPTH=24 \
|
||||||
|
VNC_RESOLUTION=1280x1024 \
|
||||||
|
VNC_PW=vncpassword \
|
||||||
|
VNC_USER=user \
|
||||||
|
VNC_VIEW_ONLY_PW=vncviewonlypassword \
|
||||||
|
LD_LIBRARY_PATH=/opt/libjpeg-turbo/lib64/:/usr/local/lib/ \
|
||||||
|
OMP_WAIT_POLICY=PASSIVE \
|
||||||
|
SHELL=/bin/bash \
|
||||||
|
SINGLE_APPLICATION=0 \
|
||||||
|
KASMVNC_BUILD_OS=ubuntu \
|
||||||
|
KASMVNC_BUILD_OS_CODENAME=bionic
|
||||||
|
|
||||||
|
EXPOSE $VNC_PORT
|
||||||
|
|
||||||
|
WORKDIR $HOME
|
||||||
|
|
||||||
|
### REQUIRED STUFF ###
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y supervisor xfce4 xfce4-terminal xterm libnss-wrapper gettext wget
|
||||||
|
RUN apt-get purge -y pm-utils xscreensaver*
|
||||||
|
RUN apt-get update && apt-get install -y vim less
|
||||||
|
RUN apt-get update && apt-get -y install lsb-release
|
||||||
|
|
||||||
|
RUN echo 'source $STARTUPDIR/generate_container_user' >> $HOME/.bashrc
|
||||||
|
|
||||||
|
RUN mkdir -p $STARTUPDIR
|
||||||
|
COPY startup/ $STARTUPDIR
|
||||||
|
|
||||||
|
### START CUSTOM STUFF ####
|
||||||
|
|
||||||
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
|
COPY $KASMVNC_PACKAGE_DIR/libjpeg-turbo_*.deb /tmp
|
||||||
|
RUN apt-get install /tmp/libjpeg-turbo*deb
|
||||||
|
|
||||||
|
ARG BUILD_DEBIAN_REVISION
|
||||||
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*$BUILD_DEBIAN_REVISION*.deb /tmp
|
||||||
|
RUN dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
|
|
||||||
|
### END CUSTOM STUFF ###
|
||||||
|
|
||||||
|
RUN chown -R 1000:0 $HOME
|
||||||
|
USER 1000:ssl-cert
|
||||||
|
WORKDIR $HOME
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/dockerstartup/vnc_startup.sh" ]
|
||||||
@@ -11,7 +11,7 @@ RUN apt-get update && \
|
|||||||
|
|
||||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||||
|
|
||||||
# Additions for webp
|
# Additions for webp
|
||||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -28,8 +28,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -7,4 +7,11 @@ RUN apt-get update && \
|
|||||||
COPY ./debian/control /tmp
|
COPY ./debian/control /tmp
|
||||||
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
||||||
|
|
||||||
USER 1000
|
ARG L_UID
|
||||||
|
RUN if [ "$L_UID" -eq 0 ]; then \
|
||||||
|
useradd -m docker; \
|
||||||
|
else \
|
||||||
|
useradd -m docker -u $L_UID;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER docker
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
|
|||||||
### START CUSTOM STUFF ####
|
### START CUSTOM STUFF ####
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
|
|
||||||
### END CUSTOM STUFF ###
|
### END CUSTOM STUFF ###
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
FROM ubuntu:focal
|
FROM ubuntu:focal
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
RUN apt-get update && apt-get -y install xterm lsb-release
|
RUN apt-get update && apt-get -y install xterm lsb-release
|
||||||
|
|
||||||
RUN useradd -m foo && addgroup foo ssl-cert
|
RUN useradd -m foo && addgroup foo ssl-cert
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
|
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 build-dep xorg-server libxfont-dev
|
||||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||||
|
|
||||||
# Additions for webp
|
# Additions for webp
|
||||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -9,4 +9,11 @@ RUN apt-get update && \
|
|||||||
COPY ./debian/control /tmp
|
COPY ./debian/control /tmp
|
||||||
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
|
||||||
|
|
||||||
USER 1000
|
ARG L_UID
|
||||||
|
RUN if [ "$L_UID" -eq 0 ]; then \
|
||||||
|
useradd -m docker; \
|
||||||
|
else \
|
||||||
|
useradd -m docker -u $L_UID;\
|
||||||
|
fi
|
||||||
|
|
||||||
|
USER docker
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
|
|||||||
### START CUSTOM STUFF ####
|
### START CUSTOM STUFF ####
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
|
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
|
||||||
RUN dpkg -i /tmp/*.deb; apt-get -yf install
|
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xfce4-session &' >> ~/.vnc/xstartup && \
|
RUN mkdir ~/.vnc && echo '/usr/bin/xfce4-session &' >> ~/.vnc/xstartup && \
|
||||||
chmod +x ~/.vnc/xstartup
|
chmod +x ~/.vnc/xstartup
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ default_os_codename=bionic
|
|||||||
|
|
||||||
os=${1:-$default_os}
|
os=${1:-$default_os}
|
||||||
os_codename=${2:-$default_os_codename}
|
os_codename=${2:-$default_os_codename}
|
||||||
|
build_tag="${3:-}"
|
||||||
|
if [[ -n "$build_tag" ]]; then
|
||||||
|
build_tag_for_images="_${build_tag#+}"
|
||||||
|
build_debian_revision="$(echo $build_tag | tr _ -)"
|
||||||
|
fi
|
||||||
os_image="$os:$os_codename"
|
os_image="$os:$os_codename"
|
||||||
|
|
||||||
echo "Building for $os_image"
|
echo "Building for $os_image"
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ set -e
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
. ./os_ver_cli.sh
|
. ./os_ver_cli.sh
|
||||||
|
|
||||||
|
tester_image=kasmvnctester_${os}:$os_codename${build_tag_for_images}
|
||||||
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
|
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
|
||||||
-t kasmvnctester_${os}:$os_codename \
|
--build-arg BUILD_DEBIAN_REVISION="$build_debian_revision" \
|
||||||
-f dockerfile.${os}_${os_codename}.deb.test .
|
-t "$tester_image" \
|
||||||
|
-f dockerfile.${os}_${os_codename}${build_tag}.deb.test .
|
||||||
docker run -it -p 443:8443 --rm \
|
docker run -it -p 443:8443 --rm \
|
||||||
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
|
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
|
||||||
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
|
||||||
kasmvnctester_${os}:$os_codename
|
"$tester_image"
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include <rfb/PixelBuffer.h>
|
#include <rfb/PixelBuffer.h>
|
||||||
#include <rfb/PixelFormat.h>
|
#include <rfb/PixelFormat.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace network {
|
namespace network {
|
||||||
@@ -34,6 +36,17 @@ namespace network {
|
|||||||
|
|
||||||
// from main thread
|
// from main thread
|
||||||
void mainUpdateScreen(rfb::PixelBuffer *pb);
|
void mainUpdateScreen(rfb::PixelBuffer *pb);
|
||||||
|
void mainUpdateBottleneckStats(const char userid[], const char stats[]);
|
||||||
|
void mainClearBottleneckStats(const char userid[]);
|
||||||
|
void mainUpdateServerFrameStats(uint8_t changedPerc, uint32_t all,
|
||||||
|
uint32_t jpeg, uint32_t webp, uint32_t analysis,
|
||||||
|
uint32_t jpegarea, uint32_t webparea,
|
||||||
|
uint16_t njpeg, uint16_t nwebp,
|
||||||
|
uint16_t enc, uint16_t scale, uint16_t shot,
|
||||||
|
uint16_t w, uint16_t h);
|
||||||
|
void mainUpdateClientFrameStats(const char userid[], uint32_t render, uint32_t all,
|
||||||
|
uint32_t ping);
|
||||||
|
void mainUpdateUserInfo(const uint8_t ownerConn, const uint8_t numUsers);
|
||||||
|
|
||||||
// from network threads
|
// from network threads
|
||||||
uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
|
uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
|
||||||
@@ -42,13 +55,25 @@ namespace network {
|
|||||||
uint8_t netAddUser(const char name[], const char pw[], const bool write);
|
uint8_t netAddUser(const char name[], const char pw[], const bool write);
|
||||||
uint8_t netRemoveUser(const char name[]);
|
uint8_t netRemoveUser(const char name[]);
|
||||||
uint8_t netGiveControlTo(const char name[]);
|
uint8_t netGiveControlTo(const char name[]);
|
||||||
|
void netGetBottleneckStats(char *buf, uint32_t len);
|
||||||
|
void netGetFrameStats(char *buf, uint32_t len);
|
||||||
|
uint8_t netServerFrameStatsReady();
|
||||||
|
|
||||||
enum USER_ACTION {
|
enum USER_ACTION {
|
||||||
//USER_ADD, - handled locally for interactivity
|
//USER_ADD, - handled locally for interactivity
|
||||||
USER_REMOVE,
|
USER_REMOVE,
|
||||||
USER_GIVE_CONTROL,
|
USER_GIVE_CONTROL,
|
||||||
|
WANT_FRAME_STATS_SERVERONLY,
|
||||||
|
WANT_FRAME_STATS_ALL,
|
||||||
|
WANT_FRAME_STATS_OWNER,
|
||||||
|
WANT_FRAME_STATS_SPECIFIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint8_t netRequestFrameStats(USER_ACTION what, const char *client);
|
||||||
|
uint8_t netOwnerConnected();
|
||||||
|
uint8_t netNumActiveUsers();
|
||||||
|
uint8_t netGetClientFrameStatsNum();
|
||||||
|
|
||||||
struct action_data {
|
struct action_data {
|
||||||
enum USER_ACTION action;
|
enum USER_ACTION action;
|
||||||
kasmpasswd_entry_t data;
|
kasmpasswd_entry_t data;
|
||||||
@@ -68,6 +93,40 @@ namespace network {
|
|||||||
std::vector<uint8_t> cachedJpeg;
|
std::vector<uint8_t> cachedJpeg;
|
||||||
uint16_t cachedW, cachedH;
|
uint16_t cachedW, cachedH;
|
||||||
uint8_t cachedQ;
|
uint8_t cachedQ;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> bottleneckStats;
|
||||||
|
pthread_mutex_t statMutex;
|
||||||
|
|
||||||
|
struct clientFrameStats_t {
|
||||||
|
uint32_t render;
|
||||||
|
uint32_t all;
|
||||||
|
uint32_t ping;
|
||||||
|
};
|
||||||
|
struct serverFrameStats_t {
|
||||||
|
uint32_t all;
|
||||||
|
uint32_t jpeg;
|
||||||
|
uint32_t webp;
|
||||||
|
uint32_t analysis;
|
||||||
|
uint32_t jpegarea;
|
||||||
|
uint32_t webparea;
|
||||||
|
uint16_t njpeg;
|
||||||
|
uint16_t nwebp;
|
||||||
|
uint16_t enc;
|
||||||
|
uint16_t scale;
|
||||||
|
uint16_t shot;
|
||||||
|
uint16_t w;
|
||||||
|
uint16_t h;
|
||||||
|
uint8_t changedPerc;
|
||||||
|
|
||||||
|
uint8_t inprogress;
|
||||||
|
};
|
||||||
|
std::map<std::string, clientFrameStats_t> clientFrameStats;
|
||||||
|
serverFrameStats_t serverFrameStats;
|
||||||
|
pthread_mutex_t frameStatMutex;
|
||||||
|
|
||||||
|
uint8_t ownerConnected;
|
||||||
|
uint8_t activeUsers;
|
||||||
|
pthread_mutex_t userInfoMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <network/GetAPI.h>
|
#include <network/GetAPI.h>
|
||||||
#include <rfb/ConnParams.h>
|
#include <rfb/ConnParams.h>
|
||||||
|
#include <rfb/EncodeManager.h>
|
||||||
#include <rfb/LogWriter.h>
|
#include <rfb/LogWriter.h>
|
||||||
#include <rfb/JpegCompressor.h>
|
#include <rfb/JpegCompressor.h>
|
||||||
#include <rfb/xxhash.h>
|
#include <rfb/xxhash.h>
|
||||||
@@ -32,10 +33,6 @@ using namespace rfb;
|
|||||||
|
|
||||||
static LogWriter vlog("GetAPIMessager");
|
static LogWriter vlog("GetAPIMessager");
|
||||||
|
|
||||||
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
|
|
||||||
const uint16_t tgtw, const uint16_t tgth,
|
|
||||||
const float tgtdiff);
|
|
||||||
|
|
||||||
struct TightJPEGConfiguration {
|
struct TightJPEGConfiguration {
|
||||||
int quality;
|
int quality;
|
||||||
int subsampling;
|
int subsampling;
|
||||||
@@ -56,10 +53,16 @@ static const struct TightJPEGConfiguration conf[10] = {
|
|||||||
|
|
||||||
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
|
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
|
||||||
screenW(0), screenH(0), screenHash(0),
|
screenW(0), screenH(0), screenHash(0),
|
||||||
cachedW(0), cachedH(0), cachedQ(0) {
|
cachedW(0), cachedH(0), cachedQ(0),
|
||||||
|
ownerConnected(0), activeUsers(0) {
|
||||||
|
|
||||||
pthread_mutex_init(&screenMutex, NULL);
|
pthread_mutex_init(&screenMutex, NULL);
|
||||||
pthread_mutex_init(&userMutex, NULL);
|
pthread_mutex_init(&userMutex, NULL);
|
||||||
|
pthread_mutex_init(&statMutex, NULL);
|
||||||
|
pthread_mutex_init(&frameStatMutex, NULL);
|
||||||
|
pthread_mutex_init(&userInfoMutex, NULL);
|
||||||
|
|
||||||
|
serverFrameStats.inprogress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from main thread
|
// from main thread
|
||||||
@@ -95,6 +98,78 @@ void GetAPIMessager::mainUpdateScreen(rfb::PixelBuffer *pb) {
|
|||||||
pthread_mutex_unlock(&screenMutex);
|
pthread_mutex_unlock(&screenMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateBottleneckStats(const char userid[], const char stats[]) {
|
||||||
|
if (pthread_mutex_trylock(&statMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bottleneckStats[userid] = stats;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&statMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainClearBottleneckStats(const char userid[]) {
|
||||||
|
if (pthread_mutex_lock(&statMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bottleneckStats.erase(userid);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&statMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateServerFrameStats(uint8_t changedPerc,
|
||||||
|
uint32_t all, uint32_t jpeg, uint32_t webp, uint32_t analysis,
|
||||||
|
uint32_t jpegarea, uint32_t webparea,
|
||||||
|
uint16_t njpeg, uint16_t nwebp,
|
||||||
|
uint16_t enc, uint16_t scale, uint16_t shot,
|
||||||
|
uint16_t w, uint16_t h) {
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
serverFrameStats.changedPerc = changedPerc;
|
||||||
|
serverFrameStats.all = all;
|
||||||
|
serverFrameStats.jpeg = jpeg;
|
||||||
|
serverFrameStats.webp = webp;
|
||||||
|
serverFrameStats.analysis = analysis;
|
||||||
|
serverFrameStats.jpegarea = jpegarea;
|
||||||
|
serverFrameStats.webparea = webparea;
|
||||||
|
serverFrameStats.njpeg = njpeg;
|
||||||
|
serverFrameStats.nwebp = nwebp;
|
||||||
|
serverFrameStats.enc = enc;
|
||||||
|
serverFrameStats.scale = scale;
|
||||||
|
serverFrameStats.shot = shot;
|
||||||
|
serverFrameStats.w = w;
|
||||||
|
serverFrameStats.h = h;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateClientFrameStats(const char userid[], uint32_t render,
|
||||||
|
uint32_t all, uint32_t ping) {
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
clientFrameStats_t s;
|
||||||
|
s.render = render;
|
||||||
|
s.all = all;
|
||||||
|
s.ping = ping;
|
||||||
|
|
||||||
|
clientFrameStats[userid] = s;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateUserInfo(const uint8_t ownerConn, const uint8_t numUsers) {
|
||||||
|
if (pthread_mutex_lock(&userInfoMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ownerConnected = ownerConn;
|
||||||
|
activeUsers = numUsers;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userInfoMutex);
|
||||||
|
}
|
||||||
|
|
||||||
// from network threads
|
// from network threads
|
||||||
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
|
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
|
||||||
const uint8_t q, const bool dedup,
|
const uint8_t q, const bool dedup,
|
||||||
@@ -286,3 +361,271 @@ uint8_t GetAPIMessager::netGiveControlTo(const char name[]) {
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::netGetBottleneckStats(char *buf, uint32_t len) {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"username.1": {
|
||||||
|
"192.168.100.2:14908": [ 100, 100, 100, 100 ],
|
||||||
|
"192.168.100.3:14918": [ 100, 100, 100, 100 ]
|
||||||
|
},
|
||||||
|
"username.2": {
|
||||||
|
"192.168.100.5:14904": [ 100, 100, 100, 100 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
const char *prev = NULL;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&statMutex)) {
|
||||||
|
buf[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conservative estimate
|
||||||
|
if (len < bottleneckStats.size() * 60) {
|
||||||
|
buf[0] = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fmemopen(buf, len, "w");
|
||||||
|
|
||||||
|
fprintf(f, "{\n");
|
||||||
|
|
||||||
|
for (it = bottleneckStats.begin(); it != bottleneckStats.end(); it++) {
|
||||||
|
// user@127.0.0.1_1627311208.791752::websocket
|
||||||
|
const char *id = it->first.c_str();
|
||||||
|
const char *data = it->second.c_str();
|
||||||
|
|
||||||
|
const char *at = strchr(id, '@');
|
||||||
|
if (!at)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const unsigned userlen = at - id;
|
||||||
|
if (prev && !strncmp(prev, id, userlen)) {
|
||||||
|
// Same user
|
||||||
|
fprintf(f, ",\n\t\t\"%s\": %s", at + 1, data);
|
||||||
|
} else {
|
||||||
|
// New one
|
||||||
|
if (prev) {
|
||||||
|
fprintf(f, "\n\t},\n");
|
||||||
|
}
|
||||||
|
fprintf(f, "\t\"%.*s\": {\n", userlen, id);
|
||||||
|
fprintf(f, "\t\t\"%s\": %s", at + 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bottleneckStats.size())
|
||||||
|
fprintf(f, "}\n");
|
||||||
|
else
|
||||||
|
fprintf(f, "\n\t}\n}\n");
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&statMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::netGetFrameStats(char *buf, uint32_t len) {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"frame" : {
|
||||||
|
"resx": 1024,
|
||||||
|
"resy": 1280,
|
||||||
|
"changed": 75,
|
||||||
|
"server_time": 23
|
||||||
|
},
|
||||||
|
"server_side" : [
|
||||||
|
{ "process_name": "Analysis", "time": 20 },
|
||||||
|
{ "process_name": "TightWEBPEncoder", "time": 20, "count": 64, "area": 12 },
|
||||||
|
{ "process_name": "TightJPEGEncoder", "time": 20, "count": 64, "area": 12 }
|
||||||
|
],
|
||||||
|
"client_side" : [
|
||||||
|
{
|
||||||
|
"client": "123.1.2.1:1211",
|
||||||
|
"client_time": 20,
|
||||||
|
"ping": 20,
|
||||||
|
"processes" : [
|
||||||
|
{ "process_name": "scanRenderQ", "time": 20 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
std::map<std::string, clientFrameStats_t>::const_iterator it;
|
||||||
|
unsigned i = 0;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex)) {
|
||||||
|
buf[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned num = clientFrameStats.size();
|
||||||
|
|
||||||
|
// Conservative estimate
|
||||||
|
if (len < 1024) {
|
||||||
|
buf[0] = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fmemopen(buf, len, "w");
|
||||||
|
|
||||||
|
fprintf(f, "{\n");
|
||||||
|
|
||||||
|
fprintf(f, "\t\"frame\" : {\n"
|
||||||
|
"\t\t\"resx\": %u,\n"
|
||||||
|
"\t\t\"resy\": %u,\n"
|
||||||
|
"\t\t\"changed\": %u,\n"
|
||||||
|
"\t\t\"server_time\": %u\n"
|
||||||
|
"\t},\n",
|
||||||
|
serverFrameStats.w,
|
||||||
|
serverFrameStats.h,
|
||||||
|
serverFrameStats.changedPerc,
|
||||||
|
serverFrameStats.all);
|
||||||
|
|
||||||
|
fprintf(f, "\t\"server_side\" : [\n"
|
||||||
|
"\t\t{ \"process_name\": \"Analysis\", \"time\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"Screenshot\", \"time\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"Encoding_total\", \"time\": %u, \"videoscaling\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"TightJPEGEncoder\", \"time\": %u, \"count\": %u, \"area\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"TightWEBPEncoder\", \"time\": %u, \"count\": %u, \"area\": %u }\n"
|
||||||
|
"\t],\n",
|
||||||
|
serverFrameStats.analysis,
|
||||||
|
serverFrameStats.shot,
|
||||||
|
serverFrameStats.enc,
|
||||||
|
serverFrameStats.scale,
|
||||||
|
serverFrameStats.jpeg,
|
||||||
|
serverFrameStats.njpeg,
|
||||||
|
serverFrameStats.jpegarea,
|
||||||
|
serverFrameStats.webp,
|
||||||
|
serverFrameStats.nwebp,
|
||||||
|
serverFrameStats.webparea);
|
||||||
|
|
||||||
|
fprintf(f, "\t\"client_side\" : [\n");
|
||||||
|
|
||||||
|
for (it = clientFrameStats.begin(); it != clientFrameStats.end(); it++, i++) {
|
||||||
|
const char *id = it->first.c_str();
|
||||||
|
const clientFrameStats_t &s = it->second;
|
||||||
|
|
||||||
|
fprintf(f, "\t\t\{\n"
|
||||||
|
"\t\t\t\"client\": \"%s\",\n"
|
||||||
|
"\t\t\t\"client_time\": %u,\n"
|
||||||
|
"\t\t\t\"ping\": %u,\n"
|
||||||
|
"\t\t\t\"processes\" : [\n"
|
||||||
|
"\t\t\t\t{ \"process_name\": \"scanRenderQ\", \"time\": %u }\n"
|
||||||
|
"\t\t\t]\n"
|
||||||
|
"\t\t}",
|
||||||
|
id,
|
||||||
|
s.all,
|
||||||
|
s.ping,
|
||||||
|
s.render);
|
||||||
|
|
||||||
|
if (i == num - 1)
|
||||||
|
fprintf(f, "\n");
|
||||||
|
else
|
||||||
|
fprintf(f, ",\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\t]\n}\n");
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
serverFrameStats.inprogress = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netRequestFrameStats(USER_ACTION what, const char *client) {
|
||||||
|
// Return 1 for success
|
||||||
|
action_data act;
|
||||||
|
act.action = what;
|
||||||
|
if (client) {
|
||||||
|
strncpy(act.data.password, client, PASSWORD_LEN);
|
||||||
|
act.data.password[PASSWORD_LEN - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// In progress already?
|
||||||
|
bool fail = false;
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (serverFrameStats.inprogress) {
|
||||||
|
fail = true;
|
||||||
|
vlog.error("Frame stats request already in progress, refusing another");
|
||||||
|
} else {
|
||||||
|
clientFrameStats.clear();
|
||||||
|
memset(&serverFrameStats, 0, sizeof(serverFrameStats_t));
|
||||||
|
serverFrameStats.inprogress = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
if (fail)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Send it in
|
||||||
|
if (pthread_mutex_lock(&userMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
actionQueue.push_back(act);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userMutex);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netOwnerConnected() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userInfoMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = ownerConnected;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userInfoMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netNumActiveUsers() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userInfoMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = activeUsers;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userInfoMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netGetClientFrameStatsNum() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = clientFrameStats.size();
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netServerFrameStatsReady() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = serverFrameStats.w != 0;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
#include <network/GetAPI.h>
|
#include <network/GetAPI.h>
|
||||||
@@ -459,6 +461,67 @@ static uint8_t givecontrolCb(void *messager, const char name[])
|
|||||||
return msgr->netGiveControlTo(name);
|
return msgr->netGiveControlTo(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bottleneckStatsCb(void *messager, char *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
msgr->netGetBottleneckStats(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frameStatsCb(void *messager, char *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
msgr->netGetFrameStats(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsNoneCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_SERVERONLY, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsOwnerCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_OWNER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsAllCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_ALL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsOneCb(void *messager, const char *client)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_SPECIFIC, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ownerConnectedCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netOwnerConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t numActiveUsersCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netNumActiveUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t getClientFrameStatsNumCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netGetClientFrameStatsNum();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t serverFrameStatsReadyCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netServerFrameStatsReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
||||||
socklen_t listenaddrlen,
|
socklen_t listenaddrlen,
|
||||||
bool sslonly, const char *cert, const char *certkey,
|
bool sslonly, const char *cert, const char *certkey,
|
||||||
@@ -503,7 +566,7 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
|
if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
|
||||||
int e = errorNumber;
|
int e = errorNumber;
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
throw SocketException("failed to bind socket", e);
|
throw SocketException("failed to bind socket, is someone else on our -websocketPort?", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(sock); // sets the internal fd
|
listen(sock); // sets the internal fd
|
||||||
@@ -513,13 +576,16 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
//
|
//
|
||||||
internalSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
internalSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
char sockname[32];
|
||||||
|
sprintf(sockname, ".KasmVNCSock%u", getpid());
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
strcpy(addr.sun_path, sockname);
|
||||||
addr.sun_path[0] = '\0';
|
addr.sun_path[0] = '\0';
|
||||||
|
|
||||||
if (bind(internalSocket, (struct sockaddr *) &addr,
|
if (bind(internalSocket, (struct sockaddr *) &addr,
|
||||||
sizeof(sa_family_t) + sizeof(".KasmVNCSock"))) {
|
sizeof(sa_family_t) + strlen(sockname))) {
|
||||||
throw SocketException("failed to bind socket", errorNumber);
|
throw SocketException("failed to bind socket", errorNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,6 +614,18 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
settings.adduserCb = adduserCb;
|
settings.adduserCb = adduserCb;
|
||||||
settings.removeCb = removeCb;
|
settings.removeCb = removeCb;
|
||||||
settings.givecontrolCb = givecontrolCb;
|
settings.givecontrolCb = givecontrolCb;
|
||||||
|
settings.bottleneckStatsCb = bottleneckStatsCb;
|
||||||
|
settings.frameStatsCb = frameStatsCb;
|
||||||
|
|
||||||
|
settings.requestFrameStatsNoneCb = requestFrameStatsNoneCb;
|
||||||
|
settings.requestFrameStatsOwnerCb = requestFrameStatsOwnerCb;
|
||||||
|
settings.requestFrameStatsAllCb = requestFrameStatsAllCb;
|
||||||
|
settings.requestFrameStatsOneCb = requestFrameStatsOneCb;
|
||||||
|
|
||||||
|
settings.ownerConnectedCb = ownerConnectedCb;
|
||||||
|
settings.numActiveUsersCb = numActiveUsersCb;
|
||||||
|
settings.getClientFrameStatsNumCb = getClientFrameStatsNumCb;
|
||||||
|
settings.serverFrameStatsReadyCb = serverFrameStatsReadyCb;
|
||||||
|
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
pthread_create(&tid, NULL, start_server, NULL);
|
pthread_create(&tid, NULL, start_server, NULL);
|
||||||
|
|||||||
@@ -583,7 +583,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
headers->host[end-start] = '\0';
|
headers->host[end-start] = '\0';
|
||||||
|
|
||||||
headers->origin[0] = '\0';
|
headers->origin[0] = '\0';
|
||||||
start = strstr(handshake, "\r\nOrigin: ");
|
start = strcasestr(handshake, "\r\nOrigin: ");
|
||||||
if (start) {
|
if (start) {
|
||||||
start += 10;
|
start += 10;
|
||||||
} else {
|
} else {
|
||||||
@@ -1074,6 +1074,89 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|||||||
|
|
||||||
wserr("Passed give_control request to main thread\n");
|
wserr("Passed give_control request to main thread\n");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
} else entry("/api/get_bottleneck_stats") {
|
||||||
|
char statbuf[4096];
|
||||||
|
settings.bottleneckStatsCb(settings.messager, statbuf, 4096);
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: %lu\r\n"
|
||||||
|
"\r\n", strlen(statbuf));
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
ws_send(ws_ctx, statbuf, strlen(statbuf));
|
||||||
|
|
||||||
|
wserr("Sent bottleneck stats to API caller\n");
|
||||||
|
ret = 1;
|
||||||
|
} else entry("/api/get_frame_stats") {
|
||||||
|
char statbuf[4096], decname[1024];
|
||||||
|
unsigned waitfor;
|
||||||
|
|
||||||
|
param = parse_get(args, "client", &len);
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, param, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
percent_decode(buf, decname);
|
||||||
|
} else {
|
||||||
|
wserr("client param required\n");
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decname[0])
|
||||||
|
goto nope;
|
||||||
|
|
||||||
|
if (!strcmp(decname, "none")) {
|
||||||
|
waitfor = 0;
|
||||||
|
if (!settings.requestFrameStatsNoneCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
} else if (!strcmp(decname, "auto")) {
|
||||||
|
waitfor = settings.ownerConnectedCb(settings.messager);
|
||||||
|
if (!waitfor) {
|
||||||
|
if (!settings.requestFrameStatsNoneCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
} else {
|
||||||
|
if (!settings.requestFrameStatsOwnerCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(decname, "all")) {
|
||||||
|
waitfor = settings.numActiveUsersCb(settings.messager);
|
||||||
|
if (!settings.requestFrameStatsAllCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
} else {
|
||||||
|
waitfor = 1;
|
||||||
|
if (!settings.requestFrameStatsOneCb(settings.messager, decname))
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
usleep(10 * 1000);
|
||||||
|
if (settings.serverFrameStatsReadyCb(settings.messager))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitfor) {
|
||||||
|
unsigned waits;
|
||||||
|
for (waits = 0; waits < 20; waits++) { // wait up to 2s
|
||||||
|
if (settings.getClientFrameStatsNumCb(settings.messager) >= waitfor)
|
||||||
|
break;
|
||||||
|
usleep(100 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.frameStatsCb(settings.messager, statbuf, 4096);
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: %lu\r\n"
|
||||||
|
"\r\n", strlen(statbuf));
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
ws_send(ws_ctx, statbuf, strlen(statbuf));
|
||||||
|
|
||||||
|
wserr("Sent frame stats to API caller\n");
|
||||||
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef entry
|
#undef entry
|
||||||
|
|||||||
@@ -84,6 +84,18 @@ typedef struct {
|
|||||||
const uint8_t write);
|
const uint8_t write);
|
||||||
uint8_t (*removeCb)(void *messager, const char name[]);
|
uint8_t (*removeCb)(void *messager, const char name[]);
|
||||||
uint8_t (*givecontrolCb)(void *messager, const char name[]);
|
uint8_t (*givecontrolCb)(void *messager, const char name[]);
|
||||||
|
void (*bottleneckStatsCb)(void *messager, char *buf, uint32_t len);
|
||||||
|
void (*frameStatsCb)(void *messager, char *buf, uint32_t len);
|
||||||
|
|
||||||
|
uint8_t (*requestFrameStatsNoneCb)(void *messager);
|
||||||
|
uint8_t (*requestFrameStatsOwnerCb)(void *messager);
|
||||||
|
uint8_t (*requestFrameStatsAllCb)(void *messager);
|
||||||
|
uint8_t (*requestFrameStatsOneCb)(void *messager, const char *client);
|
||||||
|
|
||||||
|
uint8_t (*ownerConnectedCb)(void *messager);
|
||||||
|
uint8_t (*numActiveUsersCb)(void *messager);
|
||||||
|
uint8_t (*getClientFrameStatsNumCb)(void *messager);
|
||||||
|
uint8_t (*serverFrameStatsReadyCb)(void *messager);
|
||||||
} settings_t;
|
} settings_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -223,9 +224,12 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
|||||||
|
|
||||||
void proxy_handler(ws_ctx_t *ws_ctx) {
|
void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||||
|
|
||||||
|
char sockname[32];
|
||||||
|
sprintf(sockname, ".KasmVNCSock%u", getpid());
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
strcpy(addr.sun_path, sockname);
|
||||||
addr.sun_path[0] = '\0';
|
addr.sun_path[0] = '\0';
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@@ -243,7 +247,7 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
|||||||
handler_msg("connecting to VNC target\n");
|
handler_msg("connecting to VNC target\n");
|
||||||
|
|
||||||
if (connect(tsock, (struct sockaddr *) &addr,
|
if (connect(tsock, (struct sockaddr *) &addr,
|
||||||
sizeof(sa_family_t) + sizeof(".KasmVNCSock")) < 0) {
|
sizeof(sa_family_t) + strlen(sockname)) < 0) {
|
||||||
|
|
||||||
handler_emsg("Could not connect to target: %s\n",
|
handler_emsg("Could not connect to target: %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ set(RFB_SOURCES
|
|||||||
Security.cxx
|
Security.cxx
|
||||||
SecurityServer.cxx
|
SecurityServer.cxx
|
||||||
SecurityClient.cxx
|
SecurityClient.cxx
|
||||||
|
SelfBench.cxx
|
||||||
SSecurityPlain.cxx
|
SSecurityPlain.cxx
|
||||||
SSecurityStack.cxx
|
SSecurityStack.cxx
|
||||||
SSecurityVncAuth.cxx
|
SSecurityVncAuth.cxx
|
||||||
@@ -58,19 +59,14 @@ set(RFB_SOURCES
|
|||||||
TightEncoder.cxx
|
TightEncoder.cxx
|
||||||
TightJPEGEncoder.cxx
|
TightJPEGEncoder.cxx
|
||||||
TightWEBPEncoder.cxx
|
TightWEBPEncoder.cxx
|
||||||
TightX264Encoder.cxx
|
|
||||||
UpdateTracker.cxx
|
UpdateTracker.cxx
|
||||||
VNCSConnectionST.cxx
|
VNCSConnectionST.cxx
|
||||||
VNCServerST.cxx
|
VNCServerST.cxx
|
||||||
ZRLEEncoder.cxx
|
ZRLEEncoder.cxx
|
||||||
ZRLEDecoder.cxx
|
ZRLEDecoder.cxx
|
||||||
|
cpuid.cxx
|
||||||
encodings.cxx
|
encodings.cxx
|
||||||
util.cxx
|
util.cxx
|
||||||
mp4.c
|
|
||||||
mp4_bitbuf.c
|
|
||||||
mp4_moov.c
|
|
||||||
mp4_moof.c
|
|
||||||
nvidia.cxx
|
|
||||||
xxhash.c)
|
xxhash.c)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
@@ -102,6 +98,27 @@ if(GNUTLS_FOUND)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# SSE2
|
||||||
|
|
||||||
|
set(SSE2_SOURCES
|
||||||
|
scale_sse2.cxx)
|
||||||
|
|
||||||
|
set(SCALE_DUMMY_SOURCES
|
||||||
|
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()
|
||||||
|
|
||||||
add_library(rfb STATIC ${RFB_SOURCES})
|
add_library(rfb STATIC ${RFB_SOURCES})
|
||||||
|
|
||||||
target_link_libraries(rfb ${RFB_LIBRARIES})
|
target_link_libraries(rfb ${RFB_LIBRARIES})
|
||||||
|
|||||||
@@ -695,6 +695,8 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
|
|||||||
std::vector<Rect> rects;
|
std::vector<Rect> rects;
|
||||||
std::vector<Rect>::iterator i;
|
std::vector<Rect>::iterator i;
|
||||||
|
|
||||||
|
changedPerc = 100;
|
||||||
|
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -749,8 +751,13 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
|
|||||||
for (i = rects.begin(); i != rects.end(); i++)
|
for (i = rects.begin(); i != rects.end(); i++)
|
||||||
totalPixels += i->area();
|
totalPixels += i->area();
|
||||||
newChanged.get_rects(&rects);
|
newChanged.get_rects(&rects);
|
||||||
for (i = rects.begin(); i != rects.end(); i++)
|
unsigned newchangedarea = 0;
|
||||||
|
for (i = rects.begin(); i != rects.end(); i++) {
|
||||||
missedPixels += i->area();
|
missedPixels += i->area();
|
||||||
|
newchangedarea += i->area();
|
||||||
|
}
|
||||||
|
|
||||||
|
changedPerc = newchangedarea * 100 / fb->area();
|
||||||
|
|
||||||
if (changed.equals(newChanged))
|
if (changed.equals(newChanged))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ namespace rfb {
|
|||||||
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
|
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
||||||
|
rdr::U8 changedPerc;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
|
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
|
||||||
PixelBuffer* fb;
|
PixelBuffer* fb;
|
||||||
|
|||||||
@@ -298,6 +298,11 @@ size_t Congestion::getBandwidth()
|
|||||||
return congWindow * 1000 / safeBaseRTT;
|
return congWindow * 1000 / safeBaseRTT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned Congestion::getPingTime() const
|
||||||
|
{
|
||||||
|
return safeBaseRTT;
|
||||||
|
}
|
||||||
|
|
||||||
void Congestion::debugTrace(const char* filename, int fd)
|
void Congestion::debugTrace(const char* filename, int fd)
|
||||||
{
|
{
|
||||||
#ifdef CONGESTION_TRACE
|
#ifdef CONGESTION_TRACE
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ namespace rfb {
|
|||||||
// per second.
|
// per second.
|
||||||
size_t getBandwidth();
|
size_t getBandwidth();
|
||||||
|
|
||||||
|
unsigned getPingTime() const;
|
||||||
|
|
||||||
// debugTrace() writes the current congestion window, as well as the
|
// debugTrace() writes the current congestion window, as well as the
|
||||||
// congestion window of the underlying TCP layer, to the specified
|
// congestion window of the underlying TCP layer, to the specified
|
||||||
// file
|
// file
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ ConnParams::ConnParams()
|
|||||||
width(0), height(0), useCopyRect(false),
|
width(0), height(0), useCopyRect(false),
|
||||||
supportsLocalCursor(false), supportsLocalXCursor(false),
|
supportsLocalCursor(false), supportsLocalXCursor(false),
|
||||||
supportsLocalCursorWithAlpha(false),
|
supportsLocalCursorWithAlpha(false),
|
||||||
|
supportsVMWareCursor(false),
|
||||||
supportsCursorPosition(false),
|
supportsCursorPosition(false),
|
||||||
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
|
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
|
||||||
supportsDesktopRename(false), supportsLastRect(false),
|
supportsDesktopRename(false), supportsLastRect(false),
|
||||||
@@ -123,6 +124,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
useCopyRect = false;
|
useCopyRect = false;
|
||||||
supportsLocalCursor = false;
|
supportsLocalCursor = false;
|
||||||
supportsLocalCursorWithAlpha = false;
|
supportsLocalCursorWithAlpha = false;
|
||||||
|
supportsVMWareCursor = false;
|
||||||
supportsDesktopResize = false;
|
supportsDesktopResize = false;
|
||||||
supportsExtendedDesktopSize = false;
|
supportsExtendedDesktopSize = false;
|
||||||
supportsLocalXCursor = false;
|
supportsLocalXCursor = false;
|
||||||
@@ -153,6 +155,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
case pseudoEncodingCursorWithAlpha:
|
case pseudoEncodingCursorWithAlpha:
|
||||||
supportsLocalCursorWithAlpha = true;
|
supportsLocalCursorWithAlpha = true;
|
||||||
break;
|
break;
|
||||||
|
case pseudoEncodingVMwareCursor:
|
||||||
|
supportsVMWareCursor = true;
|
||||||
|
break;
|
||||||
case pseudoEncodingDesktopSize:
|
case pseudoEncodingDesktopSize:
|
||||||
supportsDesktopResize = true;
|
supportsDesktopResize = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ namespace rfb {
|
|||||||
bool supportsLocalCursor;
|
bool supportsLocalCursor;
|
||||||
bool supportsLocalXCursor;
|
bool supportsLocalXCursor;
|
||||||
bool supportsLocalCursorWithAlpha;
|
bool supportsLocalCursorWithAlpha;
|
||||||
|
bool supportsVMWareCursor;
|
||||||
bool supportsCursorPosition;
|
bool supportsCursorPosition;
|
||||||
bool supportsDesktopResize;
|
bool supportsDesktopResize;
|
||||||
bool supportsExtendedDesktopSize;
|
bool supportsExtendedDesktopSize;
|
||||||
|
|||||||
@@ -22,10 +22,12 @@
|
|||||||
#include <omp.h>
|
#include <omp.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <rfb/cpuid.h>
|
||||||
#include <rfb/EncCache.h>
|
#include <rfb/EncCache.h>
|
||||||
#include <rfb/EncodeManager.h>
|
#include <rfb/EncodeManager.h>
|
||||||
#include <rfb/Encoder.h>
|
#include <rfb/Encoder.h>
|
||||||
#include <rfb/Palette.h>
|
#include <rfb/Palette.h>
|
||||||
|
#include <rfb/scale_sse2.h>
|
||||||
#include <rfb/SConnection.h>
|
#include <rfb/SConnection.h>
|
||||||
#include <rfb/ServerCore.h>
|
#include <rfb/ServerCore.h>
|
||||||
#include <rfb/SMsgWriter.h>
|
#include <rfb/SMsgWriter.h>
|
||||||
@@ -40,7 +42,6 @@
|
|||||||
#include <rfb/TightEncoder.h>
|
#include <rfb/TightEncoder.h>
|
||||||
#include <rfb/TightJPEGEncoder.h>
|
#include <rfb/TightJPEGEncoder.h>
|
||||||
#include <rfb/TightWEBPEncoder.h>
|
#include <rfb/TightWEBPEncoder.h>
|
||||||
#include <rfb/TightX264Encoder.h>
|
|
||||||
|
|
||||||
using namespace rfb;
|
using namespace rfb;
|
||||||
|
|
||||||
@@ -69,7 +70,6 @@ enum EncoderClass {
|
|||||||
encoderTight,
|
encoderTight,
|
||||||
encoderTightJPEG,
|
encoderTightJPEG,
|
||||||
encoderTightWEBP,
|
encoderTightWEBP,
|
||||||
encoderTightX264,
|
|
||||||
encoderZRLE,
|
encoderZRLE,
|
||||||
encoderClassMax,
|
encoderClassMax,
|
||||||
};
|
};
|
||||||
@@ -112,8 +112,6 @@ static const char *encoderClassName(EncoderClass klass)
|
|||||||
return "Tight (JPEG)";
|
return "Tight (JPEG)";
|
||||||
case encoderTightWEBP:
|
case encoderTightWEBP:
|
||||||
return "Tight (WEBP)";
|
return "Tight (WEBP)";
|
||||||
case encoderTightX264:
|
|
||||||
return "Tight (X264)";
|
|
||||||
case encoderZRLE:
|
case encoderZRLE:
|
||||||
return "ZRLE";
|
return "ZRLE";
|
||||||
case encoderClassMax:
|
case encoderClassMax:
|
||||||
@@ -174,7 +172,6 @@ EncodeManager::EncodeManager(SConnection* conn_, EncCache *encCache_) : conn(con
|
|||||||
encoders[encoderTight] = new TightEncoder(conn);
|
encoders[encoderTight] = new TightEncoder(conn);
|
||||||
encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
|
encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
|
||||||
encoders[encoderTightWEBP] = new TightWEBPEncoder(conn);
|
encoders[encoderTightWEBP] = new TightWEBPEncoder(conn);
|
||||||
encoders[encoderTightX264] = new TightX264Encoder(conn, encCache, encoderTightX264);
|
|
||||||
encoders[encoderZRLE] = new ZRLEEncoder(conn);
|
encoders[encoderZRLE] = new ZRLEEncoder(conn);
|
||||||
|
|
||||||
webpBenchResult = ((TightWEBPEncoder *) encoders[encoderTightWEBP])->benchmark();
|
webpBenchResult = ((TightWEBPEncoder *) encoders[encoderTightWEBP])->benchmark();
|
||||||
@@ -364,6 +361,8 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
|||||||
changed = changed_;
|
changed = changed_;
|
||||||
|
|
||||||
gettimeofday(&start, NULL);
|
gettimeofday(&start, NULL);
|
||||||
|
memset(&jpegstats, 0, sizeof(codecstats_t));
|
||||||
|
memset(&webpstats, 0, sizeof(codecstats_t));
|
||||||
|
|
||||||
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
|
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
|
||||||
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
|
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
|
||||||
@@ -398,33 +397,20 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
|||||||
|
|
||||||
conn->writer()->writeFramebufferUpdateStart(nRects);
|
conn->writer()->writeFramebufferUpdateStart(nRects);
|
||||||
|
|
||||||
|
writeCopyRects(copied, copyDelta);
|
||||||
|
writeCopyPassRects(copypassed);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In extra-low-quality mode, if x264 is enabled, send entire screen frames
|
* We start by searching for solid rects, which are then removed
|
||||||
|
* from the changed region.
|
||||||
*/
|
*/
|
||||||
if (rfb::Server::x264Bitrate && videoDetected &&
|
if (conn->cp.supportsLastRect)
|
||||||
((TightX264Encoder *) encoders[encoderTightX264])->tryInit(pb)) {
|
writeSolidRects(&changed, pb);
|
||||||
std::vector<Rect> rects;
|
|
||||||
changed.get_rects(&rects);
|
|
||||||
updateVideoStats(rects, pb);
|
|
||||||
|
|
||||||
writeSubRect(pb->getRect(), pb, encoderFullColour, Palette(), std::vector<uint8_t>(),
|
writeRects(changed, pb,
|
||||||
false);
|
&start, true);
|
||||||
} else {
|
if (!videoDetected) // In case detection happened between the calls
|
||||||
writeCopyRects(copied, copyDelta);
|
writeRects(cursorRegion, renderedCursor);
|
||||||
writeCopyPassRects(copypassed);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We start by searching for solid rects, which are then removed
|
|
||||||
* from the changed region.
|
|
||||||
*/
|
|
||||||
if (conn->cp.supportsLastRect)
|
|
||||||
writeSolidRects(&changed, pb);
|
|
||||||
|
|
||||||
writeRects(changed, pb,
|
|
||||||
&start, true);
|
|
||||||
if (!videoDetected) // In case detection happened between the calls
|
|
||||||
writeRects(cursorRegion, renderedCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateQualities();
|
updateQualities();
|
||||||
|
|
||||||
@@ -635,8 +621,6 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type, const bool trackQu
|
|||||||
klass = activeEncoders[activeType];
|
klass = activeEncoders[activeType];
|
||||||
if (isWebp)
|
if (isWebp)
|
||||||
klass = encoderTightWEBP;
|
klass = encoderTightWEBP;
|
||||||
else if (rfb::Server::x264Bitrate && videoDetected) // if x264 enabled
|
|
||||||
klass = encoderTightX264;
|
|
||||||
|
|
||||||
beforeLength = conn->getOutStream()->length();
|
beforeLength = conn->getOutStream()->length();
|
||||||
|
|
||||||
@@ -676,8 +660,6 @@ void EncodeManager::endRect(const uint8_t isWebp)
|
|||||||
klass = activeEncoders[activeType];
|
klass = activeEncoders[activeType];
|
||||||
if (isWebp)
|
if (isWebp)
|
||||||
klass = encoderTightWEBP;
|
klass = encoderTightWEBP;
|
||||||
else if (rfb::Server::x264Bitrate && videoDetected) // if x264 enabled
|
|
||||||
klass = encoderTightX264;
|
|
||||||
stats[klass][activeType].bytes += length;
|
stats[klass][activeType].bytes += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,8 +865,6 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
|
|||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
if (!rfb::Server::videoTime) {
|
if (!rfb::Server::videoTime) {
|
||||||
if (!videoDetected)
|
|
||||||
((TightX264Encoder *) encoders[encoderTightX264])->setKeyframe();
|
|
||||||
videoDetected = true;
|
videoDetected = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -912,14 +892,12 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
|
|||||||
|
|
||||||
if (area > (unsigned) rfb::Server::videoArea) {
|
if (area > (unsigned) rfb::Server::videoArea) {
|
||||||
// Initiate low-quality video mode
|
// Initiate low-quality video mode
|
||||||
if (!videoDetected)
|
|
||||||
((TightX264Encoder *) encoders[encoderTightX264])->setKeyframe();
|
|
||||||
videoDetected = true;
|
videoDetected = true;
|
||||||
videoTimer.start(1000 * rfb::Server::videoOutTime);
|
videoTimer.start(1000 * rfb::Server::videoOutTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
PixelBuffer *rfb::nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
||||||
const float diff)
|
const float diff)
|
||||||
{
|
{
|
||||||
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h);
|
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h);
|
||||||
@@ -944,7 +922,7 @@ static PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const
|
|||||||
return newpb;
|
return newpb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
PixelBuffer *rfb::bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
||||||
const float diff)
|
const float diff)
|
||||||
{
|
{
|
||||||
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h);
|
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h);
|
||||||
@@ -992,10 +970,68 @@ static PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const
|
|||||||
return newpb;
|
return newpb;
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
|
PixelBuffer *rfb::progressiveBilinearScale(const PixelBuffer *pb,
|
||||||
const uint16_t tgtw, const uint16_t tgth,
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
const float tgtdiff)
|
const float tgtdiff)
|
||||||
{
|
{
|
||||||
|
if (supportsSSE2()) {
|
||||||
|
if (tgtdiff >= 0.5f) {
|
||||||
|
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), tgtw, tgth);
|
||||||
|
|
||||||
|
int oldstride, newstride;
|
||||||
|
const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride);
|
||||||
|
rdr::U8 *newpx = newpb->getBufferRW(newpb->getRect(), &newstride);
|
||||||
|
|
||||||
|
SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtdiff);
|
||||||
|
return newpb;
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelBuffer *newpb;
|
||||||
|
uint16_t neww, newh, oldw, oldh;
|
||||||
|
bool del = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldw = pb->getRect().width();
|
||||||
|
oldh = pb->getRect().height();
|
||||||
|
neww = oldw / 2;
|
||||||
|
newh = oldh / 2;
|
||||||
|
|
||||||
|
newpb = new ManagedPixelBuffer(pb->getPF(), neww, newh);
|
||||||
|
|
||||||
|
int oldstride, newstride;
|
||||||
|
const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride);
|
||||||
|
rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(),
|
||||||
|
&newstride);
|
||||||
|
|
||||||
|
SSE2_halve(oldpx, neww, newh, newpx, oldstride, newstride);
|
||||||
|
|
||||||
|
if (del)
|
||||||
|
delete pb;
|
||||||
|
del = true;
|
||||||
|
|
||||||
|
pb = newpb;
|
||||||
|
} while (tgtw * 2 < neww);
|
||||||
|
|
||||||
|
// Final, non-halving step
|
||||||
|
if (tgtw != neww || tgth != newh) {
|
||||||
|
oldw = pb->getRect().width();
|
||||||
|
oldh = pb->getRect().height();
|
||||||
|
|
||||||
|
newpb = new ManagedPixelBuffer(pb->getPF(), tgtw, tgth);
|
||||||
|
|
||||||
|
int oldstride, newstride;
|
||||||
|
const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride);
|
||||||
|
rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(),
|
||||||
|
&newstride);
|
||||||
|
|
||||||
|
SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtdiff);
|
||||||
|
if (del)
|
||||||
|
delete pb;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newpb;
|
||||||
|
} // SSE2
|
||||||
|
|
||||||
if (tgtdiff >= 0.5f)
|
if (tgtdiff >= 0.5f)
|
||||||
return bilinearScale(pb, tgtw, tgth, tgtdiff);
|
return bilinearScale(pb, tgtw, tgth, tgtdiff);
|
||||||
|
|
||||||
@@ -1040,6 +1076,7 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
std::vector<uint8_t> isWebp, fromCache;
|
std::vector<uint8_t> isWebp, fromCache;
|
||||||
std::vector<Palette> palettes;
|
std::vector<Palette> palettes;
|
||||||
std::vector<std::vector<uint8_t> > compresseds;
|
std::vector<std::vector<uint8_t> > compresseds;
|
||||||
|
std::vector<uint32_t> ms;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
if (rfb::Server::rectThreads > 0)
|
if (rfb::Server::rectThreads > 0)
|
||||||
@@ -1048,6 +1085,11 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
webpTookTooLong = false;
|
webpTookTooLong = false;
|
||||||
changed.get_rects(&rects);
|
changed.get_rects(&rects);
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
if (mainScreen) {
|
||||||
|
updateVideoStats(rects, pb);
|
||||||
|
}
|
||||||
|
|
||||||
if (videoDetected) {
|
if (videoDetected) {
|
||||||
rects.clear();
|
rects.clear();
|
||||||
rects.push_back(pb->getRect());
|
rects.push_back(pb->getRect());
|
||||||
@@ -1099,9 +1141,13 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
palettes.resize(subrects.size());
|
palettes.resize(subrects.size());
|
||||||
compresseds.resize(subrects.size());
|
compresseds.resize(subrects.size());
|
||||||
scaledrects.resize(subrects.size());
|
scaledrects.resize(subrects.size());
|
||||||
|
ms.resize(subrects.size());
|
||||||
|
|
||||||
// In case the current resolution is above the max video res, and video was detected,
|
// In case the current resolution is above the max video res, and video was detected,
|
||||||
// scale to that res, keeping aspect ratio
|
// scale to that res, keeping aspect ratio
|
||||||
|
struct timeval scalestart;
|
||||||
|
gettimeofday(&scalestart, NULL);
|
||||||
|
|
||||||
const PixelBuffer *scaledpb = NULL;
|
const PixelBuffer *scaledpb = NULL;
|
||||||
if (videoDetected &&
|
if (videoDetected &&
|
||||||
(maxVideoX < pb->getRect().width() || maxVideoY < pb->getRect().height())) {
|
(maxVideoX < pb->getRect().width() || maxVideoY < pb->getRect().height())) {
|
||||||
@@ -1150,15 +1196,25 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scalingTime = msSince(&scalestart);
|
||||||
|
|
||||||
#pragma omp parallel for schedule(dynamic, 1)
|
#pragma omp parallel for schedule(dynamic, 1)
|
||||||
for (i = 0; i < subrects.size(); ++i) {
|
for (i = 0; i < subrects.size(); ++i) {
|
||||||
encoderTypes[i] = getEncoderType(subrects[i], pb, &palettes[i], compresseds[i],
|
encoderTypes[i] = getEncoderType(subrects[i], pb, &palettes[i], compresseds[i],
|
||||||
&isWebp[i], &fromCache[i],
|
&isWebp[i], &fromCache[i],
|
||||||
scaledpb, scaledrects[i]);
|
scaledpb, scaledrects[i], ms[i]);
|
||||||
checkWebpFallback(start);
|
checkWebpFallback(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < subrects.size(); ++i) {
|
||||||
|
if (encoderTypes[i] == encoderFullColour) {
|
||||||
|
if (isWebp[i])
|
||||||
|
webpstats.ms += ms[i];
|
||||||
|
else
|
||||||
|
jpegstats.ms += ms[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (start) {
|
if (start) {
|
||||||
encodingTime = msSince(start);
|
encodingTime = msSince(start);
|
||||||
|
|
||||||
@@ -1192,11 +1248,6 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
writeSubRect(subrects[i], pb, encoderTypes[i], palettes[i], compresseds[i], isWebp[i]);
|
writeSubRect(subrects[i], pb, encoderTypes[i], palettes[i], compresseds[i], isWebp[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update stats
|
|
||||||
if (mainScreen) {
|
|
||||||
updateVideoStats(rects, pb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scaledpb)
|
if (scaledpb)
|
||||||
delete scaledpb;
|
delete scaledpb;
|
||||||
}
|
}
|
||||||
@@ -1204,7 +1255,8 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
||||||
Palette *pal, std::vector<uint8_t> &compressed,
|
Palette *pal, std::vector<uint8_t> &compressed,
|
||||||
uint8_t *isWebp, uint8_t *fromCache,
|
uint8_t *isWebp, uint8_t *fromCache,
|
||||||
const PixelBuffer *scaledpb, const Rect& scaledrect) const
|
const PixelBuffer *scaledpb, const Rect& scaledrect,
|
||||||
|
uint32_t &ms) const
|
||||||
{
|
{
|
||||||
struct RectInfo info;
|
struct RectInfo info;
|
||||||
unsigned int maxColours = 256;
|
unsigned int maxColours = 256;
|
||||||
@@ -1257,9 +1309,12 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
|||||||
|
|
||||||
*isWebp = 0;
|
*isWebp = 0;
|
||||||
*fromCache = 0;
|
*fromCache = 0;
|
||||||
|
ms = 0;
|
||||||
if (type == encoderFullColour) {
|
if (type == encoderFullColour) {
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
const void *data;
|
const void *data;
|
||||||
|
struct timeval start;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
|
||||||
if (encCache->enabled &&
|
if (encCache->enabled &&
|
||||||
(data = encCache->get(activeEncoders[encoderFullColour],
|
(data = encCache->get(activeEncoders[encoderFullColour],
|
||||||
@@ -1300,6 +1355,8 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
|||||||
compressed,
|
compressed,
|
||||||
videoDetected);
|
videoDetected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms = msSince(&start);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete ppb;
|
delete ppb;
|
||||||
@@ -1318,10 +1375,15 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb,
|
|||||||
encoder = startRect(rect, type, compressed.size() == 0, isWebp);
|
encoder = startRect(rect, type, compressed.size() == 0, isWebp);
|
||||||
|
|
||||||
if (compressed.size()) {
|
if (compressed.size()) {
|
||||||
if (isWebp)
|
if (isWebp) {
|
||||||
((TightWEBPEncoder *) encoder)->writeOnly(compressed);
|
((TightWEBPEncoder *) encoder)->writeOnly(compressed);
|
||||||
else
|
webpstats.area += rect.area();
|
||||||
|
webpstats.rects++;
|
||||||
|
} else {
|
||||||
((TightJPEGEncoder *) encoder)->writeOnly(compressed);
|
((TightJPEGEncoder *) encoder)->writeOnly(compressed);
|
||||||
|
jpegstats.area += rect.area();
|
||||||
|
jpegstats.rects++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (encoder->flags & EncoderUseNativePF) {
|
if (encoder->flags & EncoderUseNativePF) {
|
||||||
ppb = preparePixelBuffer(rect, pb, false);
|
ppb = preparePixelBuffer(rect, pb, false);
|
||||||
|
|||||||
@@ -68,9 +68,24 @@ namespace rfb {
|
|||||||
const RenderedCursor* renderedCursor,
|
const RenderedCursor* renderedCursor,
|
||||||
size_t maxUpdateSize);
|
size_t maxUpdateSize);
|
||||||
|
|
||||||
|
void clearEncodingTime() {
|
||||||
|
encodingTime = 0;
|
||||||
|
};
|
||||||
|
|
||||||
unsigned getEncodingTime() const {
|
unsigned getEncodingTime() const {
|
||||||
return encodingTime;
|
return encodingTime;
|
||||||
};
|
};
|
||||||
|
unsigned getScalingTime() const {
|
||||||
|
return scalingTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct codecstats_t {
|
||||||
|
uint32_t ms;
|
||||||
|
uint32_t area;
|
||||||
|
uint32_t rects;
|
||||||
|
};
|
||||||
|
|
||||||
|
codecstats_t jpegstats, webpstats;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void doUpdate(bool allowLossy, const Region& changed,
|
void doUpdate(bool allowLossy, const Region& changed,
|
||||||
@@ -105,7 +120,8 @@ namespace rfb {
|
|||||||
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
|
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
|
||||||
std::vector<uint8_t> &compressed, uint8_t *isWebp,
|
std::vector<uint8_t> &compressed, uint8_t *isWebp,
|
||||||
uint8_t *fromCache,
|
uint8_t *fromCache,
|
||||||
const PixelBuffer *scaledpb, const Rect& scaledrect) const;
|
const PixelBuffer *scaledpb, const Rect& scaledrect,
|
||||||
|
uint32_t &ms) const;
|
||||||
virtual bool handleTimeout(Timer* t);
|
virtual bool handleTimeout(Timer* t);
|
||||||
|
|
||||||
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,
|
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,
|
||||||
@@ -183,6 +199,7 @@ namespace rfb {
|
|||||||
bool webpTookTooLong;
|
bool webpTookTooLong;
|
||||||
unsigned encodingTime;
|
unsigned encodingTime;
|
||||||
unsigned maxEncodingTime, framesSinceEncPrint;
|
unsigned maxEncodingTime, framesSinceEncPrint;
|
||||||
|
unsigned scalingTime;
|
||||||
|
|
||||||
EncCache *encCache;
|
EncCache *encCache;
|
||||||
|
|
||||||
@@ -198,6 +215,13 @@ namespace rfb {
|
|||||||
virtual rdr::U8* getBufferRW(const Rect& r, int* stride);
|
virtual rdr::U8* getBufferRW(const Rect& r, int* stride);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
||||||
|
const float diff);
|
||||||
|
PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
||||||
|
const float diff);
|
||||||
|
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
|
||||||
|
const float diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ namespace rfb {
|
|||||||
const size_t* lengths,
|
const size_t* lengths,
|
||||||
const rdr::U8* const* data);
|
const rdr::U8* const* data);
|
||||||
|
|
||||||
virtual void sendStats() = 0;
|
virtual void sendStats(const bool toClient = true) = 0;
|
||||||
|
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;
|
||||||
|
|
||||||
virtual bool canChangeKasmSettings() const = 0;
|
virtual bool canChangeKasmSettings() const = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ void SMsgReader::readMsg()
|
|||||||
case msgTypeRequestStats:
|
case msgTypeRequestStats:
|
||||||
readRequestStats();
|
readRequestStats();
|
||||||
break;
|
break;
|
||||||
|
case msgTypeFrameStats:
|
||||||
|
readFrameStats();
|
||||||
|
break;
|
||||||
case msgTypeKeyEvent:
|
case msgTypeKeyEvent:
|
||||||
readKeyEvent();
|
readKeyEvent();
|
||||||
break;
|
break;
|
||||||
@@ -346,6 +349,14 @@ void SMsgReader::readRequestStats()
|
|||||||
handler->sendStats();
|
handler->sendStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgReader::readFrameStats()
|
||||||
|
{
|
||||||
|
is->skip(3);
|
||||||
|
rdr::U32 all = is->readU32();
|
||||||
|
rdr::U32 render = is->readU32();
|
||||||
|
handler->handleFrameStats(all, render);
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgReader::readQEMUMessage()
|
void SMsgReader::readQEMUMessage()
|
||||||
{
|
{
|
||||||
int subType = is->readU8();
|
int subType = is->readU8();
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace rfb {
|
|||||||
void readClientCutText();
|
void readClientCutText();
|
||||||
void readExtendedClipboard(rdr::S32 len);
|
void readExtendedClipboard(rdr::S32 len);
|
||||||
void readRequestStats();
|
void readRequestStats();
|
||||||
|
void readFrameStats();
|
||||||
|
|
||||||
void readQEMUMessage();
|
void readQEMUMessage();
|
||||||
void readQEMUKeyEvent();
|
void readQEMUKeyEvent();
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
|
|||||||
needSetDesktopSize(false), needExtendedDesktopSize(false),
|
needSetDesktopSize(false), needExtendedDesktopSize(false),
|
||||||
needSetDesktopName(false), needSetCursor(false),
|
needSetDesktopName(false), needSetCursor(false),
|
||||||
needSetXCursor(false), needSetCursorWithAlpha(false),
|
needSetXCursor(false), needSetCursorWithAlpha(false),
|
||||||
|
needSetVMWareCursor(false),
|
||||||
needCursorPos(false),
|
needCursorPos(false),
|
||||||
needLEDState(false), needQEMUKeyEvent(false)
|
needLEDState(false), needQEMUKeyEvent(false)
|
||||||
{
|
{
|
||||||
@@ -208,6 +209,12 @@ void SMsgWriter::writeStats(const char* str, int len)
|
|||||||
endMsg();
|
endMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeRequestFrameStats()
|
||||||
|
{
|
||||||
|
startMsg(msgTypeRequestFrameStats);
|
||||||
|
endMsg();
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
|
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
|
||||||
{
|
{
|
||||||
if (!cp->supportsFence)
|
if (!cp->supportsFence)
|
||||||
@@ -315,6 +322,16 @@ bool SMsgWriter::writeSetCursorWithAlpha()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SMsgWriter::writeSetVMwareCursor()
|
||||||
|
{
|
||||||
|
if (!cp->supportsVMWareCursor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
needSetVMWareCursor = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgWriter::writeCursorPos()
|
void SMsgWriter::writeCursorPos()
|
||||||
{
|
{
|
||||||
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
||||||
@@ -349,7 +366,7 @@ bool SMsgWriter::needFakeUpdate()
|
|||||||
{
|
{
|
||||||
if (needSetDesktopName)
|
if (needSetDesktopName)
|
||||||
return true;
|
return true;
|
||||||
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
|
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha || needSetVMWareCursor)
|
||||||
return true;
|
return true;
|
||||||
if (needCursorPos)
|
if (needCursorPos)
|
||||||
return true;
|
return true;
|
||||||
@@ -405,6 +422,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects)
|
|||||||
nRects++;
|
nRects++;
|
||||||
if (needSetCursorWithAlpha)
|
if (needSetCursorWithAlpha)
|
||||||
nRects++;
|
nRects++;
|
||||||
|
if (needSetVMWareCursor)
|
||||||
|
nRects++;
|
||||||
if (needCursorPos)
|
if (needCursorPos)
|
||||||
nRects++;
|
nRects++;
|
||||||
if (needLEDState)
|
if (needLEDState)
|
||||||
@@ -522,6 +541,15 @@ void SMsgWriter::writePseudoRects()
|
|||||||
needSetCursorWithAlpha = false;
|
needSetCursorWithAlpha = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needSetVMWareCursor) {
|
||||||
|
const Cursor& cursor = cp->cursor();
|
||||||
|
|
||||||
|
writeSetVMwareCursorRect(cursor.width(), cursor.height(),
|
||||||
|
cursor.hotspot().x, cursor.hotspot().y,
|
||||||
|
cursor.getBuffer());
|
||||||
|
needSetVMWareCursor = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (needCursorPos) {
|
if (needCursorPos) {
|
||||||
const Point& cursorPos = cp->cursorPos();
|
const Point& cursorPos = cp->cursorPos();
|
||||||
|
|
||||||
@@ -712,6 +740,28 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
|
||||||
|
int hotspotX, int hotspotY,
|
||||||
|
const rdr::U8* data)
|
||||||
|
{
|
||||||
|
if (!cp->supportsVMWareCursor)
|
||||||
|
throw Exception("Client does not support local cursors");
|
||||||
|
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
|
||||||
|
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
|
||||||
|
|
||||||
|
os->writeS16(hotspotX);
|
||||||
|
os->writeS16(hotspotY);
|
||||||
|
os->writeU16(width);
|
||||||
|
os->writeU16(height);
|
||||||
|
os->writeU32(pseudoEncodingVMwareCursor);
|
||||||
|
|
||||||
|
os->writeU8(1); // Alpha cursor
|
||||||
|
os->pad(1);
|
||||||
|
|
||||||
|
// FIXME: Should alpha be premultiplied?
|
||||||
|
os->writeBytes(data, width*height*4);
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY)
|
void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY)
|
||||||
{
|
{
|
||||||
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ namespace rfb {
|
|||||||
|
|
||||||
void writeStats(const char* str, int len);
|
void writeStats(const char* str, int len);
|
||||||
|
|
||||||
|
void writeRequestFrameStats();
|
||||||
|
|
||||||
// writeFence() sends a new fence request or response to the client.
|
// writeFence() sends a new fence request or response to the client.
|
||||||
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
|
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
|
||||||
|
|
||||||
@@ -90,6 +92,7 @@ namespace rfb {
|
|||||||
bool writeSetCursor();
|
bool writeSetCursor();
|
||||||
bool writeSetXCursor();
|
bool writeSetXCursor();
|
||||||
bool writeSetCursorWithAlpha();
|
bool writeSetCursorWithAlpha();
|
||||||
|
bool writeSetVMwareCursor();
|
||||||
|
|
||||||
// Notifies the client that the cursor pointer was moved by the server.
|
// Notifies the client that the cursor pointer was moved by the server.
|
||||||
void writeCursorPos();
|
void writeCursorPos();
|
||||||
@@ -149,6 +152,9 @@ namespace rfb {
|
|||||||
void writeSetCursorWithAlphaRect(int width, int height,
|
void writeSetCursorWithAlphaRect(int width, int height,
|
||||||
int hotspotX, int hotspotY,
|
int hotspotX, int hotspotY,
|
||||||
const rdr::U8* data);
|
const rdr::U8* data);
|
||||||
|
void writeSetVMwareCursorRect(int width, int height,
|
||||||
|
int hotspotX, int hotspotY,
|
||||||
|
const rdr::U8* data);
|
||||||
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY);
|
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY);
|
||||||
void writeLEDStateRect(rdr::U8 state);
|
void writeLEDStateRect(rdr::U8 state);
|
||||||
void writeQEMUKeyEventRect();
|
void writeQEMUKeyEventRect();
|
||||||
@@ -165,6 +171,7 @@ namespace rfb {
|
|||||||
bool needSetCursor;
|
bool needSetCursor;
|
||||||
bool needSetXCursor;
|
bool needSetXCursor;
|
||||||
bool needSetCursorWithAlpha;
|
bool needSetCursorWithAlpha;
|
||||||
|
bool needSetVMWareCursor;
|
||||||
bool needCursorPos;
|
bool needCursorPos;
|
||||||
bool needLEDState;
|
bool needLEDState;
|
||||||
bool needQEMUKeyEvent;
|
bool needQEMUKeyEvent;
|
||||||
|
|||||||
197
common/rfb/SelfBench.cxx
Normal file
197
common/rfb/SelfBench.cxx
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/* Copyright (C) 2021 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 <rfb/ComparingUpdateTracker.h>
|
||||||
|
#include <rfb/EncodeManager.h>
|
||||||
|
#include <rfb/LogWriter.h>
|
||||||
|
#include <rfb/SConnection.h>
|
||||||
|
#include <rfb/ServerCore.h>
|
||||||
|
#include <rfb/PixelBuffer.h>
|
||||||
|
#include <rfb/TightJPEGEncoder.h>
|
||||||
|
#include <rfb/TightWEBPEncoder.h>
|
||||||
|
#include <rfb/util.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.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
|
||||||
|
|
||||||
|
#define W 1600
|
||||||
|
#define H 1200
|
||||||
|
|
||||||
|
void SelfBench() {
|
||||||
|
|
||||||
|
unsigned i, runs;
|
||||||
|
struct timeval start;
|
||||||
|
|
||||||
|
ManagedPixelBuffer f1(pfRGBX, W, H);
|
||||||
|
ManagedPixelBuffer f2(pfRGBX, W, H);
|
||||||
|
ManagedPixelBuffer screen(pfRGBX, W, H);
|
||||||
|
|
||||||
|
int stride;
|
||||||
|
rdr::U8 *f1ptr = f1.getBufferRW(f1.getRect(), &stride);
|
||||||
|
rdr::U8 *f2ptr = f2.getBufferRW(f2.getRect(), &stride);
|
||||||
|
rdr::U8 * const screenptr = screen.getBufferRW(screen.getRect(), &stride);
|
||||||
|
|
||||||
|
rdr::U8 * const f1orig = f1ptr;
|
||||||
|
rdr::U8 * const f2orig = f2ptr;
|
||||||
|
|
||||||
|
for (i = 0; i < W * H * 4; i += 4) {
|
||||||
|
f1ptr[0] = rand();
|
||||||
|
f1ptr[1] = rand();
|
||||||
|
f1ptr[2] = rand();
|
||||||
|
|
||||||
|
f2ptr[0] = rand();
|
||||||
|
f2ptr[1] = rand();
|
||||||
|
f2ptr[2] = rand();
|
||||||
|
|
||||||
|
f1ptr += 4;
|
||||||
|
f2ptr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlog.info("Running micro-benchmarks (single-threaded, runs depending on task)");
|
||||||
|
|
||||||
|
// Encoding
|
||||||
|
std::vector<uint8_t> vec;
|
||||||
|
|
||||||
|
TightJPEGEncoder jpeg(NULL);
|
||||||
|
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
runs = RUNS;
|
||||||
|
for (i = 0; i < runs; i++) {
|
||||||
|
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++) {
|
||||||
|
jpeg.compressOnly(&f1, 4, vec, false);
|
||||||
|
}
|
||||||
|
vlog.info("Jpeg compression at quality 4 took %u ms (%u runs)", msSince(&start), runs);
|
||||||
|
|
||||||
|
|
||||||
|
TightWEBPEncoder webp(NULL);
|
||||||
|
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
runs = RUNS / 8;
|
||||||
|
for (i = 0; i < runs; i++) {
|
||||||
|
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++) {
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
delete pb;
|
||||||
|
}
|
||||||
|
vlog.info("Progressive bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
|
||||||
|
|
||||||
|
// Analysis
|
||||||
|
ComparingUpdateTracker *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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
@@ -113,6 +113,10 @@ rfb::BoolParameter rfb::Server::ignoreClientSettingsKasm
|
|||||||
("IgnoreClientSettingsKasm",
|
("IgnoreClientSettingsKasm",
|
||||||
"Ignore the additional client settings exposed in Kasm.",
|
"Ignore the additional client settings exposed in Kasm.",
|
||||||
false);
|
false);
|
||||||
|
rfb::BoolParameter rfb::Server::selfBench
|
||||||
|
("SelfBench",
|
||||||
|
"Run self-benchmarks and exit.",
|
||||||
|
false);
|
||||||
rfb::IntParameter rfb::Server::dynamicQualityMin
|
rfb::IntParameter rfb::Server::dynamicQualityMin
|
||||||
("DynamicQualityMin",
|
("DynamicQualityMin",
|
||||||
"The minimum dynamic JPEG quality, 0 = low, 9 = high",
|
"The minimum dynamic JPEG quality, 0 = low, 9 = high",
|
||||||
@@ -199,10 +203,6 @@ rfb::IntParameter rfb::Server::videoScaling
|
|||||||
("VideoScaling",
|
("VideoScaling",
|
||||||
"Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear, 2 = prog bilinear",
|
"Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear, 2 = prog bilinear",
|
||||||
2, 0, 2);
|
2, 0, 2);
|
||||||
rfb::IntParameter rfb::Server::x264Bitrate
|
|
||||||
("x264Bitrate",
|
|
||||||
"Enable x264 encoding for full-screen video, in kbps. Default 0 (off)",
|
|
||||||
0, 0, 50000);
|
|
||||||
rfb::BoolParameter rfb::Server::printVideoArea
|
rfb::BoolParameter rfb::Server::printVideoArea
|
||||||
("PrintVideoArea",
|
("PrintVideoArea",
|
||||||
"Print the detected video area % value.",
|
"Print the detected video area % value.",
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ namespace rfb {
|
|||||||
static IntParameter videoOutTime;
|
static IntParameter videoOutTime;
|
||||||
static IntParameter videoArea;
|
static IntParameter videoArea;
|
||||||
static IntParameter videoScaling;
|
static IntParameter videoScaling;
|
||||||
static IntParameter x264Bitrate;
|
|
||||||
static StringParameter kasmPasswordFile;
|
static StringParameter kasmPasswordFile;
|
||||||
static BoolParameter printVideoArea;
|
static BoolParameter printVideoArea;
|
||||||
static BoolParameter protocol3_3;
|
static BoolParameter protocol3_3;
|
||||||
@@ -75,6 +74,7 @@ namespace rfb {
|
|||||||
static BoolParameter detectScrolling;
|
static BoolParameter detectScrolling;
|
||||||
static BoolParameter detectHorizontal;
|
static BoolParameter detectHorizontal;
|
||||||
static BoolParameter ignoreClientSettingsKasm;
|
static BoolParameter ignoreClientSettingsKasm;
|
||||||
|
static BoolParameter selfBench;
|
||||||
static PresetParameter preferBandwidth;
|
static PresetParameter preferBandwidth;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ namespace rfb {
|
|||||||
const unsigned int tightJpeg = 0x09;
|
const unsigned int tightJpeg = 0x09;
|
||||||
const unsigned int tightPng = 0x0a;
|
const unsigned int tightPng = 0x0a;
|
||||||
const unsigned int tightWebp = 0x0b;
|
const unsigned int tightWebp = 0x0b;
|
||||||
const unsigned int tightX264 = 0x0c;
|
const unsigned int tightMaxSubencoding = 0x0b;
|
||||||
const unsigned int tightMaxSubencoding = 0x0c;
|
|
||||||
|
|
||||||
// Filters to improve compression efficiency
|
// Filters to improve compression efficiency
|
||||||
const unsigned int tightFilterCopy = 0x00;
|
const unsigned int tightFilterCopy = 0x00;
|
||||||
|
|||||||
@@ -1,507 +0,0 @@
|
|||||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
|
||||||
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
|
|
||||||
* Copyright 2014 Pierre Ossman for Cendio AB
|
|
||||||
*
|
|
||||||
* 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 <rdr/OutStream.h>
|
|
||||||
#include <rfb/EncCache.h>
|
|
||||||
#include <rfb/encodings.h>
|
|
||||||
#include <rfb/LogWriter.h>
|
|
||||||
#include <rfb/SConnection.h>
|
|
||||||
#include <rfb/ServerCore.h>
|
|
||||||
#include <rfb/PixelBuffer.h>
|
|
||||||
#include <rfb/TightX264Encoder.h>
|
|
||||||
#include <rfb/TightConstants.h>
|
|
||||||
|
|
||||||
#include <webp/encode.h>
|
|
||||||
#include <x264.h>
|
|
||||||
#include "nvidia.h"
|
|
||||||
#include "mp4.h"
|
|
||||||
|
|
||||||
#define MAX_FRAMELEN (1024 * 1024)
|
|
||||||
|
|
||||||
using namespace rfb;
|
|
||||||
|
|
||||||
static LogWriter vlog("x264");
|
|
||||||
|
|
||||||
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
|
|
||||||
static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
|
|
||||||
|
|
||||||
bool TightX264Encoder::skip_nvidia = false;
|
|
||||||
|
|
||||||
TightX264Encoder::TightX264Encoder(SConnection* conn, EncCache *cache_, uint8_t cacheType_) :
|
|
||||||
Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1),
|
|
||||||
keyframe(true), enc(NULL), params(NULL), mux(NULL), muxstate(NULL), framectr(0),
|
|
||||||
nvidia_init_done(false), using_nvidia(true),
|
|
||||||
encCache(cache_), cacheType(cacheType_),
|
|
||||||
framebuf(NULL), framelen(0), bitbuf(NULL), myw(0), myh(0)
|
|
||||||
{
|
|
||||||
params = new x264_param_t;
|
|
||||||
x264_param_default_preset(params, "veryfast", "zerolatency");
|
|
||||||
|
|
||||||
params->i_threads = X264_THREADS_AUTO;
|
|
||||||
params->i_fps_num = params->i_keyint_max = rfb::Server::frameRate;
|
|
||||||
params->i_fps_den = 1;
|
|
||||||
params->rc.i_rc_method = X264_RC_ABR;
|
|
||||||
params->rc.i_bitrate = rfb::Server::x264Bitrate;
|
|
||||||
params->i_csp = X264_CSP_I420;
|
|
||||||
params->i_log_level = X264_LOG_WARNING;
|
|
||||||
params->b_annexb = 0;
|
|
||||||
|
|
||||||
framebuf = new uint8_t[MAX_FRAMELEN];
|
|
||||||
bitbuf = new uint8_t[MAX_FRAMELEN];
|
|
||||||
mux = new Mp4Context;
|
|
||||||
memset(mux, 0, sizeof(Mp4Context));
|
|
||||||
muxstate = new Mp4State;
|
|
||||||
memset(muxstate, 0, sizeof(Mp4State));
|
|
||||||
}
|
|
||||||
|
|
||||||
TightX264Encoder::~TightX264Encoder()
|
|
||||||
{
|
|
||||||
delete params;
|
|
||||||
delete [] framebuf;
|
|
||||||
delete [] bitbuf;
|
|
||||||
delete mux;
|
|
||||||
delete muxstate;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TightX264Encoder::isSupported()
|
|
||||||
{
|
|
||||||
if (!conn->cp.supportsEncoding(encodingTight))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Unconditional support if enabled
|
|
||||||
return rfb::Server::x264Bitrate != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TightX264Encoder::mp4_write_callback(const void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
if (framelen + size > MAX_FRAMELEN)
|
|
||||||
vlog.error("Tried to write too large a frame, %lu bytes", framelen + size);
|
|
||||||
|
|
||||||
memcpy(&framebuf[framelen], buffer, size);
|
|
||||||
framelen += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TightX264Encoder::writeRect(const PixelBuffer* pb, const Palette& palette)
|
|
||||||
{
|
|
||||||
const rdr::U8* buffer;
|
|
||||||
int stride;
|
|
||||||
|
|
||||||
rdr::OutStream* os;
|
|
||||||
|
|
||||||
if (pb->width() < 320)
|
|
||||||
return; // Sometimes we get sent an 1x1 frame, or a cursor
|
|
||||||
|
|
||||||
uint32_t w, h;
|
|
||||||
w = pb->width();
|
|
||||||
h = pb->height();
|
|
||||||
|
|
||||||
os = conn->getOutStream();
|
|
||||||
|
|
||||||
if (using_nvidia) {
|
|
||||||
|
|
||||||
if (w != myw || h != myh) {
|
|
||||||
if (nvidia_init_done)
|
|
||||||
nvidia_unload();
|
|
||||||
nvidia_init_done = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nvidia_init_done) {
|
|
||||||
if (nvidia_init(w, h, rfb::Server::x264Bitrate,
|
|
||||||
rfb::Server::frameRate) != 0) {
|
|
||||||
vlog.error("nvidia init failed, disabling h264");
|
|
||||||
rfb::Server::x264Bitrate.setParam(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nvidia_init_done = true;
|
|
||||||
myw = w;
|
|
||||||
myh = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t cachelen;
|
|
||||||
const void *cachedata;
|
|
||||||
if (encCache->enabled &&
|
|
||||||
(cachedata = encCache->get(cacheType, framectr, 0, w, h, cachelen))) {
|
|
||||||
os->writeU8(tightX264 << 4);
|
|
||||||
writeCompact(cachelen, os);
|
|
||||||
os->writeBytes(cachedata, cachelen);
|
|
||||||
framectr++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyframe) {
|
|
||||||
framectr = 0;
|
|
||||||
keyframe = false;
|
|
||||||
|
|
||||||
free(mux->buf_header.buf);
|
|
||||||
free(mux->buf_mdat.buf);
|
|
||||||
free(mux->buf_moof.buf);
|
|
||||||
memset(mux, 0, sizeof(Mp4Context));
|
|
||||||
memset(muxstate, 0, sizeof(Mp4State));
|
|
||||||
}
|
|
||||||
|
|
||||||
mux->framerate = rfb::Server::frameRate;
|
|
||||||
mux->w = w;
|
|
||||||
mux->h = h;
|
|
||||||
|
|
||||||
buffer = pb->getBuffer(pb->getRect(), &stride);
|
|
||||||
|
|
||||||
if (!pfBGRX.equal(pb->getPF())) {
|
|
||||||
vlog.error("unsupported pixel format");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode
|
|
||||||
uint32_t bitlen;
|
|
||||||
if (nvenc_frame(buffer, framectr++, bitbuf, bitlen) != 0) {
|
|
||||||
vlog.error("encoding failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to parse NALs out of the stream
|
|
||||||
const uint8_t prefix[3] = { 0, 0, 1 };
|
|
||||||
const uint8_t *nalptr = bitbuf;
|
|
||||||
int i_nals = 0;
|
|
||||||
const uint8_t *nalstarts[32] = { NULL };
|
|
||||||
uint32_t nallens[32] = { 0 };
|
|
||||||
uint32_t remlen = bitlen;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
const uint8_t *next = (uint8_t *) memmem(nalptr, remlen, prefix, 3);
|
|
||||||
if (!next)
|
|
||||||
break;
|
|
||||||
|
|
||||||
remlen -= (next + 3) - nalptr;
|
|
||||||
nalptr = nalstarts[i_nals] = next + 3;
|
|
||||||
|
|
||||||
i_nals++;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Lens
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < i_nals; i++) {
|
|
||||||
if (i == i_nals - 1) {
|
|
||||||
nallens[i] = bitbuf + bitlen - nalstarts[i];
|
|
||||||
} else {
|
|
||||||
nallens[i] = nalstarts[i + 1] - nalstarts[i] - 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mux
|
|
||||||
framelen = 0;
|
|
||||||
os->writeU8(tightX264 << 4);
|
|
||||||
|
|
||||||
for (i = 0; i < i_nals; i++) {
|
|
||||||
uint32_t pack_len = nallens[i];
|
|
||||||
const uint8_t *pack_data = nalstarts[i];
|
|
||||||
|
|
||||||
struct NAL nal; nal_parse_header(&nal, pack_data[0]);
|
|
||||||
|
|
||||||
switch (nal.unit_type) {
|
|
||||||
case NalUnitType_SPS: { set_sps(mux, pack_data, pack_len); break; }
|
|
||||||
case NalUnitType_PPS: { set_pps(mux, pack_data, pack_len); break; }
|
|
||||||
case NalUnitType_CodedSliceIdr:
|
|
||||||
case NalUnitType_CodedSliceNonIdr: {
|
|
||||||
// Write all remaining NALs under the assumption they are the same type.
|
|
||||||
const uint32_t origlen = pack_len;
|
|
||||||
pack_len = bitbuf + bitlen - pack_data;
|
|
||||||
set_slice(mux, pack_data, origlen, pack_len, nal.unit_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nal.unit_type != NalUnitType_CodedSliceIdr &&
|
|
||||||
nal.unit_type != NalUnitType_CodedSliceNonIdr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
enum BufError err;
|
|
||||||
if (!muxstate->header_sent) {
|
|
||||||
struct BitBuf header_buf;
|
|
||||||
err = get_header(mux, &header_buf); chk_err_continue
|
|
||||||
mp4_write_callback(header_buf.buf, header_buf.offset);
|
|
||||||
|
|
||||||
muxstate->sequence_number = 1;
|
|
||||||
muxstate->base_data_offset = header_buf.offset;
|
|
||||||
muxstate->base_media_decode_time = 0;
|
|
||||||
muxstate->header_sent = true;
|
|
||||||
muxstate->nals_count = 0;
|
|
||||||
muxstate->default_sample_duration = default_sample_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = set_mp4_state(mux, muxstate); chk_err_continue
|
|
||||||
{
|
|
||||||
struct BitBuf moof_buf;
|
|
||||||
err = get_moof(mux, &moof_buf); chk_err_continue
|
|
||||||
mp4_write_callback(moof_buf.buf, moof_buf.offset);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
struct BitBuf mdat_buf;
|
|
||||||
err = get_mdat(mux, &mdat_buf); chk_err_continue
|
|
||||||
mp4_write_callback(mdat_buf.buf, mdat_buf.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encCache->enabled) {
|
|
||||||
void *tmp = malloc(framelen);
|
|
||||||
memcpy(tmp, framebuf, framelen);
|
|
||||||
encCache->add(cacheType, framectr, 0, w, h, framelen, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCompact(framelen, os);
|
|
||||||
os->writeBytes(framebuf, framelen);
|
|
||||||
} else {
|
|
||||||
w += w & 1;
|
|
||||||
h += h & 1;
|
|
||||||
|
|
||||||
params->i_width = w;
|
|
||||||
params->i_height = h;
|
|
||||||
|
|
||||||
x264_param_apply_profile(params, "baseline");
|
|
||||||
|
|
||||||
uint32_t cachelen;
|
|
||||||
const void *cachedata;
|
|
||||||
if (encCache->enabled &&
|
|
||||||
(cachedata = encCache->get(cacheType, framectr, 0, w, h, cachelen))) {
|
|
||||||
os->writeU8(tightX264 << 4);
|
|
||||||
writeCompact(cachelen, os);
|
|
||||||
os->writeBytes(cachedata, cachelen);
|
|
||||||
framectr++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyframe) {
|
|
||||||
framectr = 0;
|
|
||||||
keyframe = false;
|
|
||||||
|
|
||||||
free(mux->buf_header.buf);
|
|
||||||
free(mux->buf_mdat.buf);
|
|
||||||
free(mux->buf_moof.buf);
|
|
||||||
memset(mux, 0, sizeof(Mp4Context));
|
|
||||||
memset(muxstate, 0, sizeof(Mp4State));
|
|
||||||
}
|
|
||||||
|
|
||||||
mux->framerate = rfb::Server::frameRate;
|
|
||||||
mux->w = params->i_width;
|
|
||||||
mux->h = params->i_height;
|
|
||||||
|
|
||||||
if (!enc) {
|
|
||||||
enc = x264_encoder_open(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = pb->getBuffer(pb->getRect(), &stride);
|
|
||||||
|
|
||||||
// Convert it to yuv420 using libwebp's helper functions
|
|
||||||
WebPPicture pic;
|
|
||||||
|
|
||||||
WebPPictureInit(&pic);
|
|
||||||
pic.width = pb->getRect().width();
|
|
||||||
pic.height = pb->getRect().height();
|
|
||||||
|
|
||||||
bool freebuffer = false;
|
|
||||||
if (pic.width & 1 || pic.height & 1) {
|
|
||||||
// Expand to divisible-by-2 for x264
|
|
||||||
freebuffer = true;
|
|
||||||
const uint32_t oldw = pic.width;
|
|
||||||
const uint32_t oldh = pic.height;
|
|
||||||
pic.width += pic.width & 1;
|
|
||||||
pic.height += pic.height & 1;
|
|
||||||
stride = pic.width;
|
|
||||||
const rdr::U8 *oldbuffer = buffer;
|
|
||||||
buffer = (const rdr::U8*) calloc(pic.width * pic.height, 4);
|
|
||||||
|
|
||||||
uint32_t y;
|
|
||||||
for (y = 0; y < oldh; y++)
|
|
||||||
memcpy((void *) &buffer[y * stride * 4], &oldbuffer[y * oldw * 4], oldw * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pfRGBX.equal(pb->getPF())) {
|
|
||||||
WebPPictureImportRGBX(&pic, buffer, stride * 4);
|
|
||||||
} else if (pfBGRX.equal(pb->getPF())) {
|
|
||||||
WebPPictureImportBGRX(&pic, buffer, stride * 4);
|
|
||||||
} else {
|
|
||||||
rdr::U8* tmpbuf = new rdr::U8[pic.width * pic.height * 3];
|
|
||||||
pb->getPF().rgbFromBuffer(tmpbuf, (const rdr::U8 *) buffer, pic.width, stride, pic.height);
|
|
||||||
stride = pic.width * 3;
|
|
||||||
|
|
||||||
WebPPictureImportRGB(&pic, tmpbuf, stride);
|
|
||||||
delete [] tmpbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (freebuffer)
|
|
||||||
free((void *) buffer);
|
|
||||||
|
|
||||||
// Wrap
|
|
||||||
x264_picture_t pic_in, pic_out;
|
|
||||||
x264_picture_init(&pic_in);
|
|
||||||
|
|
||||||
pic_in.img.i_csp = X264_CSP_I420;
|
|
||||||
pic_in.img.i_plane = 3;
|
|
||||||
|
|
||||||
pic_in.img.plane[0] = pic.y;
|
|
||||||
pic_in.img.plane[1] = pic.u;
|
|
||||||
pic_in.img.plane[2] = pic.v;
|
|
||||||
|
|
||||||
pic_in.img.i_stride[0] = pic.y_stride;
|
|
||||||
pic_in.img.i_stride[1] = pic_in.img.i_stride[2] = pic.uv_stride;
|
|
||||||
|
|
||||||
pic_in.i_pts = framectr++;
|
|
||||||
|
|
||||||
// Encode
|
|
||||||
int i_nals;
|
|
||||||
x264_nal_t *nals;
|
|
||||||
const int len = x264_encoder_encode(enc, &nals, &i_nals, &pic_in, &pic_out);
|
|
||||||
|
|
||||||
if (len <= 0 || i_nals <= 0)
|
|
||||||
vlog.info("encoding error");
|
|
||||||
|
|
||||||
// Mux
|
|
||||||
framelen = 0;
|
|
||||||
os->writeU8(tightX264 << 4);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < i_nals; i++) {
|
|
||||||
uint32_t pack_len = nals[i].i_payload - 4;
|
|
||||||
const uint8_t *pack_data = nals[i].p_payload;
|
|
||||||
|
|
||||||
pack_data += 4; // Skip size
|
|
||||||
|
|
||||||
struct NAL nal; nal_parse_header(&nal, pack_data[0]);
|
|
||||||
|
|
||||||
switch (nal.unit_type) {
|
|
||||||
case NalUnitType_SPS: { set_sps(mux, pack_data, pack_len); break; }
|
|
||||||
case NalUnitType_PPS: { set_pps(mux, pack_data, pack_len); break; }
|
|
||||||
case NalUnitType_CodedSliceIdr:
|
|
||||||
case NalUnitType_CodedSliceNonIdr: {
|
|
||||||
// Write all remaining NALs under the assumption they are the same type.
|
|
||||||
const uint32_t origlen = pack_len;
|
|
||||||
int j;
|
|
||||||
for (j = i + 1; j < i_nals; j++)
|
|
||||||
pack_len += nals[j].i_payload;
|
|
||||||
set_slice(mux, pack_data, origlen, pack_len, nal.unit_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nal.unit_type != NalUnitType_CodedSliceIdr &&
|
|
||||||
nal.unit_type != NalUnitType_CodedSliceNonIdr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
enum BufError err;
|
|
||||||
if (!muxstate->header_sent) {
|
|
||||||
struct BitBuf header_buf;
|
|
||||||
err = get_header(mux, &header_buf); chk_err_continue
|
|
||||||
mp4_write_callback(header_buf.buf, header_buf.offset);
|
|
||||||
|
|
||||||
muxstate->sequence_number = 1;
|
|
||||||
muxstate->base_data_offset = header_buf.offset;
|
|
||||||
muxstate->base_media_decode_time = 0;
|
|
||||||
muxstate->header_sent = true;
|
|
||||||
muxstate->nals_count = 0;
|
|
||||||
muxstate->default_sample_duration = default_sample_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = set_mp4_state(mux, muxstate); chk_err_continue
|
|
||||||
{
|
|
||||||
struct BitBuf moof_buf;
|
|
||||||
err = get_moof(mux, &moof_buf); chk_err_continue
|
|
||||||
mp4_write_callback(moof_buf.buf, moof_buf.offset);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
struct BitBuf mdat_buf;
|
|
||||||
err = get_mdat(mux, &mdat_buf); chk_err_continue
|
|
||||||
mp4_write_callback(mdat_buf.buf, mdat_buf.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encCache->enabled) {
|
|
||||||
void *tmp = malloc(framelen);
|
|
||||||
memcpy(tmp, framebuf, framelen);
|
|
||||||
encCache->add(cacheType, framectr, 0, w, h, framelen, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCompact(framelen, os);
|
|
||||||
os->writeBytes(framebuf, framelen);
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
WebPPictureFree(&pic);
|
|
||||||
x264_encoder_close(enc);
|
|
||||||
enc = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TightX264Encoder::writeSolidRect(int width, int height,
|
|
||||||
const PixelFormat& pf,
|
|
||||||
const rdr::U8* colour)
|
|
||||||
{
|
|
||||||
// FIXME: Add a shortcut in the X264 compressor to handle this case
|
|
||||||
// without having to use the default fallback which is very slow.
|
|
||||||
Encoder::writeSolidRect(width, height, pf, colour);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TightX264Encoder::writeCompact(rdr::U32 value, rdr::OutStream* os) const
|
|
||||||
{
|
|
||||||
// Copied from TightEncoder as it's overkill to inherit just for this
|
|
||||||
rdr::U8 b;
|
|
||||||
|
|
||||||
b = value & 0x7F;
|
|
||||||
if (value <= 0x7F) {
|
|
||||||
os->writeU8(b);
|
|
||||||
} else {
|
|
||||||
os->writeU8(b | 0x80);
|
|
||||||
b = value >> 7 & 0x7F;
|
|
||||||
if (value <= 0x3FFF) {
|
|
||||||
os->writeU8(b);
|
|
||||||
} else {
|
|
||||||
os->writeU8(b | 0x80);
|
|
||||||
os->writeU8(value >> 14 & 0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TightX264Encoder::tryInit(const PixelBuffer* pb) {
|
|
||||||
if (nvidia_init_done)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint32_t w, h;
|
|
||||||
w = pb->width();
|
|
||||||
h = pb->height();
|
|
||||||
|
|
||||||
if (skip_nvidia || nvidia_init(w, h, rfb::Server::x264Bitrate,
|
|
||||||
rfb::Server::frameRate) != 0) {
|
|
||||||
if (!skip_nvidia)
|
|
||||||
vlog.error("nvidia init failed, falling back to x264");
|
|
||||||
using_nvidia = false;
|
|
||||||
nvidia_init_done = true;
|
|
||||||
skip_nvidia = true;
|
|
||||||
myw = w;
|
|
||||||
myh = h;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
nvidia_init_done = true;
|
|
||||||
myw = w;
|
|
||||||
myh = h;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
|
||||||
* Copyright (C) 2011 D. R. Commander
|
|
||||||
* Copyright 2014 Pierre Ossman for Cendio AB
|
|
||||||
*
|
|
||||||
* This is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this software; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
||||||
* USA.
|
|
||||||
*/
|
|
||||||
#ifndef __RFB_TIGHTX264ENCODER_H__
|
|
||||||
#define __RFB_TIGHTX264ENCODER_H__
|
|
||||||
|
|
||||||
#include <rfb/Encoder.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct x264_t;
|
|
||||||
struct x264_param_t;
|
|
||||||
struct Mp4Context;
|
|
||||||
struct Mp4State;
|
|
||||||
|
|
||||||
namespace rfb {
|
|
||||||
|
|
||||||
class EncCache;
|
|
||||||
|
|
||||||
class TightX264Encoder : public Encoder {
|
|
||||||
public:
|
|
||||||
TightX264Encoder(SConnection* conn, EncCache *encCache, uint8_t cacheType);
|
|
||||||
virtual ~TightX264Encoder();
|
|
||||||
|
|
||||||
virtual bool isSupported();
|
|
||||||
|
|
||||||
virtual void setQualityLevel(int level) {}
|
|
||||||
virtual void setFineQualityLevel(int quality, int subsampling) {}
|
|
||||||
|
|
||||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
|
||||||
virtual void writeSolidRect(int width, int height,
|
|
||||||
const PixelFormat& pf,
|
|
||||||
const rdr::U8* colour);
|
|
||||||
|
|
||||||
virtual void setKeyframe() { keyframe = true; }
|
|
||||||
|
|
||||||
bool tryInit(const PixelBuffer* pb);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void writeCompact(rdr::U32 value, rdr::OutStream* os) const;
|
|
||||||
void mp4_write_callback(const void *buffer, size_t size);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool keyframe;
|
|
||||||
x264_t *enc;
|
|
||||||
x264_param_t *params;
|
|
||||||
Mp4Context *mux;
|
|
||||||
Mp4State *muxstate;
|
|
||||||
unsigned framectr;
|
|
||||||
|
|
||||||
bool nvidia_init_done;
|
|
||||||
bool using_nvidia;
|
|
||||||
static bool skip_nvidia;
|
|
||||||
|
|
||||||
EncCache *encCache;
|
|
||||||
uint8_t cacheType;
|
|
||||||
public:
|
|
||||||
uint8_t *framebuf;
|
|
||||||
uint32_t framelen;
|
|
||||||
|
|
||||||
uint8_t *bitbuf;
|
|
||||||
|
|
||||||
uint16_t myw, myh;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
* USA.
|
* USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <network/GetAPI.h>
|
||||||
#include <network/TcpSocket.h>
|
#include <network/TcpSocket.h>
|
||||||
|
|
||||||
#include <rfb/ComparingUpdateTracker.h>
|
#include <rfb/ComparingUpdateTracker.h>
|
||||||
@@ -61,7 +62,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||||
needsPermCheck(false), pointerEventTime(0),
|
needsPermCheck(false), pointerEventTime(0),
|
||||||
clientHasCursor(false),
|
clientHasCursor(false),
|
||||||
accessRights(AccessDefault), startTime(time(0))
|
accessRights(AccessDefault), startTime(time(0)), frameTracking(false)
|
||||||
{
|
{
|
||||||
setStreams(&sock->inStream(), &sock->outStream());
|
setStreams(&sock->inStream(), &sock->outStream());
|
||||||
peerEndpoint.buf = sock->getPeerEndpoint();
|
peerEndpoint.buf = sock->getPeerEndpoint();
|
||||||
@@ -98,6 +99,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
gettimeofday(&lastKeyEvent, NULL);
|
gettimeofday(&lastKeyEvent, NULL);
|
||||||
|
|
||||||
server->clients.push_front(this);
|
server->clients.push_front(this);
|
||||||
|
|
||||||
|
if (server->apimessager)
|
||||||
|
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -128,6 +132,11 @@ VNCSConnectionST::~VNCSConnectionST()
|
|||||||
server->clients.remove(this);
|
server->clients.remove(this);
|
||||||
|
|
||||||
delete [] fenceData;
|
delete [] fenceData;
|
||||||
|
|
||||||
|
if (server->apimessager) {
|
||||||
|
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
|
||||||
|
server->apimessager->mainClearBottleneckStats(peerEndpoint.buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -567,6 +576,7 @@ bool VNCSConnectionST::needRenderedCursor()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!cp.supportsLocalCursorWithAlpha &&
|
if (!cp.supportsLocalCursorWithAlpha &&
|
||||||
|
!cp.supportsVMWareCursor &&
|
||||||
!cp.supportsLocalCursor && !cp.supportsLocalXCursor)
|
!cp.supportsLocalCursor && !cp.supportsLocalXCursor)
|
||||||
return true;
|
return true;
|
||||||
if (!server->cursorPos.equals(pointerEventPos) &&
|
if (!server->cursorPos.equals(pointerEventPos) &&
|
||||||
@@ -1184,6 +1194,7 @@ bool VNCSConnectionST::isCongested()
|
|||||||
void VNCSConnectionST::writeFramebufferUpdate()
|
void VNCSConnectionST::writeFramebufferUpdate()
|
||||||
{
|
{
|
||||||
congestion.updatePosition(sock->outStream().length());
|
congestion.updatePosition(sock->outStream().length());
|
||||||
|
encodeManager.clearEncodingTime();
|
||||||
|
|
||||||
// We're in the middle of processing a command that's supposed to be
|
// We're in the middle of processing a command that's supposed to be
|
||||||
// synchronised. Allowing an update to slip out right now might violate
|
// synchronised. Allowing an update to slip out right now might violate
|
||||||
@@ -1229,6 +1240,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
|||||||
// window.
|
// window.
|
||||||
sock->cork(true);
|
sock->cork(true);
|
||||||
|
|
||||||
|
if (frameTracking)
|
||||||
|
writer()->writeRequestFrameStats();
|
||||||
|
|
||||||
// First take care of any updates that cannot contain framebuffer data
|
// First take care of any updates that cannot contain framebuffer data
|
||||||
// changes.
|
// changes.
|
||||||
writeNoDataUpdate();
|
writeNoDataUpdate();
|
||||||
@@ -1459,7 +1473,7 @@ static void pruneStatList(std::list<struct timeval> &list, const struct timeval
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCSConnectionST::sendStats() {
|
void VNCSConnectionST::sendStats(const bool toClient) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
|
||||||
@@ -1498,8 +1512,28 @@ void VNCSConnectionST::sendStats() {
|
|||||||
|
|
||||||
#undef ten
|
#undef ten
|
||||||
|
|
||||||
vlog.info("Sending client stats:\n%s\n", buf);
|
if (toClient) {
|
||||||
writer()->writeStats(buf, strlen(buf));
|
vlog.info("Sending client stats:\n%s\n", buf);
|
||||||
|
writer()->writeStats(buf, strlen(buf));
|
||||||
|
} else if (server->apimessager) {
|
||||||
|
server->apimessager->mainUpdateBottleneckStats(peerEndpoint.buf, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCSConnectionST::handleFrameStats(rdr::U32 all, rdr::U32 render)
|
||||||
|
{
|
||||||
|
if (server->apimessager) {
|
||||||
|
const char *at = strchr(peerEndpoint.buf, '@');
|
||||||
|
if (!at)
|
||||||
|
at = peerEndpoint.buf;
|
||||||
|
else
|
||||||
|
at++;
|
||||||
|
|
||||||
|
server->apimessager->mainUpdateClientFrameStats(at, render, all,
|
||||||
|
congestion.getPingTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
frameTracking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setCursor() is called whenever the cursor has changed shape or pixel format.
|
// setCursor() is called whenever the cursor has changed shape or pixel format.
|
||||||
@@ -1520,11 +1554,13 @@ void VNCSConnectionST::setCursor()
|
|||||||
clientHasCursor = true;
|
clientHasCursor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!writer()->writeSetCursorWithAlpha()) {
|
if (!writer()->writeSetVMwareCursor()) {
|
||||||
if (!writer()->writeSetCursor()) {
|
if (!writer()->writeSetCursorWithAlpha()) {
|
||||||
if (!writer()->writeSetXCursor()) {
|
if (!writer()->writeSetCursor()) {
|
||||||
// No client support
|
if (!writer()->writeSetXCursor()) {
|
||||||
return;
|
// No client support
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1611,3 +1647,15 @@ int VNCSConnectionST::getStatus()
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VNCSConnectionST::checkOwnerConn() const
|
||||||
|
{
|
||||||
|
std::list<VNCSConnectionST*>::const_iterator it;
|
||||||
|
|
||||||
|
for (it = server->clients.begin(); it != server->clients.end(); it++) {
|
||||||
|
bool write, owner;
|
||||||
|
if ((*it)->getPerms(write, owner) && owner)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -164,6 +164,35 @@ namespace rfb {
|
|||||||
void setStatus(int status);
|
void setStatus(int status);
|
||||||
int getStatus();
|
int getStatus();
|
||||||
|
|
||||||
|
virtual void sendStats(const bool toClient = true);
|
||||||
|
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render);
|
||||||
|
|
||||||
|
bool is_owner() const {
|
||||||
|
bool write, owner;
|
||||||
|
if (getPerms(write, owner) && owner)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFrameTracking() {
|
||||||
|
frameTracking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeManager::codecstats_t getJpegStats() const {
|
||||||
|
return encodeManager.jpegstats;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeManager::codecstats_t getWebpStats() const {
|
||||||
|
return encodeManager.webpstats;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getEncodingTime() const {
|
||||||
|
return encodeManager.getEncodingTime();
|
||||||
|
}
|
||||||
|
unsigned getScalingTime() const {
|
||||||
|
return encodeManager.getScalingTime();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// SConnection callbacks
|
// SConnection callbacks
|
||||||
|
|
||||||
@@ -191,7 +220,6 @@ namespace rfb {
|
|||||||
virtual void supportsContinuousUpdates();
|
virtual void supportsContinuousUpdates();
|
||||||
virtual void supportsLEDState();
|
virtual void supportsLEDState();
|
||||||
|
|
||||||
virtual void sendStats();
|
|
||||||
virtual bool canChangeKasmSettings() const {
|
virtual bool canChangeKasmSettings() const {
|
||||||
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
||||||
(AccessPtrEvents | AccessKeyEvents);
|
(AccessPtrEvents | AccessKeyEvents);
|
||||||
@@ -219,6 +247,8 @@ namespace rfb {
|
|||||||
|
|
||||||
bool getPerms(bool &write, bool &owner) const;
|
bool getPerms(bool &write, bool &owner) const;
|
||||||
|
|
||||||
|
bool checkOwnerConn() const;
|
||||||
|
|
||||||
// Congestion control
|
// Congestion control
|
||||||
void writeRTTPing();
|
void writeRTTPing();
|
||||||
bool isCongested();
|
bool isCongested();
|
||||||
@@ -294,6 +324,8 @@ namespace rfb {
|
|||||||
time_t startTime;
|
time_t startTime;
|
||||||
|
|
||||||
std::vector<CopyPassRect> copypassed;
|
std::vector<CopyPassRect> copypassed;
|
||||||
|
|
||||||
|
bool frameTracking;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
|
|
||||||
#include <network/GetAPI.h>
|
#include <network/GetAPI.h>
|
||||||
|
|
||||||
|
#include <rfb/cpuid.h>
|
||||||
#include <rfb/ComparingUpdateTracker.h>
|
#include <rfb/ComparingUpdateTracker.h>
|
||||||
#include <rfb/KeyRemapper.h>
|
#include <rfb/KeyRemapper.h>
|
||||||
#include <rfb/ListConnInfo.h>
|
#include <rfb/ListConnInfo.h>
|
||||||
@@ -76,6 +77,8 @@ static LogWriter slog("VNCServerST");
|
|||||||
LogWriter VNCServerST::connectionsLog("Connections");
|
LogWriter VNCServerST::connectionsLog("Connections");
|
||||||
EncCache VNCServerST::encCache;
|
EncCache VNCServerST::encCache;
|
||||||
|
|
||||||
|
void SelfBench();
|
||||||
|
|
||||||
//
|
//
|
||||||
// -=- VNCServerST Implementation
|
// -=- VNCServerST Implementation
|
||||||
//
|
//
|
||||||
@@ -128,10 +131,13 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
|||||||
renderedCursorInvalid(false),
|
renderedCursorInvalid(false),
|
||||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||||
lastConnectionTime(0), disableclients(false),
|
lastConnectionTime(0), disableclients(false),
|
||||||
frameTimer(this), apimessager(NULL)
|
frameTimer(this), apimessager(NULL), trackingFrameStats(0)
|
||||||
{
|
{
|
||||||
lastUserInputTime = lastDisconnectTime = time(0);
|
lastUserInputTime = lastDisconnectTime = time(0);
|
||||||
slog.debug("creating single-threaded server %s", name.buf);
|
slog.debug("creating single-threaded server %s", name.buf);
|
||||||
|
slog.info("CPU capability: SSE2 %s, AVX512f %s",
|
||||||
|
supportsSSE2() ? "yes" : "no",
|
||||||
|
supportsAVX512f() ? "yes" : "no");
|
||||||
|
|
||||||
DLPRegion.enabled = DLPRegion.percents = false;
|
DLPRegion.enabled = DLPRegion.percents = false;
|
||||||
|
|
||||||
@@ -210,6 +216,11 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
|||||||
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||||
slog.error("Failed to set watch");
|
slog.error("Failed to set watch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackingClient[0] = 0;
|
||||||
|
|
||||||
|
if (Server::selfBench)
|
||||||
|
SelfBench();
|
||||||
}
|
}
|
||||||
|
|
||||||
VNCServerST::~VNCServerST()
|
VNCServerST::~VNCServerST()
|
||||||
@@ -271,6 +282,11 @@ void VNCServerST::removeSocket(network::Socket* sock) {
|
|||||||
std::list<VNCSConnectionST*>::iterator ci;
|
std::list<VNCSConnectionST*>::iterator ci;
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci++) {
|
for (ci = clients.begin(); ci != clients.end(); ci++) {
|
||||||
if ((*ci)->getSock() == sock) {
|
if ((*ci)->getSock() == sock) {
|
||||||
|
|
||||||
|
if (clipboardClient == *ci)
|
||||||
|
handleClipboardAnnounce(*ci, false);
|
||||||
|
clipboardRequestors.remove(*ci);
|
||||||
|
|
||||||
// - Delete the per-Socket resources
|
// - Delete the per-Socket resources
|
||||||
delete *ci;
|
delete *ci;
|
||||||
|
|
||||||
@@ -774,7 +790,8 @@ int VNCServerST::msToNextUpdate()
|
|||||||
return frameTimer.getRemainingMs();
|
return frameTimer.getRemainingMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
static void checkAPIMessages(network::GetAPIMessager *apimessager,
|
||||||
|
rdr::U8 &trackingFrameStats, char trackingClient[])
|
||||||
{
|
{
|
||||||
if (pthread_mutex_lock(&apimessager->userMutex))
|
if (pthread_mutex_lock(&apimessager->userMutex))
|
||||||
return;
|
return;
|
||||||
@@ -827,6 +844,20 @@ static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
|||||||
slog.error("Tried to give control to nonexistent user %s", act.data.user);
|
slog.error("Tried to give control to nonexistent user %s", act.data.user);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_ALL:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_OWNER:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
memcpy(trackingClient, act.data.password, 128);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set) {
|
if (set) {
|
||||||
@@ -923,6 +954,9 @@ void VNCServerST::writeUpdate()
|
|||||||
assert(blockCounter == 0);
|
assert(blockCounter == 0);
|
||||||
assert(desktopStarted);
|
assert(desktopStarted);
|
||||||
|
|
||||||
|
struct timeval start;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
|
||||||
if (DLPRegion.enabled) {
|
if (DLPRegion.enabled) {
|
||||||
comparer->enable_copyrect(false);
|
comparer->enable_copyrect(false);
|
||||||
blackOut();
|
blackOut();
|
||||||
@@ -949,6 +983,9 @@ void VNCServerST::writeUpdate()
|
|||||||
else
|
else
|
||||||
comparer->disable();
|
comparer->disable();
|
||||||
|
|
||||||
|
struct timeval beforeAnalysis;
|
||||||
|
gettimeofday(&beforeAnalysis, NULL);
|
||||||
|
|
||||||
// Skip scroll detection if the client is slow, and didn't get the previous one yet
|
// Skip scroll detection if the client is slow, and didn't get the previous one yet
|
||||||
if (comparer->compare(clients.size() == 1 && (*clients.begin())->has_copypassed(),
|
if (comparer->compare(clients.size() == 1 && (*clients.begin())->has_copypassed(),
|
||||||
cursorReg))
|
cursorReg))
|
||||||
@@ -956,6 +993,8 @@ void VNCServerST::writeUpdate()
|
|||||||
|
|
||||||
comparer->clear();
|
comparer->clear();
|
||||||
|
|
||||||
|
const unsigned analysisMs = msSince(&beforeAnalysis);
|
||||||
|
|
||||||
encCache.clear();
|
encCache.clear();
|
||||||
encCache.enabled = clients.size() > 1;
|
encCache.enabled = clients.size() > 1;
|
||||||
|
|
||||||
@@ -981,11 +1020,22 @@ void VNCServerST::writeUpdate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned shottime = 0;
|
||||||
if (apimessager) {
|
if (apimessager) {
|
||||||
|
struct timeval shotstart;
|
||||||
|
gettimeofday(&shotstart, NULL);
|
||||||
apimessager->mainUpdateScreen(pb);
|
apimessager->mainUpdateScreen(pb);
|
||||||
|
shottime = msSince(&shotstart);
|
||||||
|
|
||||||
checkAPIMessages(apimessager);
|
trackingFrameStats = 0;
|
||||||
|
checkAPIMessages(apimessager, trackingFrameStats, trackingClient);
|
||||||
}
|
}
|
||||||
|
const rdr::U8 origtrackingFrameStats = trackingFrameStats;
|
||||||
|
|
||||||
|
EncodeManager::codecstats_t jpegstats, webpstats;
|
||||||
|
unsigned enctime = 0, scaletime = 0;
|
||||||
|
memset(&jpegstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||||
|
memset(&webpstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||||
|
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||||
ci_next = ci; ci_next++;
|
ci_next = ci; ci_next++;
|
||||||
@@ -993,10 +1043,68 @@ void VNCServerST::writeUpdate()
|
|||||||
if (permcheck)
|
if (permcheck)
|
||||||
(*ci)->recheckPerms();
|
(*ci)->recheckPerms();
|
||||||
|
|
||||||
|
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_ALL ||
|
||||||
|
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER &&
|
||||||
|
(*ci)->is_owner()) ||
|
||||||
|
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC &&
|
||||||
|
strstr((*ci)->getPeerEndpoint(), trackingClient))) {
|
||||||
|
|
||||||
|
(*ci)->setFrameTracking();
|
||||||
|
|
||||||
|
// Only one owner
|
||||||
|
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER)
|
||||||
|
trackingFrameStats = network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY;
|
||||||
|
}
|
||||||
|
|
||||||
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
||||||
(*ci)->add_copypassed(ui.copypassed);
|
(*ci)->add_copypassed(ui.copypassed);
|
||||||
(*ci)->add_changed(ui.changed);
|
(*ci)->add_changed(ui.changed);
|
||||||
(*ci)->writeFramebufferUpdateOrClose();
|
(*ci)->writeFramebufferUpdateOrClose();
|
||||||
|
|
||||||
|
if (apimessager) {
|
||||||
|
(*ci)->sendStats(false);
|
||||||
|
const EncodeManager::codecstats_t subjpeg = (*ci)->getJpegStats();
|
||||||
|
const EncodeManager::codecstats_t subwebp = (*ci)->getWebpStats();
|
||||||
|
|
||||||
|
jpegstats.ms += subjpeg.ms;
|
||||||
|
jpegstats.area += subjpeg.area;
|
||||||
|
jpegstats.rects += subjpeg.rects;
|
||||||
|
|
||||||
|
webpstats.ms += subwebp.ms;
|
||||||
|
webpstats.area += subwebp.area;
|
||||||
|
webpstats.rects += subwebp.rects;
|
||||||
|
|
||||||
|
enctime += (*ci)->getEncodingTime();
|
||||||
|
scaletime += (*ci)->getScalingTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackingFrameStats) {
|
||||||
|
if (enctime) {
|
||||||
|
const unsigned totalMs = msSince(&start);
|
||||||
|
|
||||||
|
if (apimessager)
|
||||||
|
apimessager->mainUpdateServerFrameStats(comparer->changedPerc, totalMs,
|
||||||
|
jpegstats.ms, webpstats.ms,
|
||||||
|
analysisMs,
|
||||||
|
jpegstats.area, webpstats.area,
|
||||||
|
jpegstats.rects, webpstats.rects,
|
||||||
|
enctime, scaletime, shottime,
|
||||||
|
pb->getRect().width(),
|
||||||
|
pb->getRect().height());
|
||||||
|
} else {
|
||||||
|
// Zero encoding time means this was a no-data frame; restore the stats request
|
||||||
|
if (apimessager && pthread_mutex_lock(&apimessager->userMutex) == 0) {
|
||||||
|
|
||||||
|
network::GetAPIMessager::action_data act;
|
||||||
|
act.action = (network::GetAPIMessager::USER_ACTION) origtrackingFrameStats;
|
||||||
|
memcpy(act.data.password, trackingClient, 128);
|
||||||
|
|
||||||
|
apimessager->actionQueue.push_back(act);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&apimessager->userMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ namespace rfb {
|
|||||||
|
|
||||||
network::GetAPIMessager *apimessager;
|
network::GetAPIMessager *apimessager;
|
||||||
|
|
||||||
|
rdr::U8 trackingFrameStats;
|
||||||
|
char trackingClient[128];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int x1, y1, x2, y2;
|
int x1, y1, x2, y2;
|
||||||
|
|||||||
70
common/rfb/cpuid.cxx
Normal file
70
common/rfb/cpuid.cxx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/* Copyright (C) 2021 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 <stdint.h>
|
||||||
|
|
||||||
|
static uint32_t cpuid[4] = { 0 };
|
||||||
|
static uint32_t extcpuid[4] = { 0 };
|
||||||
|
|
||||||
|
static void getcpuid() {
|
||||||
|
if (cpuid[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if defined(__x86_64__) || defined(__i386__)
|
||||||
|
uint32_t eax, ecx = 0;
|
||||||
|
|
||||||
|
eax = 1; // normal feature bits
|
||||||
|
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"cpuid\n\t"
|
||||||
|
: "=a"(cpuid[0]), "=b"(cpuid[1]), "=c"(cpuid[2]), "=d"(cpuid[3])
|
||||||
|
: "0"(eax), "2"(ecx)
|
||||||
|
);
|
||||||
|
|
||||||
|
eax = 7; // ext feature bits
|
||||||
|
ecx = 0;
|
||||||
|
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"cpuid\n\t"
|
||||||
|
: "=a"(extcpuid[0]), "=b"(extcpuid[1]), "=c"(extcpuid[2]), "=d"(extcpuid[3])
|
||||||
|
: "0"(eax), "2"(ecx)
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace rfb {
|
||||||
|
|
||||||
|
bool supportsSSE2() {
|
||||||
|
getcpuid();
|
||||||
|
#if defined(__x86_64__) || defined(__i386__)
|
||||||
|
#define bit_SSE2 (1 << 26)
|
||||||
|
return cpuid[3] & bit_SSE2;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool supportsAVX512f() {
|
||||||
|
getcpuid();
|
||||||
|
#if defined(__x86_64__) || defined(__i386__)
|
||||||
|
#define bit_AVX512f (1 << 16)
|
||||||
|
return extcpuid[1] & bit_AVX512f;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace rfb
|
||||||
28
common/rfb/cpuid.h
Normal file
28
common/rfb/cpuid.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RFB_CPUID_H__
|
||||||
|
#define __RFB_CPUID_H__
|
||||||
|
|
||||||
|
namespace rfb {
|
||||||
|
|
||||||
|
bool supportsSSE2();
|
||||||
|
bool supportsAVX512f();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,423 +0,0 @@
|
|||||||
/*
|
|
||||||
* This copyright notice applies to this header file only:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2016
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation
|
|
||||||
* files (the "Software"), to deal in the Software without
|
|
||||||
* restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the software, and to permit persons to whom the
|
|
||||||
* software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(FFNV_DYNLINK_CUDA_H) && !defined(CUDA_VERSION)
|
|
||||||
#define FFNV_DYNLINK_CUDA_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#define CUDA_VERSION 7050
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
||||||
#define CUDAAPI __stdcall
|
|
||||||
#else
|
|
||||||
#define CUDAAPI
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CU_CTX_SCHED_BLOCKING_SYNC 4
|
|
||||||
|
|
||||||
typedef int CUdevice;
|
|
||||||
#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) || defined(__LP64__) || defined(__aarch64__)
|
|
||||||
typedef unsigned long long CUdeviceptr;
|
|
||||||
#else
|
|
||||||
typedef unsigned int CUdeviceptr;
|
|
||||||
#endif
|
|
||||||
typedef unsigned long long CUtexObject;
|
|
||||||
|
|
||||||
typedef struct CUarray_st *CUarray;
|
|
||||||
typedef struct CUctx_st *CUcontext;
|
|
||||||
typedef struct CUstream_st *CUstream;
|
|
||||||
typedef struct CUevent_st *CUevent;
|
|
||||||
typedef struct CUfunc_st *CUfunction;
|
|
||||||
typedef struct CUmod_st *CUmodule;
|
|
||||||
typedef struct CUmipmappedArray_st *CUmipmappedArray;
|
|
||||||
typedef struct CUgraphicsResource_st *CUgraphicsResource;
|
|
||||||
typedef struct CUextMemory_st *CUexternalMemory;
|
|
||||||
typedef struct CUextSemaphore_st *CUexternalSemaphore;
|
|
||||||
|
|
||||||
typedef struct CUlinkState_st *CUlinkState;
|
|
||||||
|
|
||||||
typedef enum cudaError_enum {
|
|
||||||
CUDA_SUCCESS = 0,
|
|
||||||
CUDA_ERROR_NOT_READY = 600
|
|
||||||
} CUresult;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Device properties (subset)
|
|
||||||
*/
|
|
||||||
typedef enum CUdevice_attribute_enum {
|
|
||||||
CU_DEVICE_ATTRIBUTE_CLOCK_RATE = 13,
|
|
||||||
CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT = 14,
|
|
||||||
CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT = 16,
|
|
||||||
CU_DEVICE_ATTRIBUTE_INTEGRATED = 18,
|
|
||||||
CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY = 19,
|
|
||||||
CU_DEVICE_ATTRIBUTE_COMPUTE_MODE = 20,
|
|
||||||
CU_DEVICE_ATTRIBUTE_CONCURRENT_KERNELS = 31,
|
|
||||||
CU_DEVICE_ATTRIBUTE_PCI_BUS_ID = 33,
|
|
||||||
CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID = 34,
|
|
||||||
CU_DEVICE_ATTRIBUTE_TCC_DRIVER = 35,
|
|
||||||
CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE = 36,
|
|
||||||
CU_DEVICE_ATTRIBUTE_GLOBAL_MEMORY_BUS_WIDTH = 37,
|
|
||||||
CU_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT = 40,
|
|
||||||
CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING = 41,
|
|
||||||
CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID = 50,
|
|
||||||
CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT = 51,
|
|
||||||
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75,
|
|
||||||
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR = 76,
|
|
||||||
CU_DEVICE_ATTRIBUTE_MANAGED_MEMORY = 83,
|
|
||||||
CU_DEVICE_ATTRIBUTE_MULTI_GPU_BOARD = 84,
|
|
||||||
CU_DEVICE_ATTRIBUTE_MULTI_GPU_BOARD_GROUP_ID = 85,
|
|
||||||
} CUdevice_attribute;
|
|
||||||
|
|
||||||
typedef enum CUarray_format_enum {
|
|
||||||
CU_AD_FORMAT_UNSIGNED_INT8 = 0x01,
|
|
||||||
CU_AD_FORMAT_UNSIGNED_INT16 = 0x02,
|
|
||||||
CU_AD_FORMAT_UNSIGNED_INT32 = 0x03,
|
|
||||||
CU_AD_FORMAT_SIGNED_INT8 = 0x08,
|
|
||||||
CU_AD_FORMAT_SIGNED_INT16 = 0x09,
|
|
||||||
CU_AD_FORMAT_SIGNED_INT32 = 0x0a,
|
|
||||||
CU_AD_FORMAT_HALF = 0x10,
|
|
||||||
CU_AD_FORMAT_FLOAT = 0x20
|
|
||||||
} CUarray_format;
|
|
||||||
|
|
||||||
typedef enum CUmemorytype_enum {
|
|
||||||
CU_MEMORYTYPE_HOST = 1,
|
|
||||||
CU_MEMORYTYPE_DEVICE = 2,
|
|
||||||
CU_MEMORYTYPE_ARRAY = 3
|
|
||||||
} CUmemorytype;
|
|
||||||
|
|
||||||
typedef enum CUlimit_enum {
|
|
||||||
CU_LIMIT_STACK_SIZE = 0,
|
|
||||||
CU_LIMIT_PRINTF_FIFO_SIZE = 1,
|
|
||||||
CU_LIMIT_MALLOC_HEAP_SIZE = 2,
|
|
||||||
CU_LIMIT_DEV_RUNTIME_SYNC_DEPTH = 3,
|
|
||||||
CU_LIMIT_DEV_RUNTIME_PENDING_LAUNCH_COUNT = 4
|
|
||||||
} CUlimit;
|
|
||||||
|
|
||||||
typedef enum CUresourcetype_enum {
|
|
||||||
CU_RESOURCE_TYPE_ARRAY = 0x00,
|
|
||||||
CU_RESOURCE_TYPE_MIPMAPPED_ARRAY = 0x01,
|
|
||||||
CU_RESOURCE_TYPE_LINEAR = 0x02,
|
|
||||||
CU_RESOURCE_TYPE_PITCH2D = 0x03
|
|
||||||
} CUresourcetype;
|
|
||||||
|
|
||||||
typedef enum CUaddress_mode_enum {
|
|
||||||
CU_TR_ADDRESS_MODE_WRAP = 0,
|
|
||||||
CU_TR_ADDRESS_MODE_CLAMP = 1,
|
|
||||||
CU_TR_ADDRESS_MODE_MIRROR = 2,
|
|
||||||
CU_TR_ADDRESS_MODE_BORDER = 3
|
|
||||||
} CUaddress_mode;
|
|
||||||
|
|
||||||
typedef enum CUfilter_mode_enum {
|
|
||||||
CU_TR_FILTER_MODE_POINT = 0,
|
|
||||||
CU_TR_FILTER_MODE_LINEAR = 1
|
|
||||||
} CUfilter_mode;
|
|
||||||
|
|
||||||
typedef enum CUgraphicsRegisterFlags_enum {
|
|
||||||
CU_GRAPHICS_REGISTER_FLAGS_NONE = 0,
|
|
||||||
CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 1,
|
|
||||||
CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 2,
|
|
||||||
CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 4,
|
|
||||||
CU_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 8
|
|
||||||
} CUgraphicsRegisterFlags;
|
|
||||||
|
|
||||||
typedef enum CUexternalMemoryHandleType_enum {
|
|
||||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD = 1,
|
|
||||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32 = 2,
|
|
||||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT = 3,
|
|
||||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP = 4,
|
|
||||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE = 5,
|
|
||||||
} CUexternalMemoryHandleType;
|
|
||||||
|
|
||||||
typedef enum CUexternalSemaphoreHandleType_enum {
|
|
||||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD = 1,
|
|
||||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32 = 2,
|
|
||||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT = 3,
|
|
||||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE = 4
|
|
||||||
} CUexternalSemaphoreHandleType;
|
|
||||||
|
|
||||||
typedef enum CUjit_option_enum
|
|
||||||
{
|
|
||||||
CU_JIT_MAX_REGISTERS = 0,
|
|
||||||
CU_JIT_THREADS_PER_BLOCK = 1,
|
|
||||||
CU_JIT_WALL_TIME = 2,
|
|
||||||
CU_JIT_INFO_LOG_BUFFER = 3,
|
|
||||||
CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES = 4,
|
|
||||||
CU_JIT_ERROR_LOG_BUFFER = 5,
|
|
||||||
CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES = 6,
|
|
||||||
CU_JIT_OPTIMIZATION_LEVEL = 7,
|
|
||||||
CU_JIT_TARGET_FROM_CUCONTEXT = 8,
|
|
||||||
CU_JIT_TARGET = 9,
|
|
||||||
CU_JIT_FALLBACK_STRATEGY = 10,
|
|
||||||
CU_JIT_GENERATE_DEBUG_INFO = 11,
|
|
||||||
CU_JIT_LOG_VERBOSE = 12,
|
|
||||||
CU_JIT_GENERATE_LINE_INFO = 13,
|
|
||||||
CU_JIT_CACHE_MODE = 14,
|
|
||||||
CU_JIT_NEW_SM3X_OPT = 15,
|
|
||||||
CU_JIT_FAST_COMPILE = 16,
|
|
||||||
CU_JIT_GLOBAL_SYMBOL_NAMES = 17,
|
|
||||||
CU_JIT_GLOBAL_SYMBOL_ADDRESSES = 18,
|
|
||||||
CU_JIT_GLOBAL_SYMBOL_COUNT = 19,
|
|
||||||
CU_JIT_NUM_OPTIONS
|
|
||||||
} CUjit_option;
|
|
||||||
|
|
||||||
typedef enum CUjitInputType_enum
|
|
||||||
{
|
|
||||||
CU_JIT_INPUT_CUBIN = 0,
|
|
||||||
CU_JIT_INPUT_PTX = 1,
|
|
||||||
CU_JIT_INPUT_FATBINARY = 2,
|
|
||||||
CU_JIT_INPUT_OBJECT = 3,
|
|
||||||
CU_JIT_INPUT_LIBRARY = 4,
|
|
||||||
CU_JIT_NUM_INPUT_TYPES
|
|
||||||
} CUjitInputType;
|
|
||||||
|
|
||||||
#ifndef CU_UUID_HAS_BEEN_DEFINED
|
|
||||||
#define CU_UUID_HAS_BEEN_DEFINED
|
|
||||||
typedef struct CUuuid_st {
|
|
||||||
char bytes[16];
|
|
||||||
} CUuuid;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct CUDA_MEMCPY2D_st {
|
|
||||||
size_t srcXInBytes;
|
|
||||||
size_t srcY;
|
|
||||||
CUmemorytype srcMemoryType;
|
|
||||||
const void *srcHost;
|
|
||||||
CUdeviceptr srcDevice;
|
|
||||||
CUarray srcArray;
|
|
||||||
size_t srcPitch;
|
|
||||||
|
|
||||||
size_t dstXInBytes;
|
|
||||||
size_t dstY;
|
|
||||||
CUmemorytype dstMemoryType;
|
|
||||||
void *dstHost;
|
|
||||||
CUdeviceptr dstDevice;
|
|
||||||
CUarray dstArray;
|
|
||||||
size_t dstPitch;
|
|
||||||
|
|
||||||
size_t WidthInBytes;
|
|
||||||
size_t Height;
|
|
||||||
} CUDA_MEMCPY2D;
|
|
||||||
|
|
||||||
typedef struct CUDA_RESOURCE_DESC_st {
|
|
||||||
CUresourcetype resType;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
CUarray hArray;
|
|
||||||
} array;
|
|
||||||
struct {
|
|
||||||
CUmipmappedArray hMipmappedArray;
|
|
||||||
} mipmap;
|
|
||||||
struct {
|
|
||||||
CUdeviceptr devPtr;
|
|
||||||
CUarray_format format;
|
|
||||||
unsigned int numChannels;
|
|
||||||
size_t sizeInBytes;
|
|
||||||
} linear;
|
|
||||||
struct {
|
|
||||||
CUdeviceptr devPtr;
|
|
||||||
CUarray_format format;
|
|
||||||
unsigned int numChannels;
|
|
||||||
size_t width;
|
|
||||||
size_t height;
|
|
||||||
size_t pitchInBytes;
|
|
||||||
} pitch2D;
|
|
||||||
struct {
|
|
||||||
int reserved[32];
|
|
||||||
} reserved;
|
|
||||||
} res;
|
|
||||||
unsigned int flags;
|
|
||||||
} CUDA_RESOURCE_DESC;
|
|
||||||
|
|
||||||
typedef struct CUDA_TEXTURE_DESC_st {
|
|
||||||
CUaddress_mode addressMode[3];
|
|
||||||
CUfilter_mode filterMode;
|
|
||||||
unsigned int flags;
|
|
||||||
unsigned int maxAnisotropy;
|
|
||||||
CUfilter_mode mipmapFilterMode;
|
|
||||||
float mipmapLevelBias;
|
|
||||||
float minMipmapLevelClamp;
|
|
||||||
float maxMipmapLevelClamp;
|
|
||||||
float borderColor[4];
|
|
||||||
int reserved[12];
|
|
||||||
} CUDA_TEXTURE_DESC;
|
|
||||||
|
|
||||||
/* Unused type */
|
|
||||||
typedef struct CUDA_RESOURCE_VIEW_DESC_st CUDA_RESOURCE_VIEW_DESC;
|
|
||||||
|
|
||||||
typedef unsigned int GLenum;
|
|
||||||
typedef unsigned int GLuint;
|
|
||||||
|
|
||||||
typedef enum CUGLDeviceList_enum {
|
|
||||||
CU_GL_DEVICE_LIST_ALL = 1,
|
|
||||||
CU_GL_DEVICE_LIST_CURRENT_FRAME = 2,
|
|
||||||
CU_GL_DEVICE_LIST_NEXT_FRAME = 3,
|
|
||||||
} CUGLDeviceList;
|
|
||||||
|
|
||||||
typedef struct CUDA_EXTERNAL_MEMORY_HANDLE_DESC_st {
|
|
||||||
CUexternalMemoryHandleType type;
|
|
||||||
union {
|
|
||||||
int fd;
|
|
||||||
struct {
|
|
||||||
void *handle;
|
|
||||||
const void *name;
|
|
||||||
} win32;
|
|
||||||
} handle;
|
|
||||||
unsigned long long size;
|
|
||||||
unsigned int flags;
|
|
||||||
unsigned int reserved[16];
|
|
||||||
} CUDA_EXTERNAL_MEMORY_HANDLE_DESC;
|
|
||||||
|
|
||||||
typedef struct CUDA_EXTERNAL_MEMORY_BUFFER_DESC_st {
|
|
||||||
unsigned long long offset;
|
|
||||||
unsigned long long size;
|
|
||||||
unsigned int flags;
|
|
||||||
unsigned int reserved[16];
|
|
||||||
} CUDA_EXTERNAL_MEMORY_BUFFER_DESC;
|
|
||||||
|
|
||||||
typedef struct CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC_st {
|
|
||||||
CUexternalSemaphoreHandleType type;
|
|
||||||
union {
|
|
||||||
int fd;
|
|
||||||
struct {
|
|
||||||
void *handle;
|
|
||||||
const void *name;
|
|
||||||
} win32;
|
|
||||||
} handle;
|
|
||||||
unsigned int flags;
|
|
||||||
unsigned int reserved[16];
|
|
||||||
} CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC;
|
|
||||||
|
|
||||||
typedef struct CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS_st {
|
|
||||||
struct {
|
|
||||||
struct {
|
|
||||||
unsigned long long value;
|
|
||||||
} fence;
|
|
||||||
unsigned int reserved[16];
|
|
||||||
} params;
|
|
||||||
unsigned int flags;
|
|
||||||
unsigned int reserved[16];
|
|
||||||
} CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS;
|
|
||||||
|
|
||||||
typedef CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS;
|
|
||||||
|
|
||||||
typedef struct CUDA_ARRAY3D_DESCRIPTOR_st {
|
|
||||||
size_t Width;
|
|
||||||
size_t Height;
|
|
||||||
size_t Depth;
|
|
||||||
|
|
||||||
CUarray_format Format;
|
|
||||||
unsigned int NumChannels;
|
|
||||||
unsigned int Flags;
|
|
||||||
} CUDA_ARRAY3D_DESCRIPTOR;
|
|
||||||
|
|
||||||
typedef struct CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC_st {
|
|
||||||
unsigned long long offset;
|
|
||||||
CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
|
|
||||||
unsigned int numLevels;
|
|
||||||
unsigned int reserved[16];
|
|
||||||
} CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC;
|
|
||||||
|
|
||||||
#define CU_STREAM_NON_BLOCKING 1
|
|
||||||
#define CU_EVENT_BLOCKING_SYNC 1
|
|
||||||
#define CU_EVENT_DISABLE_TIMING 2
|
|
||||||
#define CU_TRSF_READ_AS_INTEGER 1
|
|
||||||
|
|
||||||
typedef void CUDAAPI CUstreamCallback(CUstream hStream, CUresult status, void *userdata);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuInit(unsigned int Flags);
|
|
||||||
typedef CUresult CUDAAPI tcuDeviceGetCount(int *count);
|
|
||||||
typedef CUresult CUDAAPI tcuDeviceGet(CUdevice *device, int ordinal);
|
|
||||||
typedef CUresult CUDAAPI tcuDeviceGetAttribute(int *pi, CUdevice_attribute attrib, CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuDeviceGetName(char *name, int len, CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuDeviceGetUuid(CUuuid *uuid, CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuDeviceComputeCapability(int *major, int *minor, CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuCtxCreate_v2(CUcontext *pctx, unsigned int flags, CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuCtxSetLimit(CUlimit limit, size_t value);
|
|
||||||
typedef CUresult CUDAAPI tcuCtxPushCurrent_v2(CUcontext pctx);
|
|
||||||
typedef CUresult CUDAAPI tcuCtxPopCurrent_v2(CUcontext *pctx);
|
|
||||||
typedef CUresult CUDAAPI tcuCtxDestroy_v2(CUcontext ctx);
|
|
||||||
typedef CUresult CUDAAPI tcuMemAlloc_v2(CUdeviceptr *dptr, size_t bytesize);
|
|
||||||
typedef CUresult CUDAAPI tcuMemAllocPitch_v2(CUdeviceptr *dptr, size_t *pPitch, size_t WidthInBytes, size_t Height, unsigned int ElementSizeBytes);
|
|
||||||
typedef CUresult CUDAAPI tcuMemsetD8Async(CUdeviceptr dstDevice, unsigned char uc, size_t N, CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuMemFree_v2(CUdeviceptr dptr);
|
|
||||||
typedef CUresult CUDAAPI tcuMemcpy(CUdeviceptr dst, CUdeviceptr src, size_t bytesize);
|
|
||||||
typedef CUresult CUDAAPI tcuMemcpyAsync(CUdeviceptr dst, CUdeviceptr src, size_t bytesize, CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuMemcpy2D_v2(const CUDA_MEMCPY2D *pcopy);
|
|
||||||
typedef CUresult CUDAAPI tcuMemcpy2DAsync_v2(const CUDA_MEMCPY2D *pcopy, CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuGetErrorName(CUresult error, const char** pstr);
|
|
||||||
typedef CUresult CUDAAPI tcuGetErrorString(CUresult error, const char** pstr);
|
|
||||||
typedef CUresult CUDAAPI tcuCtxGetDevice(CUdevice *device);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxRetain(CUcontext *pctx, CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxRelease(CUdevice dev);
|
|
||||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxSetFlags(CUdevice dev, unsigned int flags);
|
|
||||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxGetState(CUdevice dev, unsigned int *flags, int *active);
|
|
||||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxReset(CUdevice dev);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuStreamCreate(CUstream *phStream, unsigned int flags);
|
|
||||||
typedef CUresult CUDAAPI tcuStreamQuery(CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuStreamSynchronize(CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuStreamDestroy_v2(CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuStreamAddCallback(CUstream hStream, CUstreamCallback *callback, void *userdata, unsigned int flags);
|
|
||||||
typedef CUresult CUDAAPI tcuEventCreate(CUevent *phEvent, unsigned int flags);
|
|
||||||
typedef CUresult CUDAAPI tcuEventDestroy_v2(CUevent hEvent);
|
|
||||||
typedef CUresult CUDAAPI tcuEventSynchronize(CUevent hEvent);
|
|
||||||
typedef CUresult CUDAAPI tcuEventQuery(CUevent hEvent);
|
|
||||||
typedef CUresult CUDAAPI tcuEventRecord(CUevent hEvent, CUstream hStream);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuLaunchKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, CUstream hStream, void** kernelParams, void** extra);
|
|
||||||
typedef CUresult CUDAAPI tcuLinkCreate(unsigned int numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut);
|
|
||||||
typedef CUresult CUDAAPI tcuLinkAddData(CUlinkState state, CUjitInputType type, void* data, size_t size, const char* name, unsigned int numOptions, CUjit_option* options, void** optionValues);
|
|
||||||
typedef CUresult CUDAAPI tcuLinkComplete(CUlinkState state, void** cubinOut, size_t* sizeOut);
|
|
||||||
typedef CUresult CUDAAPI tcuLinkDestroy(CUlinkState state);
|
|
||||||
typedef CUresult CUDAAPI tcuModuleLoadData(CUmodule* module, const void* image);
|
|
||||||
typedef CUresult CUDAAPI tcuModuleUnload(CUmodule hmod);
|
|
||||||
typedef CUresult CUDAAPI tcuModuleGetFunction(CUfunction* hfunc, CUmodule hmod, const char* name);
|
|
||||||
typedef CUresult CUDAAPI tcuModuleGetGlobal(CUdeviceptr *dptr, size_t *bytes, CUmodule hmod, const char* name);
|
|
||||||
typedef CUresult CUDAAPI tcuTexObjectCreate(CUtexObject* pTexObject, const CUDA_RESOURCE_DESC* pResDesc, const CUDA_TEXTURE_DESC* pTexDesc, const CUDA_RESOURCE_VIEW_DESC* pResViewDesc);
|
|
||||||
typedef CUresult CUDAAPI tcuTexObjectDestroy(CUtexObject texObject);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuGLGetDevices_v2(unsigned int* pCudaDeviceCount, CUdevice* pCudaDevices, unsigned int cudaDeviceCount, CUGLDeviceList deviceList);
|
|
||||||
typedef CUresult CUDAAPI tcuGraphicsGLRegisterImage(CUgraphicsResource* pCudaResource, GLuint image, GLenum target, unsigned int Flags);
|
|
||||||
typedef CUresult CUDAAPI tcuGraphicsUnregisterResource(CUgraphicsResource resource);
|
|
||||||
typedef CUresult CUDAAPI tcuGraphicsMapResources(unsigned int count, CUgraphicsResource* resources, CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuGraphicsUnmapResources(unsigned int count, CUgraphicsResource* resources, CUstream hStream);
|
|
||||||
typedef CUresult CUDAAPI tcuGraphicsSubResourceGetMappedArray(CUarray* pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuImportExternalMemory(CUexternalMemory* extMem_out, const CUDA_EXTERNAL_MEMORY_HANDLE_DESC* memHandleDesc);
|
|
||||||
typedef CUresult CUDAAPI tcuDestroyExternalMemory(CUexternalMemory extMem);
|
|
||||||
typedef CUresult CUDAAPI tcuExternalMemoryGetMappedBuffer(CUdeviceptr* devPtr, CUexternalMemory extMem, const CUDA_EXTERNAL_MEMORY_BUFFER_DESC* bufferDesc);
|
|
||||||
typedef CUresult CUDAAPI tcuExternalMemoryGetMappedMipmappedArray(CUmipmappedArray* mipmap, CUexternalMemory extMem, const CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC* mipmapDesc);
|
|
||||||
typedef CUresult CUDAAPI tcuMipmappedArrayGetLevel(CUarray* pLevelArray, CUmipmappedArray hMipmappedArray, unsigned int level);
|
|
||||||
typedef CUresult CUDAAPI tcuMipmappedArrayDestroy(CUmipmappedArray hMipmappedArray);
|
|
||||||
|
|
||||||
typedef CUresult CUDAAPI tcuImportExternalSemaphore(CUexternalSemaphore* extSem_out, const CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC* semHandleDesc);
|
|
||||||
typedef CUresult CUDAAPI tcuDestroyExternalSemaphore(CUexternalSemaphore extSem);
|
|
||||||
typedef CUresult CUDAAPI tcuSignalExternalSemaphoresAsync(const CUexternalSemaphore* extSemArray, const CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS* paramsArray, unsigned int numExtSems, CUstream stream);
|
|
||||||
typedef CUresult CUDAAPI tcuWaitExternalSemaphoresAsync(const CUexternalSemaphore* extSemArray, const CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS* paramsArray, unsigned int numExtSems, CUstream stream);
|
|
||||||
#endif
|
|
||||||
@@ -1,340 +0,0 @@
|
|||||||
/*
|
|
||||||
* This copyright notice applies to this header file only:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2016
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation
|
|
||||||
* files (the "Software"), to deal in the Software without
|
|
||||||
* restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the software, and to permit persons to whom the
|
|
||||||
* software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FFNV_CUDA_DYNLINK_LOADER_H
|
|
||||||
#define FFNV_CUDA_DYNLINK_LOADER_H
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "dynlink_cuda.h"
|
|
||||||
#include "nvEncodeAPI.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32) && (!defined(FFNV_LOAD_FUNC) || !defined(FFNV_SYM_FUNC) || !defined(FFNV_LIB_HANDLE))
|
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FFNV_LIB_HANDLE
|
|
||||||
# if defined(_WIN32)
|
|
||||||
# define FFNV_LIB_HANDLE HMODULE
|
|
||||||
# else
|
|
||||||
# define FFNV_LIB_HANDLE void*
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
||||||
# define CUDA_LIBNAME "nvcuda.dll"
|
|
||||||
# define NVCUVID_LIBNAME "nvcuvid.dll"
|
|
||||||
# if defined(_WIN64) || defined(__CYGWIN64__)
|
|
||||||
# define NVENC_LIBNAME "nvEncodeAPI64.dll"
|
|
||||||
# else
|
|
||||||
# define NVENC_LIBNAME "nvEncodeAPI.dll"
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
# define CUDA_LIBNAME "libcuda.so.1"
|
|
||||||
# define NVCUVID_LIBNAME "libnvcuvid.so.1"
|
|
||||||
# define NVENC_LIBNAME "libnvidia-encode.so.1"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(FFNV_LOAD_FUNC) || !defined(FFNV_SYM_FUNC)
|
|
||||||
# ifdef _WIN32
|
|
||||||
# define FFNV_LOAD_FUNC(path) LoadLibrary(TEXT(path))
|
|
||||||
# define FFNV_SYM_FUNC(lib, sym) GetProcAddress((lib), (sym))
|
|
||||||
# define FFNV_FREE_FUNC(lib) FreeLibrary(lib)
|
|
||||||
# else
|
|
||||||
# include <dlfcn.h>
|
|
||||||
# define FFNV_LOAD_FUNC(path) dlopen((path), RTLD_LAZY)
|
|
||||||
# define FFNV_SYM_FUNC(lib, sym) dlsym((lib), (sym))
|
|
||||||
# define FFNV_FREE_FUNC(lib) dlclose(lib)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(FFNV_LOG_FUNC) || !defined(FFNV_DEBUG_LOG_FUNC)
|
|
||||||
# include <stdio.h>
|
|
||||||
# define FFNV_LOG_FUNC(logctx, msg, ...) fprintf(stderr, (msg), __VA_ARGS__)
|
|
||||||
# define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LOAD_LIBRARY(l, path) \
|
|
||||||
do { \
|
|
||||||
if (!((l) = FFNV_LOAD_FUNC(path))) { \
|
|
||||||
FFNV_LOG_FUNC(logctx, "Cannot load %s\n", path); \
|
|
||||||
ret = -1; \
|
|
||||||
goto error; \
|
|
||||||
} \
|
|
||||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded lib: %s\n", path); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LOAD_SYMBOL(fun, tp, symbol) \
|
|
||||||
do { \
|
|
||||||
if (!((f->fun) = (tp*)FFNV_SYM_FUNC(f->lib, symbol))) { \
|
|
||||||
FFNV_LOG_FUNC(logctx, "Cannot load %s\n", symbol); \
|
|
||||||
ret = -1; \
|
|
||||||
goto error; \
|
|
||||||
} \
|
|
||||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded sym: %s\n", symbol); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LOAD_SYMBOL_OPT(fun, tp, symbol) \
|
|
||||||
do { \
|
|
||||||
if (!((f->fun) = (tp*)FFNV_SYM_FUNC(f->lib, symbol))) { \
|
|
||||||
FFNV_DEBUG_LOG_FUNC(logctx, "Cannot load optional %s\n", symbol); \
|
|
||||||
} else { \
|
|
||||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded sym: %s\n", symbol); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define GENERIC_LOAD_FUNC_PREAMBLE(T, n, N) \
|
|
||||||
T *f; \
|
|
||||||
int ret; \
|
|
||||||
\
|
|
||||||
n##_free_functions(functions); \
|
|
||||||
\
|
|
||||||
f = *functions = (T*)calloc(1, sizeof(*f)); \
|
|
||||||
if (!f) \
|
|
||||||
return -1; \
|
|
||||||
\
|
|
||||||
LOAD_LIBRARY(f->lib, N);
|
|
||||||
|
|
||||||
#define GENERIC_LOAD_FUNC_FINALE(n) \
|
|
||||||
return 0; \
|
|
||||||
error: \
|
|
||||||
n##_free_functions(functions); \
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
#define GENERIC_FREE_FUNC() \
|
|
||||||
if (!functions) \
|
|
||||||
return; \
|
|
||||||
if (*functions && (*functions)->lib) \
|
|
||||||
FFNV_FREE_FUNC((*functions)->lib); \
|
|
||||||
free(*functions); \
|
|
||||||
*functions = NULL;
|
|
||||||
|
|
||||||
#ifdef FFNV_DYNLINK_CUDA_H
|
|
||||||
typedef struct CudaFunctions {
|
|
||||||
tcuInit *cuInit;
|
|
||||||
tcuDeviceGetCount *cuDeviceGetCount;
|
|
||||||
tcuDeviceGet *cuDeviceGet;
|
|
||||||
tcuDeviceGetAttribute *cuDeviceGetAttribute;
|
|
||||||
tcuDeviceGetName *cuDeviceGetName;
|
|
||||||
tcuDeviceGetUuid *cuDeviceGetUuid;
|
|
||||||
tcuDeviceComputeCapability *cuDeviceComputeCapability;
|
|
||||||
tcuCtxCreate_v2 *cuCtxCreate;
|
|
||||||
tcuCtxSetLimit *cuCtxSetLimit;
|
|
||||||
tcuCtxPushCurrent_v2 *cuCtxPushCurrent;
|
|
||||||
tcuCtxPopCurrent_v2 *cuCtxPopCurrent;
|
|
||||||
tcuCtxDestroy_v2 *cuCtxDestroy;
|
|
||||||
tcuMemAlloc_v2 *cuMemAlloc;
|
|
||||||
tcuMemAllocPitch_v2 *cuMemAllocPitch;
|
|
||||||
tcuMemsetD8Async *cuMemsetD8Async;
|
|
||||||
tcuMemFree_v2 *cuMemFree;
|
|
||||||
tcuMemcpy *cuMemcpy;
|
|
||||||
tcuMemcpyAsync *cuMemcpyAsync;
|
|
||||||
tcuMemcpy2D_v2 *cuMemcpy2D;
|
|
||||||
tcuMemcpy2DAsync_v2 *cuMemcpy2DAsync;
|
|
||||||
tcuGetErrorName *cuGetErrorName;
|
|
||||||
tcuGetErrorString *cuGetErrorString;
|
|
||||||
tcuCtxGetDevice *cuCtxGetDevice;
|
|
||||||
|
|
||||||
tcuDevicePrimaryCtxRetain *cuDevicePrimaryCtxRetain;
|
|
||||||
tcuDevicePrimaryCtxRelease *cuDevicePrimaryCtxRelease;
|
|
||||||
tcuDevicePrimaryCtxSetFlags *cuDevicePrimaryCtxSetFlags;
|
|
||||||
tcuDevicePrimaryCtxGetState *cuDevicePrimaryCtxGetState;
|
|
||||||
tcuDevicePrimaryCtxReset *cuDevicePrimaryCtxReset;
|
|
||||||
|
|
||||||
tcuStreamCreate *cuStreamCreate;
|
|
||||||
tcuStreamQuery *cuStreamQuery;
|
|
||||||
tcuStreamSynchronize *cuStreamSynchronize;
|
|
||||||
tcuStreamDestroy_v2 *cuStreamDestroy;
|
|
||||||
tcuStreamAddCallback *cuStreamAddCallback;
|
|
||||||
tcuEventCreate *cuEventCreate;
|
|
||||||
tcuEventDestroy_v2 *cuEventDestroy;
|
|
||||||
tcuEventSynchronize *cuEventSynchronize;
|
|
||||||
tcuEventQuery *cuEventQuery;
|
|
||||||
tcuEventRecord *cuEventRecord;
|
|
||||||
|
|
||||||
tcuLaunchKernel *cuLaunchKernel;
|
|
||||||
tcuLinkCreate *cuLinkCreate;
|
|
||||||
tcuLinkAddData *cuLinkAddData;
|
|
||||||
tcuLinkComplete *cuLinkComplete;
|
|
||||||
tcuLinkDestroy *cuLinkDestroy;
|
|
||||||
tcuModuleLoadData *cuModuleLoadData;
|
|
||||||
tcuModuleUnload *cuModuleUnload;
|
|
||||||
tcuModuleGetFunction *cuModuleGetFunction;
|
|
||||||
tcuModuleGetGlobal *cuModuleGetGlobal;
|
|
||||||
tcuTexObjectCreate *cuTexObjectCreate;
|
|
||||||
tcuTexObjectDestroy *cuTexObjectDestroy;
|
|
||||||
|
|
||||||
tcuGLGetDevices_v2 *cuGLGetDevices;
|
|
||||||
tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
|
|
||||||
tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
|
|
||||||
tcuGraphicsMapResources *cuGraphicsMapResources;
|
|
||||||
tcuGraphicsUnmapResources *cuGraphicsUnmapResources;
|
|
||||||
tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray;
|
|
||||||
|
|
||||||
tcuImportExternalMemory *cuImportExternalMemory;
|
|
||||||
tcuDestroyExternalMemory *cuDestroyExternalMemory;
|
|
||||||
tcuExternalMemoryGetMappedBuffer *cuExternalMemoryGetMappedBuffer;
|
|
||||||
tcuExternalMemoryGetMappedMipmappedArray *cuExternalMemoryGetMappedMipmappedArray;
|
|
||||||
tcuMipmappedArrayDestroy *cuMipmappedArrayDestroy;
|
|
||||||
|
|
||||||
tcuMipmappedArrayGetLevel *cuMipmappedArrayGetLevel;
|
|
||||||
|
|
||||||
tcuImportExternalSemaphore *cuImportExternalSemaphore;
|
|
||||||
tcuDestroyExternalSemaphore *cuDestroyExternalSemaphore;
|
|
||||||
tcuSignalExternalSemaphoresAsync *cuSignalExternalSemaphoresAsync;
|
|
||||||
tcuWaitExternalSemaphoresAsync *cuWaitExternalSemaphoresAsync;
|
|
||||||
|
|
||||||
FFNV_LIB_HANDLE lib;
|
|
||||||
} CudaFunctions;
|
|
||||||
#else
|
|
||||||
typedef struct CudaFunctions CudaFunctions;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef NVENCSTATUS NVENCAPI tNvEncodeAPICreateInstance(NV_ENCODE_API_FUNCTION_LIST *functionList);
|
|
||||||
typedef NVENCSTATUS NVENCAPI tNvEncodeAPIGetMaxSupportedVersion(uint32_t* version);
|
|
||||||
|
|
||||||
typedef struct NvencFunctions {
|
|
||||||
tNvEncodeAPICreateInstance *NvEncodeAPICreateInstance;
|
|
||||||
tNvEncodeAPIGetMaxSupportedVersion *NvEncodeAPIGetMaxSupportedVersion;
|
|
||||||
|
|
||||||
FFNV_LIB_HANDLE lib;
|
|
||||||
} NvencFunctions;
|
|
||||||
|
|
||||||
#ifdef FFNV_DYNLINK_CUDA_H
|
|
||||||
static inline void cuda_free_functions(CudaFunctions **functions)
|
|
||||||
{
|
|
||||||
GENERIC_FREE_FUNC();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline void nvenc_free_functions(NvencFunctions **functions)
|
|
||||||
{
|
|
||||||
GENERIC_FREE_FUNC();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef FFNV_DYNLINK_CUDA_H
|
|
||||||
static inline int cuda_load_functions(CudaFunctions **functions)
|
|
||||||
{
|
|
||||||
GENERIC_LOAD_FUNC_PREAMBLE(CudaFunctions, cuda, CUDA_LIBNAME);
|
|
||||||
|
|
||||||
LOAD_SYMBOL(cuInit, tcuInit, "cuInit");
|
|
||||||
LOAD_SYMBOL(cuDeviceGetCount, tcuDeviceGetCount, "cuDeviceGetCount");
|
|
||||||
LOAD_SYMBOL(cuDeviceGet, tcuDeviceGet, "cuDeviceGet");
|
|
||||||
LOAD_SYMBOL(cuDeviceGetAttribute, tcuDeviceGetAttribute, "cuDeviceGetAttribute");
|
|
||||||
LOAD_SYMBOL(cuDeviceGetName, tcuDeviceGetName, "cuDeviceGetName");
|
|
||||||
LOAD_SYMBOL(cuDeviceComputeCapability, tcuDeviceComputeCapability, "cuDeviceComputeCapability");
|
|
||||||
LOAD_SYMBOL(cuCtxCreate, tcuCtxCreate_v2, "cuCtxCreate_v2");
|
|
||||||
LOAD_SYMBOL(cuCtxSetLimit, tcuCtxSetLimit, "cuCtxSetLimit");
|
|
||||||
LOAD_SYMBOL(cuCtxPushCurrent, tcuCtxPushCurrent_v2, "cuCtxPushCurrent_v2");
|
|
||||||
LOAD_SYMBOL(cuCtxPopCurrent, tcuCtxPopCurrent_v2, "cuCtxPopCurrent_v2");
|
|
||||||
LOAD_SYMBOL(cuCtxDestroy, tcuCtxDestroy_v2, "cuCtxDestroy_v2");
|
|
||||||
LOAD_SYMBOL(cuMemAlloc, tcuMemAlloc_v2, "cuMemAlloc_v2");
|
|
||||||
LOAD_SYMBOL(cuMemAllocPitch, tcuMemAllocPitch_v2, "cuMemAllocPitch_v2");
|
|
||||||
LOAD_SYMBOL(cuMemsetD8Async, tcuMemsetD8Async, "cuMemsetD8Async");
|
|
||||||
LOAD_SYMBOL(cuMemFree, tcuMemFree_v2, "cuMemFree_v2");
|
|
||||||
LOAD_SYMBOL(cuMemcpy, tcuMemcpy, "cuMemcpy");
|
|
||||||
LOAD_SYMBOL(cuMemcpyAsync, tcuMemcpyAsync, "cuMemcpyAsync");
|
|
||||||
LOAD_SYMBOL(cuMemcpy2D, tcuMemcpy2D_v2, "cuMemcpy2D_v2");
|
|
||||||
LOAD_SYMBOL(cuMemcpy2DAsync, tcuMemcpy2DAsync_v2, "cuMemcpy2DAsync_v2");
|
|
||||||
LOAD_SYMBOL(cuGetErrorName, tcuGetErrorName, "cuGetErrorName");
|
|
||||||
LOAD_SYMBOL(cuGetErrorString, tcuGetErrorString, "cuGetErrorString");
|
|
||||||
LOAD_SYMBOL(cuCtxGetDevice, tcuCtxGetDevice, "cuCtxGetDevice");
|
|
||||||
|
|
||||||
LOAD_SYMBOL(cuDevicePrimaryCtxRetain, tcuDevicePrimaryCtxRetain, "cuDevicePrimaryCtxRetain");
|
|
||||||
LOAD_SYMBOL(cuDevicePrimaryCtxRelease, tcuDevicePrimaryCtxRelease, "cuDevicePrimaryCtxRelease");
|
|
||||||
LOAD_SYMBOL(cuDevicePrimaryCtxSetFlags, tcuDevicePrimaryCtxSetFlags, "cuDevicePrimaryCtxSetFlags");
|
|
||||||
LOAD_SYMBOL(cuDevicePrimaryCtxGetState, tcuDevicePrimaryCtxGetState, "cuDevicePrimaryCtxGetState");
|
|
||||||
LOAD_SYMBOL(cuDevicePrimaryCtxReset, tcuDevicePrimaryCtxReset, "cuDevicePrimaryCtxReset");
|
|
||||||
|
|
||||||
LOAD_SYMBOL(cuStreamCreate, tcuStreamCreate, "cuStreamCreate");
|
|
||||||
LOAD_SYMBOL(cuStreamQuery, tcuStreamQuery, "cuStreamQuery");
|
|
||||||
LOAD_SYMBOL(cuStreamSynchronize, tcuStreamSynchronize, "cuStreamSynchronize");
|
|
||||||
LOAD_SYMBOL(cuStreamDestroy, tcuStreamDestroy_v2, "cuStreamDestroy_v2");
|
|
||||||
LOAD_SYMBOL(cuStreamAddCallback, tcuStreamAddCallback, "cuStreamAddCallback");
|
|
||||||
LOAD_SYMBOL(cuEventCreate, tcuEventCreate, "cuEventCreate");
|
|
||||||
LOAD_SYMBOL(cuEventDestroy, tcuEventDestroy_v2, "cuEventDestroy_v2");
|
|
||||||
LOAD_SYMBOL(cuEventSynchronize, tcuEventSynchronize, "cuEventSynchronize");
|
|
||||||
LOAD_SYMBOL(cuEventQuery, tcuEventQuery, "cuEventQuery");
|
|
||||||
LOAD_SYMBOL(cuEventRecord, tcuEventRecord, "cuEventRecord");
|
|
||||||
|
|
||||||
LOAD_SYMBOL(cuLaunchKernel, tcuLaunchKernel, "cuLaunchKernel");
|
|
||||||
LOAD_SYMBOL(cuLinkCreate, tcuLinkCreate, "cuLinkCreate");
|
|
||||||
LOAD_SYMBOL(cuLinkAddData, tcuLinkAddData, "cuLinkAddData");
|
|
||||||
LOAD_SYMBOL(cuLinkComplete, tcuLinkComplete, "cuLinkComplete");
|
|
||||||
LOAD_SYMBOL(cuLinkDestroy, tcuLinkDestroy, "cuLinkDestroy");
|
|
||||||
LOAD_SYMBOL(cuModuleLoadData, tcuModuleLoadData, "cuModuleLoadData");
|
|
||||||
LOAD_SYMBOL(cuModuleUnload, tcuModuleUnload, "cuModuleUnload");
|
|
||||||
LOAD_SYMBOL(cuModuleGetFunction, tcuModuleGetFunction, "cuModuleGetFunction");
|
|
||||||
LOAD_SYMBOL(cuModuleGetGlobal, tcuModuleGetGlobal, "cuModuleGetGlobal");
|
|
||||||
LOAD_SYMBOL(cuTexObjectCreate, tcuTexObjectCreate, "cuTexObjectCreate");
|
|
||||||
LOAD_SYMBOL(cuTexObjectDestroy, tcuTexObjectDestroy, "cuTexObjectDestroy");
|
|
||||||
|
|
||||||
LOAD_SYMBOL(cuGLGetDevices, tcuGLGetDevices_v2, "cuGLGetDevices_v2");
|
|
||||||
LOAD_SYMBOL(cuGraphicsGLRegisterImage, tcuGraphicsGLRegisterImage, "cuGraphicsGLRegisterImage");
|
|
||||||
LOAD_SYMBOL(cuGraphicsUnregisterResource, tcuGraphicsUnregisterResource, "cuGraphicsUnregisterResource");
|
|
||||||
LOAD_SYMBOL(cuGraphicsMapResources, tcuGraphicsMapResources, "cuGraphicsMapResources");
|
|
||||||
LOAD_SYMBOL(cuGraphicsUnmapResources, tcuGraphicsUnmapResources, "cuGraphicsUnmapResources");
|
|
||||||
LOAD_SYMBOL(cuGraphicsSubResourceGetMappedArray, tcuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray");
|
|
||||||
|
|
||||||
LOAD_SYMBOL_OPT(cuDeviceGetUuid, tcuDeviceGetUuid, "cuDeviceGetUuid");
|
|
||||||
LOAD_SYMBOL_OPT(cuImportExternalMemory, tcuImportExternalMemory, "cuImportExternalMemory");
|
|
||||||
LOAD_SYMBOL_OPT(cuDestroyExternalMemory, tcuDestroyExternalMemory, "cuDestroyExternalMemory");
|
|
||||||
LOAD_SYMBOL_OPT(cuExternalMemoryGetMappedBuffer, tcuExternalMemoryGetMappedBuffer, "cuExternalMemoryGetMappedBuffer");
|
|
||||||
LOAD_SYMBOL_OPT(cuExternalMemoryGetMappedMipmappedArray, tcuExternalMemoryGetMappedMipmappedArray, "cuExternalMemoryGetMappedMipmappedArray");
|
|
||||||
LOAD_SYMBOL_OPT(cuMipmappedArrayGetLevel, tcuMipmappedArrayGetLevel, "cuMipmappedArrayGetLevel");
|
|
||||||
LOAD_SYMBOL_OPT(cuMipmappedArrayDestroy, tcuMipmappedArrayDestroy, "cuMipmappedArrayDestroy");
|
|
||||||
|
|
||||||
LOAD_SYMBOL_OPT(cuImportExternalSemaphore, tcuImportExternalSemaphore, "cuImportExternalSemaphore");
|
|
||||||
LOAD_SYMBOL_OPT(cuDestroyExternalSemaphore, tcuDestroyExternalSemaphore, "cuDestroyExternalSemaphore");
|
|
||||||
LOAD_SYMBOL_OPT(cuSignalExternalSemaphoresAsync, tcuSignalExternalSemaphoresAsync, "cuSignalExternalSemaphoresAsync");
|
|
||||||
LOAD_SYMBOL_OPT(cuWaitExternalSemaphoresAsync, tcuWaitExternalSemaphoresAsync, "cuWaitExternalSemaphoresAsync");
|
|
||||||
|
|
||||||
GENERIC_LOAD_FUNC_FINALE(cuda);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline int nvenc_load_functions(NvencFunctions **functions)
|
|
||||||
{
|
|
||||||
GENERIC_LOAD_FUNC_PREAMBLE(NvencFunctions, nvenc, NVENC_LIBNAME);
|
|
||||||
|
|
||||||
LOAD_SYMBOL(NvEncodeAPICreateInstance, tNvEncodeAPICreateInstance, "NvEncodeAPICreateInstance");
|
|
||||||
LOAD_SYMBOL(NvEncodeAPIGetMaxSupportedVersion, tNvEncodeAPIGetMaxSupportedVersion, "NvEncodeAPIGetMaxSupportedVersion");
|
|
||||||
|
|
||||||
GENERIC_LOAD_FUNC_FINALE(nvenc);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef GENERIC_LOAD_FUNC_PREAMBLE
|
|
||||||
#undef LOAD_LIBRARY
|
|
||||||
#undef LOAD_SYMBOL
|
|
||||||
#undef GENERIC_LOAD_FUNC_FINALE
|
|
||||||
#undef GENERIC_FREE_FUNC
|
|
||||||
#undef CUDA_LIBNAME
|
|
||||||
#undef NVCUVID_LIBNAME
|
|
||||||
#undef NVENC_LIBNAME
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -86,6 +86,7 @@ namespace rfb {
|
|||||||
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
||||||
|
|
||||||
// VMware-specific
|
// VMware-specific
|
||||||
|
const int pseudoEncodingVMwareCursor = 0x574d5664;
|
||||||
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
||||||
|
|
||||||
// UltraVNC-specific
|
// UltraVNC-specific
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
#include "mp4.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
uint32_t default_sample_size = 40000;
|
|
||||||
|
|
||||||
enum BufError create_header();
|
|
||||||
|
|
||||||
void set_sps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len) {
|
|
||||||
memcpy(ctx->buf_sps, nal_data, nal_len);
|
|
||||||
ctx->buf_sps_len = nal_len;
|
|
||||||
create_header(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len) {
|
|
||||||
memcpy(ctx->buf_pps, nal_data, nal_len);
|
|
||||||
ctx->buf_pps_len = nal_len;
|
|
||||||
create_header(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError create_header(struct Mp4Context *ctx) {
|
|
||||||
if (ctx->buf_header.offset > 0) return BUF_OK;
|
|
||||||
if (ctx->buf_sps_len == 0) return BUF_OK;
|
|
||||||
if (ctx->buf_pps_len == 0) return BUF_OK;
|
|
||||||
|
|
||||||
struct MoovInfo moov_info;
|
|
||||||
memset(&moov_info, 0, sizeof(struct MoovInfo));
|
|
||||||
moov_info.width = ctx->w;
|
|
||||||
moov_info.height = ctx->h;
|
|
||||||
moov_info.horizontal_resolution = 0x00480000; // 72 dpi
|
|
||||||
moov_info.vertical_resolution = 0x00480000; // 72 dpi
|
|
||||||
moov_info.creation_time = 0;
|
|
||||||
moov_info.timescale = default_sample_size * ctx->framerate;
|
|
||||||
moov_info.sps = (uint8_t *) ctx->buf_sps;
|
|
||||||
moov_info.sps_length = ctx->buf_sps_len;
|
|
||||||
moov_info.pps = (uint8_t *) ctx->buf_pps;
|
|
||||||
moov_info.pps_length = ctx->buf_pps_len;
|
|
||||||
|
|
||||||
ctx->buf_header.offset = 0;
|
|
||||||
enum BufError err = write_header(&ctx->buf_header, &moov_info); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
|
||||||
ptr->buf = ctx->buf_header.buf;
|
|
||||||
ptr->size = ctx->buf_header.size;
|
|
||||||
ptr->offset = ctx->buf_header.offset;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError set_slice(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t origlen, const uint32_t nal_len, const enum NalUnitType unit_type) {
|
|
||||||
enum BufError err;
|
|
||||||
|
|
||||||
const uint32_t samples_info_len = 1;
|
|
||||||
struct SampleInfo samples_info[1];
|
|
||||||
memset(&samples_info[0], 0, sizeof(struct SampleInfo));
|
|
||||||
samples_info[0].size = nal_len + 4; // add size of sample
|
|
||||||
samples_info[0].composition_offset = default_sample_size;
|
|
||||||
samples_info[0].decode_time = default_sample_size;
|
|
||||||
samples_info[0].duration = default_sample_size;
|
|
||||||
samples_info[0].flags = unit_type == NalUnitType_CodedSliceIdr ? 0 : 65536;
|
|
||||||
|
|
||||||
ctx->buf_moof.offset = 0;
|
|
||||||
err = write_moof(&ctx->buf_moof, 0, 0, 0, default_sample_size, samples_info, samples_info_len); chk_err
|
|
||||||
|
|
||||||
ctx->buf_mdat.offset = 0;
|
|
||||||
// printf("nal_data %d\n", nal_data);
|
|
||||||
// printf(" slice: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", nal_data[i]); printf("\n");
|
|
||||||
|
|
||||||
err = write_mdat(&ctx->buf_mdat, nal_data, origlen, nal_len); chk_err
|
|
||||||
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state) {
|
|
||||||
enum BufError err = BUF_OK;
|
|
||||||
if (pos_sequence_number > 0) err = put_u32_be_to_offset(&ctx->buf_moof, pos_sequence_number, state->sequence_number); chk_err
|
|
||||||
if (pos_base_data_offset > 0) err = put_u64_be_to_offset(&ctx->buf_moof, pos_base_data_offset, state->base_data_offset); chk_err
|
|
||||||
if (pos_base_media_decode_time > 0) err = put_u64_be_to_offset(&ctx->buf_moof, pos_base_media_decode_time, state->base_media_decode_time); chk_err
|
|
||||||
state->sequence_number++;
|
|
||||||
state->base_data_offset += ctx->buf_moof.offset + ctx->buf_mdat.offset;
|
|
||||||
state->base_media_decode_time += state->default_sample_duration;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
|
||||||
ptr->buf = ctx->buf_moof.buf;
|
|
||||||
ptr->size = ctx->buf_moof.size;
|
|
||||||
ptr->offset = ctx->buf_moof.offset;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
|
||||||
ptr->buf = ctx->buf_mdat.buf;
|
|
||||||
ptr->size = ctx->buf_mdat.size;
|
|
||||||
ptr->offset = ctx->buf_mdat.offset;
|
|
||||||
// int o = 4+4;
|
|
||||||
// printf(" get_mdat: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", ptr->buf[o+i]); printf("\n");
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
197
common/rfb/mp4.h
197
common/rfb/mp4.h
@@ -1,197 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define chk_err if (err != BUF_OK) { printf("Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), __func__, __FILE__, __LINE__); return err; }
|
|
||||||
#define chk_err_continue if (err != BUF_OK) { printf("Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), __func__, __FILE__, __LINE__); continue; }
|
|
||||||
|
|
||||||
enum BufError {
|
|
||||||
BUF_OK = 0,
|
|
||||||
BUF_ENDOFBUF_ERROR,
|
|
||||||
BUF_MALLOC_ERROR,
|
|
||||||
BUF_INCORRECT
|
|
||||||
};
|
|
||||||
char* buf_error_to_str(const enum BufError err);
|
|
||||||
|
|
||||||
struct BitBuf {
|
|
||||||
char *buf;
|
|
||||||
uint32_t size;
|
|
||||||
uint32_t offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BufError put_skip(struct BitBuf *ptr, const uint32_t count);
|
|
||||||
enum BufError put_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t* data, const uint32_t size);
|
|
||||||
enum BufError put(struct BitBuf *ptr, const uint8_t* data, const uint32_t size);
|
|
||||||
enum BufError put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val);
|
|
||||||
enum BufError put_u8(struct BitBuf *ptr, uint8_t val);
|
|
||||||
enum BufError put_u16_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val);
|
|
||||||
enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val);
|
|
||||||
enum BufError put_u16_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val);
|
|
||||||
enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val);
|
|
||||||
enum BufError put_u32_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val);
|
|
||||||
enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val);
|
|
||||||
enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val);
|
|
||||||
enum BufError put_u64_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint64_t val);
|
|
||||||
enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val);
|
|
||||||
enum BufError put_u32_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val);
|
|
||||||
enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val);
|
|
||||||
enum BufError put_str4_to_offset(struct BitBuf *ptr, const uint32_t offset, const char str[4]);
|
|
||||||
enum BufError put_str4(struct BitBuf *ptr, const char str[4]);
|
|
||||||
enum BufError put_counted_str_to_offset(struct BitBuf *ptr, const uint32_t offset, const char *str, const uint32_t len);
|
|
||||||
enum BufError put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len);
|
|
||||||
|
|
||||||
struct MoovInfo {
|
|
||||||
uint8_t profile_idc;
|
|
||||||
uint8_t level_idc;
|
|
||||||
uint8_t *sps;
|
|
||||||
uint16_t sps_length;
|
|
||||||
uint8_t *pps;
|
|
||||||
uint16_t pps_length;
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
uint32_t horizontal_resolution;
|
|
||||||
uint32_t vertical_resolution;
|
|
||||||
uint32_t creation_time;
|
|
||||||
uint32_t timescale;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info);
|
|
||||||
|
|
||||||
extern uint32_t pos_sequence_number;
|
|
||||||
extern uint32_t pos_base_data_offset;
|
|
||||||
extern uint32_t pos_base_media_decode_time;
|
|
||||||
|
|
||||||
struct SampleInfo {
|
|
||||||
uint32_t duration;
|
|
||||||
uint32_t decode_time;
|
|
||||||
uint32_t composition_time;
|
|
||||||
uint32_t composition_offset;
|
|
||||||
uint32_t size;
|
|
||||||
uint32_t flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BufError write_mdat(struct BitBuf *ptr, const uint8_t* data, const uint32_t origlen, const uint32_t len);
|
|
||||||
enum BufError write_moof(struct BitBuf *ptr,
|
|
||||||
const uint32_t sequence_number,
|
|
||||||
const uint64_t base_data_offset,
|
|
||||||
const uint64_t base_media_decode_time,
|
|
||||||
const uint32_t default_sample_duration,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len);
|
|
||||||
|
|
||||||
enum NalUnitType { // Table 7-1 NAL unit type codes
|
|
||||||
NalUnitType_Unspecified = 0, // Unspecified
|
|
||||||
NalUnitType_CodedSliceNonIdr = 1, // Coded slice of a non-IDR picture
|
|
||||||
NalUnitType_CodedSliceDataPartitionA = 2, // Coded slice data partition A
|
|
||||||
NalUnitType_CodedSliceDataPartitionB = 3, // Coded slice data partition B
|
|
||||||
NalUnitType_CodedSliceDataPartitionC = 4, // Coded slice data partition C
|
|
||||||
NalUnitType_CodedSliceIdr = 5, // Coded slice of an IDR picture
|
|
||||||
NalUnitType_SEI = 6, // Supplemental enhancement information (SEI)
|
|
||||||
NalUnitType_SPS = 7, // Sequence parameter set
|
|
||||||
NalUnitType_PPS = 8, // Picture parameter set
|
|
||||||
NalUnitType_AUD = 9, // Access unit delimiter
|
|
||||||
NalUnitType_EndOfSequence = 10, // End of sequence
|
|
||||||
NalUnitType_EndOfStream = 11, // End of stream
|
|
||||||
NalUnitType_Filler = 12, // Filler data
|
|
||||||
NalUnitType_SpsExt = 13, // Sequence parameter set extension
|
|
||||||
// 14..18 // Reserved
|
|
||||||
NalUnitType_CodedSliceAux = 19, // Coded slice of an auxiliary coded picture without partitioning
|
|
||||||
// 20..23 // Reserved
|
|
||||||
// 24..31 // Unspecified
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NAL {
|
|
||||||
char *data;
|
|
||||||
uint64_t data_size;
|
|
||||||
uint32_t picture_order_count;
|
|
||||||
|
|
||||||
// NAL header
|
|
||||||
bool forbidden_zero_bit;
|
|
||||||
uint8_t ref_idc;
|
|
||||||
uint8_t unit_type_value;
|
|
||||||
enum NalUnitType unit_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline const char* nal_type_to_str(const enum NalUnitType nal_type) {
|
|
||||||
switch (nal_type) {
|
|
||||||
case NalUnitType_Unspecified: return "Unspecified";
|
|
||||||
case NalUnitType_CodedSliceNonIdr: return "CodedSliceNonIdr";
|
|
||||||
case NalUnitType_CodedSliceDataPartitionA: return "CodedSliceDataPartitionA";
|
|
||||||
case NalUnitType_CodedSliceDataPartitionB: return "CodedSliceDataPartitionB";
|
|
||||||
case NalUnitType_CodedSliceDataPartitionC: return "CodedSliceDataPartitionC";
|
|
||||||
case NalUnitType_CodedSliceIdr: return "CodedSliceIdr";
|
|
||||||
case NalUnitType_SEI: return "SEI";
|
|
||||||
case NalUnitType_SPS: return "SPS";
|
|
||||||
case NalUnitType_PPS: return "PPS";
|
|
||||||
case NalUnitType_AUD: return "AUD";
|
|
||||||
case NalUnitType_EndOfSequence: return "EndOfSequence";
|
|
||||||
case NalUnitType_EndOfStream: return "EndOfStream";
|
|
||||||
case NalUnitType_Filler: return "Filler";
|
|
||||||
case NalUnitType_SpsExt: return "SpsExt";
|
|
||||||
case NalUnitType_CodedSliceAux: return "CodedSliceAux";
|
|
||||||
default: return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void nal_parse_header(struct NAL *nal, const char first_byte) {
|
|
||||||
nal->forbidden_zero_bit = ((first_byte & 0x80) >> 7) == 1;
|
|
||||||
nal->ref_idc = (first_byte & 0x60) >> 5;
|
|
||||||
nal->unit_type = (enum NalUnitType) ((first_byte & 0x1f) >> 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool nal_chk4(const uint8_t *buf, const uint32_t offset) {
|
|
||||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x01) { return true; }
|
|
||||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x00 && buf[offset+3] == 0x01) { return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool nal_chk3(const uint8_t *buf, const uint32_t offset) {
|
|
||||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x01) { return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern uint32_t default_sample_size;
|
|
||||||
|
|
||||||
struct Mp4State {
|
|
||||||
bool header_sent;
|
|
||||||
|
|
||||||
uint32_t sequence_number;
|
|
||||||
uint64_t base_data_offset;
|
|
||||||
uint64_t base_media_decode_time;
|
|
||||||
uint32_t default_sample_duration;
|
|
||||||
|
|
||||||
uint32_t nals_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Mp4Context {
|
|
||||||
char buf_sps[128];
|
|
||||||
uint16_t buf_sps_len;
|
|
||||||
char buf_pps[128];
|
|
||||||
uint16_t buf_pps_len;
|
|
||||||
|
|
||||||
uint16_t w, h, framerate;
|
|
||||||
|
|
||||||
struct BitBuf buf_header;
|
|
||||||
struct BitBuf buf_moof;
|
|
||||||
struct BitBuf buf_mdat;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum BufError set_slice(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t origlen, const uint32_t nal_len, const enum NalUnitType unit_type);
|
|
||||||
void set_sps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len);
|
|
||||||
void set_pps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len);
|
|
||||||
|
|
||||||
enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr);
|
|
||||||
|
|
||||||
enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state);
|
|
||||||
enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr);
|
|
||||||
enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern C
|
|
||||||
#endif
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
#include "mp4.h"
|
|
||||||
|
|
||||||
#define chk_ptr if (!ptr) return BUF_INCORRECT;
|
|
||||||
#define chk_realloc { enum BufError err; err = try_to_realloc(ptr, pos); if (err != BUF_OK) return err; }
|
|
||||||
|
|
||||||
char* buf_error_to_str(const enum BufError err) {
|
|
||||||
switch (err) {
|
|
||||||
case BUF_OK: return "BUF_OK";
|
|
||||||
case BUF_ENDOFBUF_ERROR: return "BUF_ENDOFBUF_ERROR";
|
|
||||||
case BUF_MALLOC_ERROR: return "BUF_MALLOC_ERROR";
|
|
||||||
case BUF_INCORRECT: return "BUF_INCORRECT";
|
|
||||||
default: { static char str[32]; sprintf(str, "Unknown(%d)", err); return str; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError try_to_realloc(struct BitBuf *ptr, const uint32_t min_size) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t new_size = ptr->size + min_size + 1024;
|
|
||||||
char* new_buf = realloc(ptr->buf, new_size);
|
|
||||||
if (new_buf == NULL) return BUF_MALLOC_ERROR;
|
|
||||||
ptr->buf = new_buf;
|
|
||||||
ptr->size = new_size;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_skip(struct BitBuf *ptr, const uint32_t count) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = ptr->offset + count;
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
for (uint32_t i = 0; i < count; i++) ptr->buf[ptr->offset + i] = 0;
|
|
||||||
ptr->offset = pos;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t* data, const uint32_t size) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + size;
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
for (uint32_t i = 0; i < size; i++) ptr->buf[offset + i] = data[i];
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put(struct BitBuf *ptr, const uint8_t* data, const uint32_t size) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_to_offset(ptr, ptr->offset, data, size); chk_err
|
|
||||||
ptr->offset += size;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + sizeof(uint8_t);
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
ptr->buf[offset + 0] = val & 0xff;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_u8(struct BitBuf *ptr, uint8_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u8_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(uint8_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_u16_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + sizeof(uint16_t);
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
ptr->buf[offset + 0] = (val >> 8) & 0xff;
|
|
||||||
ptr->buf[offset + 1] = (val >> 0) & 0xff;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u16_be_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(uint16_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_u16_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + sizeof(uint16_t);
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
ptr->buf[offset + 0] = (val >> 0) & 0xff;
|
|
||||||
ptr->buf[offset + 1] = (val >> 8) & 0xff;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u16_le_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(uint16_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_u32_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + sizeof(uint32_t);
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
ptr->buf[offset + 0] = (val >> 24) & 0xff;
|
|
||||||
ptr->buf[offset + 1] = (val >> 16) & 0xff;
|
|
||||||
ptr->buf[offset + 2] = (val >> 8) & 0xff;
|
|
||||||
ptr->buf[offset + 3] = (val >> 0) & 0xff;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(uint32_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(int32_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_u64_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint64_t val) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + sizeof(uint64_t);
|
|
||||||
if (pos > ptr->size) chk_realloc
|
|
||||||
ptr->buf[offset + 0] = (val >> 56) & 0xff;
|
|
||||||
ptr->buf[offset + 1] = (val >> 48) & 0xff;
|
|
||||||
ptr->buf[offset + 2] = (val >> 40) & 0xff;
|
|
||||||
ptr->buf[offset + 3] = (val >> 32) & 0xff;
|
|
||||||
ptr->buf[offset + 4] = (val >> 24) & 0xff;
|
|
||||||
ptr->buf[offset + 5] = (val >> 16) & 0xff;
|
|
||||||
ptr->buf[offset + 6] = (val >> 8) & 0xff;
|
|
||||||
ptr->buf[offset + 7] = (val >> 0) & 0xff;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u64_be_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(uint64_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_u32_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + 4;
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
ptr->buf[offset + 0] = (val >> 0) & 0xff;
|
|
||||||
ptr->buf[offset + 1] = (val >> 8) & 0xff;
|
|
||||||
ptr->buf[offset + 2] = (val >> 16) & 0xff;
|
|
||||||
ptr->buf[offset + 3] = (val >> 24) & 0xff;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_u32_le_to_offset(ptr, ptr->offset, val); chk_err
|
|
||||||
ptr->offset += sizeof(uint32_t);
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError put_str4_to_offset(struct BitBuf *ptr, const uint32_t offset, const char str[4]) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + 4;
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
for (uint8_t i = 0; i < 4; i++) { ptr->buf[offset + i] = str[i]; }
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_str4(struct BitBuf *ptr, const char str[4]) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_str4_to_offset(ptr, ptr->offset, str); chk_err
|
|
||||||
ptr->offset += 4;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_counted_str_to_offset(struct BitBuf *ptr, const uint32_t offset, const char *str, const uint32_t len) {
|
|
||||||
chk_ptr
|
|
||||||
uint32_t pos = offset + len + 1;
|
|
||||||
if (pos >= ptr->size) chk_realloc
|
|
||||||
for (uint32_t i = 0; i < len+1; i++) { ptr->buf[offset + i] = str[i]; }
|
|
||||||
ptr->buf[pos] = 0;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
enum BufError put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len) {
|
|
||||||
chk_ptr
|
|
||||||
enum BufError err = put_counted_str_to_offset(ptr, ptr->offset, str, len); chk_err
|
|
||||||
ptr->offset += len+1;
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// enum BufError hexStr(struct BitBuf *ptr, char *str) : string {
|
|
||||||
// let str = '';
|
|
||||||
// for(let i = 0; i < this.offset; i++) {
|
|
||||||
// if (i % 40 === 0) str += '\n';
|
|
||||||
// if (i % 4 === 0 && i+1 < this.data.length) {
|
|
||||||
// if (i > 0 && i % 40 !== 0) str += ' ';
|
|
||||||
// str += '0x';
|
|
||||||
// }
|
|
||||||
// str += decimalToHex(this.data[i]);
|
|
||||||
// }
|
|
||||||
// return str;
|
|
||||||
// }
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
#include "mp4.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
uint32_t pos_sequence_number = 0;
|
|
||||||
uint32_t pos_base_data_offset = 0;
|
|
||||||
uint32_t pos_base_media_decode_time = 0;
|
|
||||||
|
|
||||||
struct DataOffsetPos {
|
|
||||||
bool data_offset_present;
|
|
||||||
uint32_t offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number);
|
|
||||||
enum BufError write_traf(struct BitBuf *ptr,
|
|
||||||
const uint32_t sequence_number,
|
|
||||||
const uint64_t base_data_offset,
|
|
||||||
const uint64_t base_media_decode_time,
|
|
||||||
const uint32_t default_sample_duration,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
|
||||||
struct DataOffsetPos *data_offset);
|
|
||||||
enum BufError write_tfhd(struct BitBuf *ptr,
|
|
||||||
const uint32_t sequence_number,
|
|
||||||
const uint64_t base_data_offset,
|
|
||||||
const uint64_t base_media_decode_time,
|
|
||||||
const uint32_t default_sample_size,
|
|
||||||
const uint32_t default_sample_duration,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
|
||||||
struct DataOffsetPos *data_offset);
|
|
||||||
enum BufError write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time);
|
|
||||||
enum BufError write_trun(struct BitBuf *ptr,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_count,
|
|
||||||
struct DataOffsetPos *data_offset);
|
|
||||||
|
|
||||||
enum BufError write_mdat(struct BitBuf *ptr, const uint8_t* data, const uint32_t origlen, const uint32_t len) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdat"); chk_err
|
|
||||||
err = put_u32_be(ptr, origlen); chk_err // todo
|
|
||||||
// printf(" write_mdat: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", data[i]); printf("\n");
|
|
||||||
err = put(ptr, data, len); chk_err
|
|
||||||
// printf("mdat len %d ptr->offset %d start_atom: %d \n", len, ptr->offset, start_atom);
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_moof(struct BitBuf *ptr,
|
|
||||||
const uint32_t sequence_number,
|
|
||||||
const uint64_t base_data_offset,
|
|
||||||
const uint64_t base_media_decode_time,
|
|
||||||
const uint32_t default_sample_duration,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len)
|
|
||||||
{
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "moof"); chk_err
|
|
||||||
err = write_mfhd(ptr, sequence_number); chk_err
|
|
||||||
struct DataOffsetPos data_offset;
|
|
||||||
data_offset.offset = 0;
|
|
||||||
err = write_traf(ptr, sequence_number, base_data_offset, base_media_decode_time, default_sample_duration, samples_info, samples_info_len, &data_offset); chk_err
|
|
||||||
if (data_offset.data_offset_present)
|
|
||||||
err = put_u32_be_to_offset(ptr, data_offset.offset, ptr->offset + 4 /*mdat size*/ + 4 /*mdat id*/); chk_err
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mfhd"); chk_err
|
|
||||||
err = put_u8(ptr, 0); // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
pos_sequence_number = ptr->offset;
|
|
||||||
err = put_u32_be(ptr, sequence_number); chk_err // 4 sequence_number
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_traf(struct BitBuf *ptr,
|
|
||||||
const uint32_t sequence_number,
|
|
||||||
const uint64_t base_data_offset,
|
|
||||||
const uint64_t base_media_decode_time,
|
|
||||||
const uint32_t default_sample_duration,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
|
||||||
struct DataOffsetPos *data_offset)
|
|
||||||
{
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "traf"); chk_err
|
|
||||||
err = write_tfhd(ptr, sequence_number, base_data_offset, base_media_decode_time, samples_info[0].size, default_sample_duration, samples_info, samples_info_len, data_offset); chk_err
|
|
||||||
err = write_tfdt(ptr, base_media_decode_time); chk_err
|
|
||||||
err = write_trun(ptr, samples_info, samples_info_len, data_offset); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_tfhd(struct BitBuf *ptr,
|
|
||||||
const uint32_t sequence_number,
|
|
||||||
const uint64_t base_data_offset,
|
|
||||||
const uint64_t base_media_decode_time,
|
|
||||||
const uint32_t default_sample_size,
|
|
||||||
const uint32_t default_sample_duration,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
|
||||||
struct DataOffsetPos *data_offset)
|
|
||||||
{
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tfhd"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 byte version
|
|
||||||
uint64_t flags = 0x0;
|
|
||||||
const bool base_data_offset_present = false;
|
|
||||||
const bool sample_description_index_present = false;
|
|
||||||
const bool default_sample_duration_present = true;
|
|
||||||
const bool default_sample_size_present = true;
|
|
||||||
const bool default_sample_flags_present = true;
|
|
||||||
const bool duration_is_empty = false;
|
|
||||||
const bool default_base_is_moof = false;
|
|
||||||
|
|
||||||
if (base_data_offset_present) { flags = flags | 0x000001; } // base-data-offset-present
|
|
||||||
if (sample_description_index_present) { flags = flags | 0x000002; } // sample-description-index-present
|
|
||||||
if (default_sample_duration_present) { flags = flags | 0x000008; } // default-sample-duration-present
|
|
||||||
if (default_sample_size_present) { flags = flags | 0x000010; } // default-sample-size-present
|
|
||||||
if (default_sample_flags_present) { flags = flags | 0x000020; } // default-sample-flags-present
|
|
||||||
if (duration_is_empty) { flags = flags | 0x010000; } // duration-is-empty
|
|
||||||
if (default_base_is_moof) { flags = flags | 0x020000; } // default-base-is-moof
|
|
||||||
// buf.put_u8(0); buf.put_u8(0); buf.put_u8(0x39); // 3 flags
|
|
||||||
// println!("tfhd flags: 0x{:06x} 0x{:02x}: 0x{:02x}: 0x{:02x}", flags, (flags >> 16) as u8, (flags >> 8) as u8, (flags >> 0) as u8);
|
|
||||||
err = put_u8(ptr, flags >> 16); chk_err; err = put_u8(ptr, flags >> 8); chk_err; err = put_u8(ptr, flags >> 0); chk_err // 3 flags
|
|
||||||
|
|
||||||
err = put_u32_be(ptr, 1); chk_err // 4 track_ID
|
|
||||||
if (base_data_offset_present) { pos_base_data_offset = ptr->offset; err = put_u64_be(ptr, base_data_offset); chk_err }
|
|
||||||
// if sample_description_index_present { buf.put_u32_be(0); } // 4 default_sample_description_index
|
|
||||||
if (default_sample_duration_present) { err = put_u32_be(ptr, default_sample_duration); chk_err }
|
|
||||||
if (default_sample_size_present) { err = put_u32_be(ptr, default_sample_size); chk_err }
|
|
||||||
if (default_sample_flags_present) { err = put_u32_be(ptr, 16842752); chk_err }
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tfdt"); chk_err
|
|
||||||
err = put_u8(ptr, 1); // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
pos_base_media_decode_time = ptr->offset;
|
|
||||||
err = put_u64_be(ptr, base_media_decode_time); chk_err // 4 baseMediaDecodeTime
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_trun(struct BitBuf *ptr,
|
|
||||||
const struct SampleInfo *samples_info, const uint32_t samples_info_count,
|
|
||||||
struct DataOffsetPos *data_offset)
|
|
||||||
{
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trun"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
const bool data_offset_present = true;
|
|
||||||
const bool first_sample_flags_present = false;
|
|
||||||
const bool sample_duration_present = true;
|
|
||||||
const bool sample_size_present = true;
|
|
||||||
const bool sample_flags_present = true;
|
|
||||||
const bool sample_composition_time_offsets_present = true;
|
|
||||||
{
|
|
||||||
uint64_t flags = 0x0;
|
|
||||||
if (data_offset_present) { flags = flags | 0x000001; } // 0x000001 data-offset-present.
|
|
||||||
if (first_sample_flags_present) { flags = flags | 0x000004; } // 0x000004 first-sample-flags-present
|
|
||||||
if (sample_duration_present) { flags = flags | 0x000100; } // 0x000100 sample-duration-present
|
|
||||||
if (sample_size_present) { flags = flags | 0x000200; } // 0x000200 sample-size-present
|
|
||||||
if (sample_flags_present) { flags = flags | 0x000400; } // 0x000400 sample-flags-present
|
|
||||||
if (sample_composition_time_offsets_present) { flags = flags | 0x000800; } // 0x000800 sample-composition-time-offsets-present
|
|
||||||
// println!("trup flags: 0x{:06x} 0x{:02x}: 0x{:02x}: 0x{:02x}", flags, (flags >> 16) as u8, (flags >> 8) as u8, (flags >> 0) as u8);
|
|
||||||
err = put_u8(ptr, flags >> 16); chk_err; err = put_u8(ptr, flags >> 8); chk_err; err = put_u8(ptr, flags >> 0); chk_err // 3 flags
|
|
||||||
}
|
|
||||||
err = put_u32_be(ptr, samples_info_count); chk_err // 4 sample_count
|
|
||||||
|
|
||||||
data_offset->data_offset_present = data_offset_present;
|
|
||||||
data_offset->offset = ptr->offset; // save pointer to this place. we will change size after moof atom will created
|
|
||||||
if (data_offset_present) { err = put_i32_be(ptr, 0); chk_err } // 4 fake data_offset
|
|
||||||
|
|
||||||
if (first_sample_flags_present) { err = put_u32_be(ptr, 33554432); chk_err } // 4 first_sample_flags
|
|
||||||
for (uint32_t i = 0; i < samples_info_count; ++i) {
|
|
||||||
const struct SampleInfo sample_info = samples_info[i];
|
|
||||||
if (sample_duration_present) { err = put_u32_be(ptr, sample_info.duration); chk_err } // 4 sample_duration
|
|
||||||
if (sample_size_present) { err = put_u32_be(ptr, sample_info.size); chk_err } // 4 sample_size
|
|
||||||
if (sample_flags_present) { err = put_u32_be(ptr, sample_info.flags); chk_err } // 4 sample_flags
|
|
||||||
if (sample_composition_time_offsets_present) {
|
|
||||||
// if version == 0 { err = put_u32_be(ptr, sample_info.composition_offset as u32); chk_err }
|
|
||||||
// else
|
|
||||||
{ err = put_i32_be(ptr, sample_info.composition_offset); chk_err }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
@@ -1,420 +0,0 @@
|
|||||||
#include "mp4.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
enum BufError write_ftyp(struct BitBuf *ptr);
|
|
||||||
enum BufError write_moov(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
|
|
||||||
enum BufError write_mvhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_trak(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_tkhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_mdia(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_mdhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_minf(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_dinf(struct BitBuf *ptr);
|
|
||||||
enum BufError write_dref(struct BitBuf *ptr);
|
|
||||||
enum BufError write_url(struct BitBuf *ptr);
|
|
||||||
enum BufError write_vmhd(struct BitBuf *ptr);
|
|
||||||
enum BufError write_stbl(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_stsd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_avc1(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_avcC(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
|
||||||
enum BufError write_stts(struct BitBuf *ptr);
|
|
||||||
enum BufError write_stsc(struct BitBuf *ptr);
|
|
||||||
enum BufError write_stsz(struct BitBuf *ptr);
|
|
||||||
enum BufError write_stco(struct BitBuf *ptr);
|
|
||||||
enum BufError write_mvex(struct BitBuf *ptr);
|
|
||||||
enum BufError write_trex(struct BitBuf *ptr);
|
|
||||||
enum BufError write_udta(struct BitBuf *ptr);
|
|
||||||
enum BufError write_meta(struct BitBuf *ptr);
|
|
||||||
enum BufError write_hdlr(struct BitBuf *ptr, const char name[4], const char manufacturer[4], const char *value, const uint32_t value_len);
|
|
||||||
enum BufError write_ilst(struct BitBuf *ptr, const uint8_t *array, const uint32_t len);
|
|
||||||
|
|
||||||
enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
err = write_ftyp(ptr); chk_err
|
|
||||||
err = write_moov(ptr, moov_info); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_ftyp(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
// atom header <fake size><id>
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "ftyp"); chk_err
|
|
||||||
|
|
||||||
err = put_str4(ptr, "isom"); chk_err // major_brand
|
|
||||||
err = put_u32_be(ptr, 0x00000200); chk_err // minor_version
|
|
||||||
err = put_str4(ptr, "isom"); chk_err
|
|
||||||
err = put_str4(ptr, "iso2"); chk_err
|
|
||||||
err = put_str4(ptr, "avc1"); chk_err
|
|
||||||
err = put_str4(ptr, "iso6"); chk_err
|
|
||||||
err = put_str4(ptr, "mp41"); chk_err
|
|
||||||
|
|
||||||
// write atom size
|
|
||||||
uint32_t atom_size = ptr->offset - start_atom;
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, atom_size); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum BufError write_moov(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "moov"); chk_err
|
|
||||||
err = write_mvhd(ptr, moov_info); chk_err
|
|
||||||
err = write_trak(ptr, moov_info); chk_err
|
|
||||||
err = write_mvex(ptr); chk_err
|
|
||||||
err = write_udta(ptr); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum BufError write_mvhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mvhd"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err=put_u8(ptr, 0);chk_err; err=put_u8(ptr, 0);chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, moov_info->creation_time); chk_err // 4 creation_time
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 modification_time
|
|
||||||
err = put_u32_be(ptr, moov_info->timescale); chk_err // 4 timescale
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 duration
|
|
||||||
err = put_u32_be(ptr, 65536); chk_err // 4 preferred rate
|
|
||||||
err = put_u16_le(ptr, 1); chk_err // 2 preferred volume
|
|
||||||
err = put_skip(ptr, 10); chk_err // 10 reserved
|
|
||||||
{ // 36 matrix
|
|
||||||
err = put_u32_be(ptr, 65536); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 65536); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 1073741824); chk_err
|
|
||||||
}
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Preview time
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Preview duration
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Poster time
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Selection time
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Selection duration
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Current time
|
|
||||||
err = put_u32_be(ptr, 2); chk_err // 4 Next track ID
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_trak(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trak"); chk_err
|
|
||||||
err = write_tkhd(ptr, moov_info); chk_err
|
|
||||||
err = write_mdia(ptr, moov_info); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_tkhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tkhd"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 3); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, moov_info->creation_time); // 4 creation_time
|
|
||||||
err = put_u32_be(ptr, 0); // 4 modification_time
|
|
||||||
err = put_u32_be(ptr, 1); // 4 track id
|
|
||||||
err = put_u32_be(ptr, 0); // 4 reserved
|
|
||||||
err = put_u32_be(ptr, 0); // 4 duration
|
|
||||||
err = put_skip(ptr, 8); // 8 reserved
|
|
||||||
err = put_u16_be(ptr, 0); // 2 layer
|
|
||||||
err = put_u16_be(ptr, 0); // 2 Alternate group
|
|
||||||
err = put_u16_be(ptr, 0); // 2 Volume
|
|
||||||
err = put_u16_be(ptr, 0); // 2 Reserved
|
|
||||||
{ // 36 Matrix structure
|
|
||||||
err = put_u32_be(ptr, 65536); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 65536); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 1073741824); chk_err
|
|
||||||
}
|
|
||||||
err = put_u32_be(ptr, moov_info->width * 65536); chk_err // 4 Track width
|
|
||||||
err = put_u32_be(ptr, moov_info->height * 65536); chk_err // 4 Track height
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum BufError write_mdia(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdia"); chk_err
|
|
||||||
err = write_mdhd(ptr, moov_info); chk_err
|
|
||||||
char *str = "VideoHandler";
|
|
||||||
err = write_hdlr(ptr, "vide", "\0\0\0\0", str, strlen(str)); chk_err
|
|
||||||
err = write_minf(ptr, moov_info); chk_err
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_mdhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdhd"); chk_err
|
|
||||||
err = put_u8(ptr, 0); // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 creation_time
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 modification_time
|
|
||||||
err = put_u32_be(ptr, moov_info->timescale); chk_err // 4 timescale
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 duration
|
|
||||||
err = put_u16_be(ptr, 21956); chk_err // 2 language
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // 2 quality
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_minf(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "minf"); chk_err
|
|
||||||
err = write_vmhd(ptr); chk_err
|
|
||||||
err = write_dinf(ptr); chk_err
|
|
||||||
err = write_stbl(ptr, moov_info); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_dinf(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "dinf"); chk_err
|
|
||||||
err = write_dref(ptr); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_dref(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "dref"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 1); chk_err // 4 Component flags mask
|
|
||||||
err = write_url(ptr); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_url(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "url "); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 1); chk_err // 3 flags
|
|
||||||
//err = put_u8(ptr, 0); chk_err // <counted string> end
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_vmhd(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "vmhd"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 1); chk_err // 3 flags
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // 2 Graphics mode
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_stbl(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stbl"); chk_err
|
|
||||||
err = write_stsd(ptr, moov_info); chk_err
|
|
||||||
err = write_stts(ptr); chk_err
|
|
||||||
err = write_stsc(ptr); chk_err
|
|
||||||
err = write_stsz(ptr); chk_err
|
|
||||||
err = write_stco(ptr); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_stsd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsd"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 1); chk_err // 4 Number of entries
|
|
||||||
err = write_avc1(ptr, moov_info); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_avc1(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "avc1"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // reserved
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // reserved
|
|
||||||
err = put_u16_be(ptr, 1); chk_err // data_reference_index
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // pre_defined
|
|
||||||
err = put_u16_be(ptr, 0); chk_err // reserved
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // pre_defined
|
|
||||||
err = put_u16_be(ptr, moov_info->width); chk_err // 2 width
|
|
||||||
err = put_u16_be(ptr, moov_info->height); chk_err // 2 height
|
|
||||||
err = put_u32_be(ptr, moov_info->horizontal_resolution); chk_err // 4 horizontal_resolution
|
|
||||||
err = put_u32_be(ptr, moov_info->vertical_resolution); chk_err // 4 vertical_resolution
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // reserved
|
|
||||||
err = put_u16_be(ptr, 1); chk_err // 2 frame_count
|
|
||||||
err = put_u8(ptr, 0); chk_err
|
|
||||||
// uint8_t *compressorname = { 0, 0, 0, 0, // dailymotion/hls.js
|
|
||||||
// 0, 0, 0, 0,
|
|
||||||
// 0, 0, 0, 0,
|
|
||||||
// 0, 0, 0, 0,
|
|
||||||
// 0, 0, 0, 0,
|
|
||||||
// 0, 0, 0, 0,
|
|
||||||
// 0, 0, 0, 0,
|
|
||||||
// 0, 0, 0 };
|
|
||||||
char compressorname[50] = "OpenIPC project ";
|
|
||||||
|
|
||||||
err = put(ptr, (uint8_t *) compressorname, 31); chk_err // compressorname
|
|
||||||
err = put_u16_be(ptr, 24); chk_err // 2 depth
|
|
||||||
err = put_u16_be(ptr, 0xffff); chk_err // 2 color_table_id
|
|
||||||
err = write_avcC(ptr, moov_info); chk_err
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum BufError write_avcC(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "avcC"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 1); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, moov_info->sps[1]); chk_err // 1 profile
|
|
||||||
err = put_u8(ptr, moov_info->sps[2]); chk_err // 1 compatibility
|
|
||||||
err = put_u8(ptr, moov_info->sps[3]); chk_err // 1 level
|
|
||||||
err = put_u8(ptr, 0xFF); chk_err // 6 bits reserved (111111) + 2 bits nal size length - 1 (11)
|
|
||||||
err = put_u8(ptr, 0xE1); chk_err // 3 bits reserved (111) + 5 bits number of sps (00001)
|
|
||||||
err = put_u16_be(ptr, moov_info->sps_length); chk_err
|
|
||||||
err = put(ptr, (const uint8_t *) moov_info->sps, moov_info->sps_length); chk_err // SPS
|
|
||||||
err = put_u8(ptr, 1); chk_err // 1 num pps
|
|
||||||
err = put_u16_be(ptr, moov_info->pps_length); chk_err
|
|
||||||
err = put(ptr, (const uint8_t *)moov_info->pps, moov_info->pps_length); chk_err // pps
|
|
||||||
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_stts(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stts"); chk_err
|
|
||||||
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
|
||||||
// Time-to-sample table
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_stsc(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsc"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_stsz(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsz"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // Sample size
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_stco(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stco"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_mvex(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mvex"); chk_err
|
|
||||||
err = write_trex(ptr); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_trex(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trex"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 1); chk_err // track_ID
|
|
||||||
err = put_u32_be(ptr, 1); chk_err // default_sample_description_index
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // default_sample_duration
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // default_sample_size
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // default_sample_flags
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_udta(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "udta"); chk_err
|
|
||||||
err = write_meta(ptr); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_meta(struct BitBuf *ptr) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "meta"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = write_hdlr(ptr, "mdir", "appl", "", 0); chk_err
|
|
||||||
uint8_t array[37] = {0,0,0,37,169,116,111,111,0,0,0,29,100,97,116,97,0,0,0,1,0,0,0,0,76,97,118,102,53,55,46,56,51,46,49,48,48};
|
|
||||||
err = write_ilst(ptr, array, 37); chk_err
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_hdlr(struct BitBuf *ptr, const char name[4], const char manufacturer[4], const char *value, const uint32_t value_len) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "hdlr"); chk_err
|
|
||||||
err = put_u8(ptr, 0); chk_err // 1 version
|
|
||||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Predefined
|
|
||||||
err = put_str4(ptr, name); chk_err // 4 Component subtype
|
|
||||||
err = put_str4(ptr, manufacturer); chk_err // 4 Component manufacturer
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Component flags
|
|
||||||
err = put_u32_be(ptr, 0); chk_err // 4 Component flags mask
|
|
||||||
err = put_counted_str(ptr, value, value_len); chk_err // <counted string> Component name
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BufError write_ilst(struct BitBuf *ptr, const uint8_t *array, const uint32_t len) {
|
|
||||||
enum BufError err;
|
|
||||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "ilst"); chk_err
|
|
||||||
err = put(ptr, array, len); chk_err // <counted string> Component name
|
|
||||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
|
||||||
return BUF_OK;
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,7 @@ namespace rfb {
|
|||||||
|
|
||||||
// kasm
|
// kasm
|
||||||
const int msgTypeStats = 178;
|
const int msgTypeStats = 178;
|
||||||
|
const int msgTypeRequestFrameStats = 179;
|
||||||
|
|
||||||
const int msgTypeServerFence = 248;
|
const int msgTypeServerFence = 248;
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ namespace rfb {
|
|||||||
|
|
||||||
// kasm
|
// kasm
|
||||||
const int msgTypeRequestStats = 178;
|
const int msgTypeRequestStats = 178;
|
||||||
|
const int msgTypeFrameStats = 179;
|
||||||
|
|
||||||
const int msgTypeClientFence = 248;
|
const int msgTypeClientFence = 248;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,453 +0,0 @@
|
|||||||
/*
|
|
||||||
* H.264/HEVC hardware encoding using nvidia nvenc
|
|
||||||
* Copyright (c) 2016 Timo Rothenpieler <timo@rothenpieler.org>
|
|
||||||
*
|
|
||||||
* This file is part of FFmpeg.
|
|
||||||
*
|
|
||||||
* FFmpeg is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FFmpeg 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
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with FFmpeg; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <rfb/LogWriter.h>
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "nvidia.h"
|
|
||||||
|
|
||||||
using namespace rfb;
|
|
||||||
|
|
||||||
static LogWriter vlog("nvidia");
|
|
||||||
|
|
||||||
#define FFNV_LOG_FUNC(logctx, msg, ...) vlog.info((msg), __VA_ARGS__)
|
|
||||||
#define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...)
|
|
||||||
|
|
||||||
#include "dynlink_loader.h"
|
|
||||||
|
|
||||||
#define NUM_SURF 4
|
|
||||||
|
|
||||||
typedef struct NvencSurface
|
|
||||||
{
|
|
||||||
NV_ENC_INPUT_PTR input_surface;
|
|
||||||
int reg_idx;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int pitch;
|
|
||||||
|
|
||||||
NV_ENC_OUTPUT_PTR output_surface;
|
|
||||||
NV_ENC_BUFFER_FORMAT format;
|
|
||||||
} NvencSurface;
|
|
||||||
|
|
||||||
typedef struct NvencDynLoadFunctions
|
|
||||||
{
|
|
||||||
CudaFunctions *cuda_dl;
|
|
||||||
NvencFunctions *nvenc_dl;
|
|
||||||
|
|
||||||
void *nvenc_ctx;
|
|
||||||
NV_ENCODE_API_FUNCTION_LIST nvenc_funcs;
|
|
||||||
|
|
||||||
NV_ENC_INITIALIZE_PARAMS init_enc_parms;
|
|
||||||
NV_ENC_CONFIG enc_cfg;
|
|
||||||
CUdevice cu_dev;
|
|
||||||
CUcontext cu_ctx;
|
|
||||||
|
|
||||||
NvencSurface surf[NUM_SURF];
|
|
||||||
uint8_t cursurf;
|
|
||||||
} NvencDynLoadFunctions;
|
|
||||||
|
|
||||||
static NvencDynLoadFunctions nvenc;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Recommended settings for streaming
|
|
||||||
Low-Latency High Quality preset
|
|
||||||
Rate control mode = Two-pass CBR
|
|
||||||
Very low VBV buffer size (Single frame)
|
|
||||||
No B Frames
|
|
||||||
Infinite GOP length
|
|
||||||
Adaptive Quantization enabled
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int loadfuncs() {
|
|
||||||
int ret;
|
|
||||||
NVENCSTATUS err;
|
|
||||||
uint32_t nvenc_max_ver;
|
|
||||||
|
|
||||||
ret = cuda_load_functions(&nvenc.cuda_dl);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = nvenc_load_functions(&nvenc.nvenc_dl);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_dl->NvEncodeAPIGetMaxSupportedVersion(&nvenc_max_ver);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
vlog.info("Loaded nvenc version %u.%u", nvenc_max_ver >> 4, nvenc_max_ver & 0xf);
|
|
||||||
|
|
||||||
if ((NVENCAPI_MAJOR_VERSION << 4 | NVENCAPI_MINOR_VERSION) > nvenc_max_ver) {
|
|
||||||
vlog.error("Your Nvidia driver is too old. Nvenc %u.%u required",
|
|
||||||
NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
nvenc.nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_dl->NvEncodeAPICreateInstance(&nvenc.nvenc_funcs);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nvenc_check_cap(NV_ENC_CAPS cap) {
|
|
||||||
NV_ENC_CAPS_PARAM params;
|
|
||||||
memset(¶ms, 0, sizeof(NV_ENC_CAPS_PARAM));
|
|
||||||
|
|
||||||
params.version = NV_ENC_CAPS_PARAM_VER;
|
|
||||||
params.capsToQuery = cap;
|
|
||||||
|
|
||||||
int ret, val = 0;
|
|
||||||
|
|
||||||
ret = nvenc.nvenc_funcs.nvEncGetEncodeCaps(nvenc.nvenc_ctx,
|
|
||||||
nvenc.init_enc_parms.encodeGUID,
|
|
||||||
¶ms, &val);
|
|
||||||
if (ret == NV_ENC_SUCCESS)
|
|
||||||
return val;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int setupdevice() {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
nvenc.init_enc_parms.encodeGUID = NV_ENC_CODEC_H264_GUID;
|
|
||||||
nvenc.init_enc_parms.presetGUID = NV_ENC_PRESET_P7_GUID;
|
|
||||||
|
|
||||||
ret = nvenc.cuda_dl->cuInit(0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = nvenc.cuda_dl->cuDeviceGet(&nvenc.cu_dev, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = nvenc.cuda_dl->cuCtxCreate(&nvenc.cu_ctx, CU_CTX_SCHED_BLOCKING_SYNC,
|
|
||||||
nvenc.cu_dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
CUcontext dummy;
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
// cuda stream is NULL to use the default
|
|
||||||
|
|
||||||
// open session
|
|
||||||
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params;
|
|
||||||
memset(¶ms, 0, sizeof(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS));
|
|
||||||
NVENCSTATUS err;
|
|
||||||
|
|
||||||
params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
|
|
||||||
params.apiVersion = NVENCAPI_VERSION;
|
|
||||||
params.device = nvenc.cu_ctx;
|
|
||||||
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncOpenEncodeSessionEx(¶ms, &nvenc.nvenc_ctx);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// check caps
|
|
||||||
const int maxw = nvenc_check_cap(NV_ENC_CAPS_WIDTH_MAX);
|
|
||||||
const int maxh = nvenc_check_cap(NV_ENC_CAPS_HEIGHT_MAX);
|
|
||||||
const int minw = nvenc_check_cap(NV_ENC_CAPS_WIDTH_MIN);
|
|
||||||
const int minh = nvenc_check_cap(NV_ENC_CAPS_HEIGHT_MIN);
|
|
||||||
|
|
||||||
vlog.info("Max enc resolution %ux%u, min %ux%u", maxw, maxh, minw, minh);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int setupenc(const unsigned w, const unsigned h, const unsigned kbps,
|
|
||||||
const unsigned fps) {
|
|
||||||
NVENCSTATUS err;
|
|
||||||
|
|
||||||
nvenc.enc_cfg.version = NV_ENC_CONFIG_VER;
|
|
||||||
nvenc.init_enc_parms.version = NV_ENC_INITIALIZE_PARAMS_VER;
|
|
||||||
nvenc.init_enc_parms.darWidth =
|
|
||||||
nvenc.init_enc_parms.encodeWidth = w;
|
|
||||||
nvenc.init_enc_parms.darHeight =
|
|
||||||
nvenc.init_enc_parms.encodeHeight = h;
|
|
||||||
|
|
||||||
nvenc.init_enc_parms.frameRateNum = fps;
|
|
||||||
nvenc.init_enc_parms.frameRateDen = 1;
|
|
||||||
|
|
||||||
nvenc.init_enc_parms.encodeConfig = &nvenc.enc_cfg;
|
|
||||||
nvenc.init_enc_parms.tuningInfo = NV_ENC_TUNING_INFO_LOW_LATENCY;
|
|
||||||
|
|
||||||
NV_ENC_PRESET_CONFIG preset_cfg;
|
|
||||||
memset(&preset_cfg, 0, sizeof(NV_ENC_PRESET_CONFIG));
|
|
||||||
|
|
||||||
preset_cfg.version = NV_ENC_PRESET_CONFIG_VER;
|
|
||||||
preset_cfg.presetCfg.version = NV_ENC_CONFIG_VER;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncGetEncodePresetConfigEx(nvenc.nvenc_ctx,
|
|
||||||
nvenc.init_enc_parms.encodeGUID,
|
|
||||||
nvenc.init_enc_parms.presetGUID,
|
|
||||||
nvenc.init_enc_parms.tuningInfo,
|
|
||||||
&preset_cfg);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memcpy(&nvenc.enc_cfg, &preset_cfg.presetCfg, sizeof(nvenc.enc_cfg));
|
|
||||||
|
|
||||||
nvenc.enc_cfg.version = NV_ENC_CONFIG_VER;
|
|
||||||
|
|
||||||
nvenc.init_enc_parms.enableEncodeAsync = 0;
|
|
||||||
nvenc.init_enc_parms.enablePTD = 1;
|
|
||||||
|
|
||||||
nvenc.enc_cfg.frameIntervalP = 0;
|
|
||||||
nvenc.enc_cfg.gopLength = 1;
|
|
||||||
|
|
||||||
// use 4 surfaces
|
|
||||||
|
|
||||||
// setup rate control
|
|
||||||
nvenc.enc_cfg.rcParams.multiPass = NV_ENC_TWO_PASS_FULL_RESOLUTION;
|
|
||||||
nvenc.enc_cfg.rcParams.averageBitRate = kbps * 1024;
|
|
||||||
nvenc.enc_cfg.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
|
|
||||||
nvenc.enc_cfg.rcParams.lowDelayKeyFrameScale = 1;
|
|
||||||
|
|
||||||
nvenc.enc_cfg.rcParams.enableAQ = 1;
|
|
||||||
nvenc.enc_cfg.rcParams.aqStrength = 4; // 1 - 15, 0 would be auto
|
|
||||||
|
|
||||||
nvenc.enc_cfg.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME;
|
|
||||||
|
|
||||||
// setup_codec_config
|
|
||||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = 1;
|
|
||||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.outputBufferingPeriodSEI = 1;
|
|
||||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
|
|
||||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
|
|
||||||
nvenc.enc_cfg.profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncInitializeEncoder(nvenc.nvenc_ctx,
|
|
||||||
&nvenc.init_enc_parms);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// custream?
|
|
||||||
|
|
||||||
CUcontext dummy;
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int setupsurf(const unsigned w, const unsigned h) {
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < NUM_SURF; i++) {
|
|
||||||
NVENCSTATUS err;
|
|
||||||
NV_ENC_CREATE_BITSTREAM_BUFFER allocOut;
|
|
||||||
memset(&allocOut, 0, sizeof(NV_ENC_CREATE_BITSTREAM_BUFFER));
|
|
||||||
allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
|
|
||||||
|
|
||||||
NV_ENC_CREATE_INPUT_BUFFER allocSurf;
|
|
||||||
memset(&allocSurf, 0, sizeof(NV_ENC_CREATE_INPUT_BUFFER));
|
|
||||||
|
|
||||||
nvenc.surf[i].format = NV_ENC_BUFFER_FORMAT_ARGB; // doesn't have RGBA!
|
|
||||||
allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
|
|
||||||
allocSurf.width = w;
|
|
||||||
allocSurf.height = h;
|
|
||||||
allocSurf.bufferFmt = nvenc.surf[i].format;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncCreateInputBuffer(nvenc.nvenc_ctx, &allocSurf);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
nvenc.surf[i].input_surface = allocSurf.inputBuffer;
|
|
||||||
nvenc.surf[i].width = allocSurf.width;
|
|
||||||
nvenc.surf[i].height = allocSurf.height;
|
|
||||||
|
|
||||||
// output
|
|
||||||
err = nvenc.nvenc_funcs.nvEncCreateBitstreamBuffer(nvenc.nvenc_ctx, &allocOut);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
nvenc.surf[i].output_surface = allocOut.bitstreamBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
CUcontext dummy;
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nvenc_frame(const uint8_t *data, unsigned pts, uint8_t *out, uint32_t &outlen) {
|
|
||||||
NVENCSTATUS err;
|
|
||||||
|
|
||||||
NV_ENC_PIC_PARAMS params;
|
|
||||||
memset(¶ms, 0, sizeof(NV_ENC_PIC_PARAMS));
|
|
||||||
params.version = NV_ENC_PIC_PARAMS_VER;
|
|
||||||
params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA | NV_ENC_PIC_FLAG_OUTPUT_SPSPPS;
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
NV_ENC_LOCK_INPUT_BUFFER lockBufferParams;
|
|
||||||
memset(&lockBufferParams, 0, sizeof(NV_ENC_LOCK_INPUT_BUFFER));
|
|
||||||
lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
|
|
||||||
lockBufferParams.inputBuffer = nvenc.surf[nvenc.cursurf].input_surface;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncLockInputBuffer(nvenc.nvenc_ctx, &lockBufferParams);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
nvenc.surf[nvenc.cursurf].pitch = lockBufferParams.pitch;
|
|
||||||
//vlog.info("pitch %u", lockBufferParams.pitch);
|
|
||||||
|
|
||||||
// copy frame
|
|
||||||
unsigned y;
|
|
||||||
uint8_t *dst = (uint8_t *) lockBufferParams.bufferDataPtr;
|
|
||||||
const unsigned linelen = nvenc.surf[nvenc.cursurf].width * 4;
|
|
||||||
for (y = 0; y < (unsigned) nvenc.surf[nvenc.cursurf].height; y++) {
|
|
||||||
memcpy(dst, data, linelen);
|
|
||||||
data += linelen;
|
|
||||||
dst += lockBufferParams.pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncUnlockInputBuffer(nvenc.nvenc_ctx,
|
|
||||||
nvenc.surf[nvenc.cursurf].input_surface);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
CUcontext dummy;
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
params.inputBuffer = nvenc.surf[nvenc.cursurf].input_surface;
|
|
||||||
params.bufferFmt = nvenc.surf[nvenc.cursurf].format;
|
|
||||||
params.inputWidth = nvenc.surf[nvenc.cursurf].width;
|
|
||||||
params.inputHeight = nvenc.surf[nvenc.cursurf].height;
|
|
||||||
params.inputPitch = nvenc.surf[nvenc.cursurf].pitch;
|
|
||||||
params.outputBitstream = nvenc.surf[nvenc.cursurf].output_surface;
|
|
||||||
params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
|
|
||||||
params.inputTimeStamp = pts;
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncEncodePicture(nvenc.nvenc_ctx, ¶ms);
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
// Get output
|
|
||||||
NV_ENC_LOCK_BITSTREAM lock_params;
|
|
||||||
memset(&lock_params, 0, sizeof(NV_ENC_LOCK_BITSTREAM));
|
|
||||||
|
|
||||||
lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
|
|
||||||
lock_params.doNotWait = 0;
|
|
||||||
lock_params.outputBitstream = nvenc.surf[nvenc.cursurf].output_surface;
|
|
||||||
// lock_params.sliceOffsets = slice_offsets; TODO?
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncLockBitstream(nvenc.nvenc_ctx, &lock_params);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memcpy(out, lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes);
|
|
||||||
outlen = lock_params.bitstreamSizeInBytes;
|
|
||||||
|
|
||||||
err = nvenc.nvenc_funcs.nvEncUnlockBitstream(nvenc.nvenc_ctx,
|
|
||||||
nvenc.surf[nvenc.cursurf].output_surface);
|
|
||||||
if (err != NV_ENC_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
//vlog.info("Pic type %x, idr %x i %x", lock_params.pictureType, NV_ENC_PIC_TYPE_IDR,
|
|
||||||
// NV_ENC_PIC_TYPE_I);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nvidia_unload() {
|
|
||||||
NV_ENC_PIC_PARAMS params;
|
|
||||||
memset(¶ms, 0, sizeof(NV_ENC_PIC_PARAMS));
|
|
||||||
params.version = NV_ENC_PIC_PARAMS_VER;
|
|
||||||
params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
nvenc.nvenc_funcs.nvEncEncodePicture(nvenc.nvenc_ctx, ¶ms);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < NUM_SURF; i++) {
|
|
||||||
nvenc.nvenc_funcs.nvEncDestroyInputBuffer(nvenc.nvenc_ctx,
|
|
||||||
nvenc.surf[i].input_surface);
|
|
||||||
nvenc.nvenc_funcs.nvEncDestroyBitstreamBuffer(nvenc.nvenc_ctx,
|
|
||||||
nvenc.surf[i].output_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
nvenc.nvenc_funcs.nvEncDestroyEncoder(nvenc.nvenc_ctx);
|
|
||||||
|
|
||||||
CUcontext dummy;
|
|
||||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
|
||||||
|
|
||||||
nvenc.cuda_dl->cuCtxDestroy(nvenc.cu_ctx);
|
|
||||||
|
|
||||||
nvenc_free_functions(&nvenc.nvenc_dl);
|
|
||||||
cuda_free_functions(&nvenc.cuda_dl);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
unsigned w = 256, h = 256, kbps = 400, fps = 15;
|
|
||||||
|
|
||||||
memset(&nvenc, 0, sizeof(NvencDynLoadFunctions));
|
|
||||||
if (loadfuncs() < 0)
|
|
||||||
return 1;
|
|
||||||
if (setupdevice() < 0)
|
|
||||||
return 1;
|
|
||||||
if (setupenc(w, h, kbps, fps) < 0)
|
|
||||||
return 1;
|
|
||||||
if (setupsurf(w, h) < 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
unload();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
int nvidia_init(const unsigned w, const unsigned h, const unsigned kbps,
|
|
||||||
const unsigned fps) {
|
|
||||||
|
|
||||||
memset(&nvenc, 0, sizeof(NvencDynLoadFunctions));
|
|
||||||
if (loadfuncs() < 0)
|
|
||||||
return 1;
|
|
||||||
if (setupdevice() < 0)
|
|
||||||
return 1;
|
|
||||||
if (setupenc(w, h, kbps, fps) < 0)
|
|
||||||
return 1;
|
|
||||||
if (setupsurf(w, h) < 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef KASM_NVIDIA_H
|
|
||||||
#define KASM_NVIDIA_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int nvidia_init(const unsigned w, const unsigned h, const unsigned kbps,
|
|
||||||
const unsigned fps);
|
|
||||||
int nvenc_frame(const uint8_t *data, unsigned pts, uint8_t *out, uint32_t &outlen);
|
|
||||||
void nvidia_unload();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
37
common/rfb/scale_dummy.cxx
Normal file
37
common/rfb/scale_dummy.cxx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/* Copyright (C) 2021 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 <rfb/scale_sse2.h>
|
||||||
|
|
||||||
|
namespace rfb {
|
||||||
|
|
||||||
|
void SSE2_halve(const uint8_t *oldpx,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
uint8_t *newpx,
|
||||||
|
const unsigned oldstride, const unsigned newstride) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles factors between 0.5 and 1.0
|
||||||
|
void SSE2_scale(const uint8_t *oldpx,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
uint8_t *newpx,
|
||||||
|
const unsigned oldstride, const unsigned newstride,
|
||||||
|
const float tgtdiff) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace rfb
|
||||||
257
common/rfb/scale_sse2.cxx
Normal file
257
common/rfb/scale_sse2.cxx
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
/* Copyright (C) 2021 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 <emmintrin.h>
|
||||||
|
|
||||||
|
#include <rfb/scale_sse2.h>
|
||||||
|
|
||||||
|
namespace rfb {
|
||||||
|
|
||||||
|
/*
|
||||||
|
static void print128(const char msg[], const __m128i v) {
|
||||||
|
union {
|
||||||
|
__m128i v;
|
||||||
|
uint8_t c[16];
|
||||||
|
} u;
|
||||||
|
|
||||||
|
u.v = v;
|
||||||
|
|
||||||
|
printf("%s %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
|
||||||
|
msg,
|
||||||
|
u.c[0],
|
||||||
|
u.c[1],
|
||||||
|
u.c[2],
|
||||||
|
u.c[3],
|
||||||
|
u.c[4],
|
||||||
|
u.c[5],
|
||||||
|
u.c[6],
|
||||||
|
u.c[7],
|
||||||
|
u.c[8],
|
||||||
|
u.c[9],
|
||||||
|
u.c[10],
|
||||||
|
u.c[11],
|
||||||
|
u.c[12],
|
||||||
|
u.c[13],
|
||||||
|
u.c[14],
|
||||||
|
u.c[15]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void SSE2_halve(const uint8_t *oldpx,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
uint8_t *newpx,
|
||||||
|
const unsigned oldstride, const unsigned newstride) {
|
||||||
|
uint16_t x, y;
|
||||||
|
const uint16_t srcw = tgtw * 2, srch = tgth * 2;
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
const __m128i shift = _mm_set_epi32(0, 0, 0, 2);
|
||||||
|
const __m128i low = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff);
|
||||||
|
const __m128i high = _mm_set_epi32(0xffffffff, 0xffffffff, 0, 0);
|
||||||
|
|
||||||
|
for (y = 0; y < srch; y += 2) {
|
||||||
|
const uint8_t * const row0 = oldpx + oldstride * y * 4;
|
||||||
|
const uint8_t * const row1 = oldpx + oldstride * (y + 1) * 4;
|
||||||
|
|
||||||
|
uint8_t * const dst = newpx + newstride * (y / 2) * 4;
|
||||||
|
|
||||||
|
for (x = 0; x < srcw; x += 4) {
|
||||||
|
__m128i lo, hi, a, b, c, d;
|
||||||
|
lo = _mm_loadu_si128((__m128i *) &row0[x * 4]);
|
||||||
|
hi = _mm_loadu_si128((__m128i *) &row1[x * 4]);
|
||||||
|
|
||||||
|
a = _mm_unpacklo_epi8(lo, zero);
|
||||||
|
b = _mm_unpackhi_epi8(lo, zero);
|
||||||
|
c = _mm_unpacklo_epi8(hi, zero);
|
||||||
|
d = _mm_unpackhi_epi8(hi, zero);
|
||||||
|
|
||||||
|
a = _mm_add_epi16(a, c);
|
||||||
|
b = _mm_add_epi16(b, d);
|
||||||
|
|
||||||
|
c = _mm_srli_si128(a, 8);
|
||||||
|
a = _mm_and_si128(a, low);
|
||||||
|
a = _mm_add_epi16(a, c);
|
||||||
|
|
||||||
|
d = _mm_slli_si128(b, 8);
|
||||||
|
b = _mm_and_si128(b, high);
|
||||||
|
b = _mm_add_epi16(b, d);
|
||||||
|
|
||||||
|
a = _mm_add_epi16(a, b);
|
||||||
|
|
||||||
|
a = _mm_srl_epi16(a, shift);
|
||||||
|
a = _mm_packus_epi16(a, zero);
|
||||||
|
|
||||||
|
_mm_storel_epi64((__m128i *) &dst[(x / 2) * 4], a);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < srcw; x += 2) {
|
||||||
|
// Remainder in C
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
dst[(x / 2) * 4 + i] =
|
||||||
|
(row0[x * 4 + i] +
|
||||||
|
row0[(x + 1) * 4 + i] +
|
||||||
|
row1[x * 4 + i] +
|
||||||
|
row1[(x + 1) * 4 + i]) / 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles factors between 0.5 and 1.0
|
||||||
|
void SSE2_scale(const uint8_t *oldpx,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
uint8_t *newpx,
|
||||||
|
const unsigned oldstride, const unsigned newstride,
|
||||||
|
const float tgtdiff) {
|
||||||
|
|
||||||
|
uint16_t x, y;
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
const __m128i low = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff);
|
||||||
|
const __m128i high = _mm_set_epi32(0xffffffff, 0xffffffff, 0, 0);
|
||||||
|
const float invdiff = 1 / tgtdiff;
|
||||||
|
|
||||||
|
for (y = 0; y < tgth; y++) {
|
||||||
|
const float ny = y * invdiff;
|
||||||
|
const uint16_t lowy = ny;
|
||||||
|
const uint16_t highy = lowy + 1;
|
||||||
|
const uint16_t bot = (ny - lowy) * 256;
|
||||||
|
const uint16_t top = 256 - bot;
|
||||||
|
const uint32_t * const row0 = (uint32_t *) (oldpx + oldstride * lowy * 4);
|
||||||
|
const uint32_t * const row1 = (uint32_t *) (oldpx + oldstride * highy * 4);
|
||||||
|
const uint8_t * const brow0 = (uint8_t *) row0;
|
||||||
|
const uint8_t * const brow1 = (uint8_t *) row1;
|
||||||
|
|
||||||
|
uint8_t * const dst = newpx + newstride * y * 4;
|
||||||
|
|
||||||
|
const __m128i vertmul = _mm_set1_epi16(top);
|
||||||
|
const __m128i vertmul2 = _mm_set1_epi16(bot);
|
||||||
|
|
||||||
|
for (x = 0; x < tgtw; x += 2) {
|
||||||
|
const float nx[2] = {
|
||||||
|
x * invdiff,
|
||||||
|
(x + 1) * invdiff,
|
||||||
|
};
|
||||||
|
const uint16_t lowx[2] = {
|
||||||
|
(uint16_t) nx[0],
|
||||||
|
(uint16_t) nx[1],
|
||||||
|
};
|
||||||
|
const uint16_t highx[2] = {
|
||||||
|
(uint16_t) (lowx[0] + 1),
|
||||||
|
(uint16_t) (lowx[1] + 1),
|
||||||
|
};
|
||||||
|
const uint16_t right[2] = {
|
||||||
|
(uint16_t) ((nx[0] - lowx[0]) * 256),
|
||||||
|
(uint16_t) ((nx[1] - lowx[1]) * 256),
|
||||||
|
};
|
||||||
|
const uint16_t left[2] = {
|
||||||
|
(uint16_t) (256 - right[0]),
|
||||||
|
(uint16_t) (256 - right[1]),
|
||||||
|
};
|
||||||
|
|
||||||
|
const __m128i horzmul = _mm_set_epi16(
|
||||||
|
right[0],
|
||||||
|
right[0],
|
||||||
|
right[0],
|
||||||
|
right[0],
|
||||||
|
left[0],
|
||||||
|
left[0],
|
||||||
|
left[0],
|
||||||
|
left[0]
|
||||||
|
);
|
||||||
|
const __m128i horzmul2 = _mm_set_epi16(
|
||||||
|
right[1],
|
||||||
|
right[1],
|
||||||
|
right[1],
|
||||||
|
right[1],
|
||||||
|
left[1],
|
||||||
|
left[1],
|
||||||
|
left[1],
|
||||||
|
left[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
__m128i lo, hi, a, b, c, d;
|
||||||
|
lo = _mm_setr_epi32(row0[lowx[0]],
|
||||||
|
row0[highx[0]],
|
||||||
|
row0[lowx[1]],
|
||||||
|
row0[highx[1]]);
|
||||||
|
hi = _mm_setr_epi32(row1[lowx[0]],
|
||||||
|
row1[highx[0]],
|
||||||
|
row1[lowx[1]],
|
||||||
|
row1[highx[1]]);
|
||||||
|
|
||||||
|
a = _mm_unpacklo_epi8(lo, zero);
|
||||||
|
b = _mm_unpackhi_epi8(lo, zero);
|
||||||
|
c = _mm_unpacklo_epi8(hi, zero);
|
||||||
|
d = _mm_unpackhi_epi8(hi, zero);
|
||||||
|
|
||||||
|
a = _mm_mullo_epi16(a, vertmul);
|
||||||
|
b = _mm_mullo_epi16(b, vertmul);
|
||||||
|
c = _mm_mullo_epi16(c, vertmul2);
|
||||||
|
d = _mm_mullo_epi16(d, vertmul2);
|
||||||
|
|
||||||
|
a = _mm_add_epi16(a, c);
|
||||||
|
a = _mm_srli_epi16(a, 8);
|
||||||
|
b = _mm_add_epi16(b, d);
|
||||||
|
b = _mm_srli_epi16(b, 8);
|
||||||
|
|
||||||
|
a = _mm_mullo_epi16(a, horzmul);
|
||||||
|
b = _mm_mullo_epi16(b, horzmul2);
|
||||||
|
|
||||||
|
lo = _mm_srli_si128(a, 8);
|
||||||
|
a = _mm_and_si128(a, low);
|
||||||
|
a = _mm_add_epi16(a, lo);
|
||||||
|
|
||||||
|
hi = _mm_slli_si128(b, 8);
|
||||||
|
b = _mm_and_si128(b, high);
|
||||||
|
b = _mm_add_epi16(b, hi);
|
||||||
|
|
||||||
|
a = _mm_add_epi16(a, b);
|
||||||
|
a = _mm_srli_epi16(a, 8);
|
||||||
|
|
||||||
|
a = _mm_packus_epi16(a, zero);
|
||||||
|
|
||||||
|
_mm_storel_epi64((__m128i *) &dst[x * 4], a);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < tgtw; x++) {
|
||||||
|
// Remainder in C
|
||||||
|
const float nx = x * invdiff;
|
||||||
|
const uint16_t lowx = nx;
|
||||||
|
const uint16_t highx = lowx + 1;
|
||||||
|
const uint16_t right = (nx - lowx) * 256;
|
||||||
|
const uint16_t left = 256 - right;
|
||||||
|
|
||||||
|
uint8_t i;
|
||||||
|
uint32_t val, val2;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
val = brow0[lowx * 4 + i] * left;
|
||||||
|
val += brow0[highx * 4 + i] * right;
|
||||||
|
val >>= 8;
|
||||||
|
|
||||||
|
val2 = brow1[lowx * 4 + i] * left;
|
||||||
|
val2 += brow1[highx * 4 + i] * right;
|
||||||
|
val2 >>= 8;
|
||||||
|
|
||||||
|
dst[x * 4 + i] =
|
||||||
|
(val * top + val2 * bot) >> 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace rfb
|
||||||
38
common/rfb/scale_sse2.h
Normal file
38
common/rfb/scale_sse2.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RFB_SCALE_SSE2_H__
|
||||||
|
#define __RFB_SCALE_SSE2_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace rfb {
|
||||||
|
|
||||||
|
void SSE2_halve(const uint8_t *oldpx,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
uint8_t *newpx,
|
||||||
|
const unsigned oldstride, const unsigned newstride);
|
||||||
|
|
||||||
|
void SSE2_scale(const uint8_t *oldpx,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
uint8_t *newpx,
|
||||||
|
const unsigned oldstride, const unsigned newstride,
|
||||||
|
const float tgtdiff);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
2
debian/Makefile.to_fakebuild_tar_package
vendored
2
debian/Makefile.to_fakebuild_tar_package
vendored
@@ -1,7 +1,7 @@
|
|||||||
TARGET_OS := $(shell lsb_release -is | tr '[:upper:]' '[:lower:]')
|
TARGET_OS := $(shell lsb_release -is | tr '[:upper:]' '[:lower:]')
|
||||||
TARGET_OS_CODENAME := $(shell lsb_release -cs | tr '[:upper:]' '[:lower:]')
|
TARGET_OS_CODENAME := $(shell lsb_release -cs | tr '[:upper:]' '[:lower:]')
|
||||||
TARBALL_DIR := builder/build
|
TARBALL_DIR := builder/build
|
||||||
TARBALL := $(TARBALL_DIR)/kasmvnc.$(TARGET_OS)_$(TARGET_OS_CODENAME).tar.gz
|
TARBALL := $(TARBALL_DIR)/kasmvnc.$(TARGET_OS)_$(TARGET_OS_CODENAME)$(BUILD_TAG).tar.gz
|
||||||
TAR_DATA := $(shell mktemp -d)
|
TAR_DATA := $(shell mktemp -d)
|
||||||
SRC := $(TAR_DATA)/usr/local
|
SRC := $(TAR_DATA)/usr/local
|
||||||
SRC_BIN := $(SRC)/bin
|
SRC_BIN := $(SRC)/bin
|
||||||
|
|||||||
5
debian/control
vendored
5
debian/control
vendored
@@ -3,8 +3,7 @@ Section: x11
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Kasm Technologies LLC <info@kasmweb.com>
|
Maintainer: Kasm Technologies LLC <info@kasmweb.com>
|
||||||
Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev,
|
Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev,
|
||||||
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6,
|
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6
|
||||||
libx264-dev
|
|
||||||
Standards-Version: 4.1.3
|
Standards-Version: 4.1.3
|
||||||
Homepage: https://github.com/kasmtech/KasmVNC
|
Homepage: https://github.com/kasmtech/KasmVNC
|
||||||
#Vcs-Browser: https://salsa.debian.org/debian/kasmvnc
|
#Vcs-Browser: https://salsa.debian.org/debian/kasmvnc
|
||||||
@@ -13,7 +12,7 @@ Homepage: https://github.com/kasmtech/KasmVNC
|
|||||||
Package: kasmvncserver
|
Package: kasmvncserver
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth,
|
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth,
|
||||||
x11-xkb-utils, xkb-data, procps, x264
|
x11-xkb-utils, xkb-data, procps
|
||||||
Provides: vnc-server
|
Provides: vnc-server
|
||||||
Description: VNC server accessible from a web browser
|
Description: VNC server accessible from a web browser
|
||||||
VNC stands for Virtual Network Computing. It is, in essence, a remote
|
VNC stands for Virtual Network Computing. It is, in essence, a remote
|
||||||
|
|||||||
2
kasmweb
2
kasmweb
Submodule kasmweb updated: cd7a460920...d1e4bda4b3
@@ -46,7 +46,7 @@ Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DKASMVNC -DNO_MODULE_EXTS \
|
|||||||
-I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir)
|
-I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir)
|
||||||
|
|
||||||
Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
|
Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
|
||||||
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt -lx264
|
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt
|
||||||
|
|
||||||
Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp
|
Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp
|
||||||
|
|
||||||
|
|||||||
@@ -256,10 +256,6 @@ Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear,
|
|||||||
Default \fB2\fP.
|
Default \fB2\fP.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.B \-x264Bitrate \fInum\fP
|
|
||||||
Enable x264 encoding for full-screen video, in kbps. Default \fB0\fP (off).
|
|
||||||
.
|
|
||||||
.TP
|
|
||||||
.B \-CompareFB \fImode\fP
|
.B \-CompareFB \fImode\fP
|
||||||
Perform pixel comparison on framebuffer to reduce unnecessary updates. Can
|
Perform pixel comparison on framebuffer to reduce unnecessary updates. Can
|
||||||
be either \fB0\fP (off), \fB1\fP (always) or \fB2\fP (auto). Default is
|
be either \fB0\fP (off), \fB1\fP (always) or \fB2\fP (auto). Default is
|
||||||
@@ -321,6 +317,10 @@ Log clipboard and keyboard actions. Info logs just clipboard direction and size,
|
|||||||
verbose adds the contents for both.
|
verbose adds the contents for both.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-selfBench
|
||||||
|
Run a set of self-benchmarks and exit.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
.B \-noWebsocket
|
.B \-noWebsocket
|
||||||
Disable websockets and expose a traditional VNC port (5901, etc.).
|
Disable websockets and expose a traditional VNC port (5901, etc.).
|
||||||
.
|
.
|
||||||
|
|||||||
Reference in New Issue
Block a user