mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-08-10 00:52:16 +00:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b21db64fb | ||
|
|
067efc7912 | ||
|
|
68c0f53bfc | ||
|
|
dc22e24744 | ||
|
|
8eead6597e | ||
|
|
6c0b01737f | ||
|
|
b4e6873649 | ||
|
|
24597178c7 | ||
|
|
05416bb9c2 | ||
|
|
15386f386a | ||
|
|
38fa794009 | ||
|
|
75fce21761 | ||
|
|
abe256144a | ||
|
|
ad2483416d | ||
|
|
87def6db85 | ||
|
|
9e0182be9c | ||
|
|
50a02dbce5 | ||
|
|
69191cafe9 | ||
|
|
c896dabb82 | ||
|
|
ec8170cb40 | ||
|
|
5db8af8a3f | ||
|
|
76d08eb883 | ||
|
|
fb4d4f50ec | ||
|
|
358bb30c3c | ||
|
|
25d8e2b478 | ||
|
|
a4d9ee3fa4 | ||
|
|
f87bc86b4a | ||
|
|
fcd4c07bd0 | ||
|
|
5c1bad7155 | ||
|
|
d14323244e | ||
|
|
6a01e58f61 | ||
|
|
ad66fcb243 | ||
|
|
ad5b816261 | ||
|
|
7602fa110c | ||
|
|
7e26d2fd30 | ||
|
|
116e59292a | ||
|
|
14ed89da0e | ||
|
|
1a48244a0a | ||
|
|
33ba03c679 | ||
|
|
93e622342c | ||
|
|
7f795f0e19 | ||
|
|
b7aa8119f1 | ||
|
|
3c13027a61 | ||
|
|
1e77d0a509 | ||
|
|
59ce8deb97 | ||
|
|
1ed22ab3b2 | ||
|
|
42b4192a04 | ||
|
|
4fe3848d32 | ||
|
|
89207a13f2 | ||
|
|
cd465652f0 | ||
|
|
699b2c160e | ||
|
|
e1588787f0 | ||
|
|
73fe31dbd2 | ||
|
|
2da6fb050a | ||
|
|
a1edc246f5 | ||
|
|
bb7c2d50ef | ||
|
|
6d1805b3fa | ||
|
|
9020c2c229 | ||
|
|
b4739b05c6 | ||
|
|
1329c510b1 | ||
|
|
991fab9370 | ||
|
|
376a2822bd | ||
|
|
2b059c6797 | ||
|
|
ae71a6ad83 | ||
|
|
2af0ce364d | ||
|
|
3b6a59af05 | ||
|
|
526121d81d | ||
|
|
e5ef0375f3 | ||
|
|
de97eacd30 | ||
|
|
1bd75bbeb0 | ||
|
|
8f1692a5ac | ||
|
|
8eb3ea4fa3 | ||
|
|
13aea3cbd8 | ||
|
|
89e8b9628c | ||
|
|
934f81182a | ||
|
|
87774333f3 | ||
|
|
8316f44e10 | ||
|
|
7534fa1023 | ||
|
|
33c6c3e38f | ||
|
|
bd5c50041c | ||
|
|
15c5e76ed4 | ||
|
|
8c9e14e335 | ||
|
|
aa1985dec8 | ||
|
|
f66a7d5da6 | ||
|
|
b523945f48 | ||
|
|
476141d740 |
@@ -1,4 +1,5 @@
|
||||
# install dependencies for C++ analysis
|
||||
set -e
|
||||
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
@@ -54,3 +55,12 @@ sudo wget \
|
||||
sudo chmod a+x /root/cuda.run
|
||||
sudo /root/cuda.run --silent --toolkit --toolkitpath=/usr --no-opengl-libs --no-man-page --no-drm
|
||||
sudo rm /root/cuda.run
|
||||
|
||||
# build
|
||||
mkdir -p build
|
||||
cd build || exit 1
|
||||
cmake -G "Unix Makefiles" ..
|
||||
make -j"$(nproc)"
|
||||
|
||||
# skip autobuild
|
||||
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"
|
||||
34
.codeql-prebuild-cpp-Windows.sh
Normal file
34
.codeql-prebuild-cpp-Windows.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
# install dependencies for C++ analysis
|
||||
set -e
|
||||
|
||||
# update pacman
|
||||
pacman --noconfirm -Suy
|
||||
|
||||
# install dependencies
|
||||
pacman --noconfirm -S \
|
||||
base-devel \
|
||||
diffutils \
|
||||
gcc \
|
||||
git \
|
||||
make \
|
||||
mingw-w64-x86_64-binutils \
|
||||
mingw-w64-x86_64-boost \
|
||||
mingw-w64-x86_64-cmake \
|
||||
mingw-w64-x86_64-curl \
|
||||
mingw-w64-x86_64-miniupnpc \
|
||||
mingw-w64-x86_64-nlohmann-json \
|
||||
mingw-w64-x86_64-nodejs \
|
||||
mingw-w64-x86_64-onevpl \
|
||||
mingw-w64-x86_64-openssl \
|
||||
mingw-w64-x86_64-opus \
|
||||
mingw-w64-x86_64-rust \
|
||||
mingw-w64-x86_64-toolchain
|
||||
|
||||
# build
|
||||
mkdir -p build
|
||||
cd build || exit 1
|
||||
cmake -G "MinGW Makefiles" ..
|
||||
mingw32-make -j"$(nproc)"
|
||||
|
||||
# skip autobuild
|
||||
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"
|
||||
20
.codeql-prebuild-cpp-macOS.sh
Normal file
20
.codeql-prebuild-cpp-macOS.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
# install dependencies for C++ analysis
|
||||
set -e
|
||||
|
||||
# install dependencies
|
||||
brew install \
|
||||
boost \
|
||||
cmake \
|
||||
miniupnpc \
|
||||
node \
|
||||
opus \
|
||||
pkg-config
|
||||
|
||||
# build
|
||||
mkdir -p build
|
||||
cd build || exit 1
|
||||
cmake -G "Unix Makefiles" ..
|
||||
make -j"$(sysctl -n hw.logicalcpu)"
|
||||
|
||||
# skip autobuild
|
||||
echo "skip_autobuild=true" >> "$GITHUB_OUTPUT"
|
||||
@@ -4,8 +4,11 @@
|
||||
# do not ignore .git, needed for versioning
|
||||
!/.git
|
||||
|
||||
# do not ignore .rstcheck.cfg, needed to test building docs
|
||||
!/.rstcheck.cfg
|
||||
|
||||
# ignore repo directories and files
|
||||
docs/
|
||||
docker/
|
||||
gh-pages-template/
|
||||
scripts/
|
||||
tools/
|
||||
@@ -13,6 +16,7 @@ crowdin.yml
|
||||
|
||||
# ignore dev directories
|
||||
build/
|
||||
cmake-*/
|
||||
venv/
|
||||
|
||||
# ignore artifacts
|
||||
|
||||
452
.github/workflows/CI.yml
vendored
452
.github/workflows/CI.yml
vendored
@@ -165,16 +165,12 @@ jobs:
|
||||
remove-android: 'true'
|
||||
remove-haskell: 'true'
|
||||
remove-codeql: 'true'
|
||||
remove-docker-images: 'false'
|
||||
remove-docker-images: 'true'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout Flathub Shared Modules
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: flathub/shared-modules
|
||||
path: build/shared-modules
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Dependencies Linux Flatpak
|
||||
run: |
|
||||
@@ -185,8 +181,10 @@ jobs:
|
||||
cmake \
|
||||
flatpak \
|
||||
qemu-user-static
|
||||
|
||||
sudo su $(whoami) -c "flatpak --user remote-add --if-not-exists flathub \
|
||||
https://flathub.org/repo/flathub.flatpakrepo"
|
||||
|
||||
sudo su $(whoami) -c "flatpak --user install -y flathub \
|
||||
org.flatpak.Builder \
|
||||
org.freedesktop.Platform/${{ matrix.arch }}/${PLATFORM_VERSION} \
|
||||
@@ -213,6 +211,7 @@ jobs:
|
||||
then
|
||||
echo "This is a PUSH event"
|
||||
branch=${{ github.ref_name }}
|
||||
build_version=${{ needs.check_changelog.outputs.next_version }}
|
||||
commit=${{ github.sha }}
|
||||
clone_url=${{ github.event.repository.clone_url }}
|
||||
else
|
||||
@@ -229,6 +228,7 @@ jobs:
|
||||
|
||||
cd build
|
||||
cmake -DGITHUB_CLONE_URL=${clone_url} \
|
||||
-DBUILD_VERSION=${build_version} \
|
||||
-DGITHUB_BRANCH=${branch} \
|
||||
-DGITHUB_COMMIT=${commit} \
|
||||
-DSUNSHINE_CONFIGURE_FLATPAK_MAN=ON \
|
||||
@@ -291,61 +291,49 @@ jobs:
|
||||
remove-android: 'true'
|
||||
remove-haskell: 'true'
|
||||
remove-codeql: 'true'
|
||||
remove-docker-images: 'false'
|
||||
remove-docker-images: 'true'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Dependencies Linux
|
||||
- name: Install wget
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
wget
|
||||
|
||||
- name: Install CUDA
|
||||
env:
|
||||
CUDA_VERSION: 11.8.0
|
||||
CUDA_BUILD: 520.61.05
|
||||
timeout-minutes: 4
|
||||
run: |
|
||||
url_base="https://developer.download.nvidia.com/compute/cuda/${CUDA_VERSION}/local_installers"
|
||||
url="${url_base}/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux.run"
|
||||
sudo wget -q -O /root/cuda.run ${url}
|
||||
sudo chmod a+x /root/cuda.run
|
||||
sudo /root/cuda.run --silent --toolkit --toolkitpath=/usr/local/cuda --no-opengl-libs --no-man-page --no-drm
|
||||
sudo rm /root/cuda.run
|
||||
|
||||
- name: Setup Dependencies Linux
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
# allow newer gcc
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
||||
|
||||
if [[ ${{ matrix.dist }} == "18.04" ]]; then
|
||||
# Ubuntu 18.04 packages
|
||||
sudo add-apt-repository ppa:savoury1/boost-defaults-1.71 -y
|
||||
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
libboost-filesystem1.71-dev \
|
||||
libboost-locale1.71-dev \
|
||||
libboost-log1.71-dev \
|
||||
libboost-regex1.71-dev \
|
||||
libboost-program-options1.71-dev
|
||||
|
||||
# Install cmake
|
||||
wget https://cmake.org/files/v3.22/cmake-3.22.2-linux-x86_64.sh
|
||||
chmod +x cmake-3.22.2-linux-x86_64.sh
|
||||
mkdir /opt/cmake
|
||||
./cmake-3.22.2-linux-x86_64.sh --prefix=/opt/cmake --skip-license
|
||||
ln --force --symbolic /opt/cmake/bin/cmake /usr/local/bin/cmake
|
||||
cmake --version
|
||||
|
||||
# install newer tar from focal... appimagelint fails on 18.04 without this
|
||||
echo "original tar version"
|
||||
tar --version
|
||||
wget -O tar.deb http://security.ubuntu.com/ubuntu/pool/main/t/tar/tar_1.30+dfsg-7ubuntu0.20.04.3_amd64.deb
|
||||
sudo apt-get -y install -f ./tar.deb
|
||||
echo "new tar version"
|
||||
tar --version
|
||||
else
|
||||
# Ubuntu 20.04+ packages
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
cmake \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-program-options-dev
|
||||
fi
|
||||
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
gcc-10 \
|
||||
g++-10 \
|
||||
libayatana-appindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-program-options-dev \
|
||||
libcap-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libdrm-dev \
|
||||
@@ -367,7 +355,7 @@ jobs:
|
||||
libxfixes-dev \
|
||||
libxrandr-dev \
|
||||
libxtst-dev \
|
||||
wget
|
||||
python3
|
||||
|
||||
# clean apt cache
|
||||
sudo apt-get clean
|
||||
@@ -382,20 +370,21 @@ jobs:
|
||||
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-10 \
|
||||
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-10
|
||||
|
||||
# Install CUDA
|
||||
sudo wget \
|
||||
https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run \
|
||||
--progress=bar:force:noscroll -q --show-progress -O /root/cuda.run
|
||||
sudo chmod a+x /root/cuda.run
|
||||
sudo /root/cuda.run --silent --toolkit --toolkitpath=/usr --no-opengl-libs --no-man-page --no-drm
|
||||
sudo rm /root/cuda.run
|
||||
- name: Setup python
|
||||
id: python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Build Linux
|
||||
env:
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version }}
|
||||
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
echo "nproc: $(nproc)"
|
||||
|
||||
mkdir -p build
|
||||
mkdir -p artifacts
|
||||
|
||||
@@ -403,6 +392,7 @@ jobs:
|
||||
cmake \
|
||||
-DBUILD_WERROR=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/usr/local/cuda/bin/nvcc \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DSUNSHINE_ASSETS_DIR=share/sunshine \
|
||||
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
|
||||
@@ -412,20 +402,7 @@ jobs:
|
||||
-DSUNSHINE_ENABLE_CUDA=ON \
|
||||
${{ matrix.EXTRA_ARGS }} \
|
||||
..
|
||||
make -j ${nproc}
|
||||
|
||||
- name: Package Linux - CPACK
|
||||
# todo - this is no longer used
|
||||
if: ${{ matrix.type == 'cpack' }}
|
||||
working-directory: build
|
||||
run: |
|
||||
cpack -G DEB
|
||||
mv ./cpack_artifacts/Sunshine.deb ../artifacts/sunshine-${{ matrix.dist }}.deb
|
||||
|
||||
if [[ ${{ matrix.dist }} == "20.04" ]]; then
|
||||
cpack -G RPM
|
||||
mv ./cpack_artifacts/Sunshine.rpm ../artifacts/sunshine.rpm
|
||||
fi
|
||||
make -j $(expr $(nproc) - 1) # use all but one core
|
||||
|
||||
- name: Set AppImage Version
|
||||
if: |
|
||||
@@ -452,12 +429,12 @@ jobs:
|
||||
|
||||
# AppImage
|
||||
# https://docs.appimage.org/packaging-guide/index.html
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
chmod +x linuxdeploy-x86_64.AppImage
|
||||
|
||||
# https://github.com/linuxdeploy/linuxdeploy-plugin-gtk
|
||||
sudo apt-get install libgtk-3-dev librsvg2-dev -y
|
||||
wget https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
|
||||
wget -q https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
|
||||
chmod +x linuxdeploy-plugin-gtk.sh
|
||||
export DEPLOY_GTK_VERSION=3
|
||||
|
||||
@@ -475,14 +452,17 @@ jobs:
|
||||
# permissions
|
||||
chmod +x ../artifacts/sunshine.AppImage
|
||||
|
||||
- name: Delete cuda
|
||||
# free up space on the runner
|
||||
run: |
|
||||
sudo rm -rf /usr/local/cuda
|
||||
|
||||
- name: Verify AppImage
|
||||
if: ${{ matrix.type == 'AppImage' }}
|
||||
run: |
|
||||
wget https://github.com/TheAssassin/appimagelint/releases/download/continuous/appimagelint-x86_64.AppImage
|
||||
chmod +x appimagelint-x86_64.AppImage
|
||||
|
||||
# rm -rf ~/.cache/appimagelint/
|
||||
|
||||
./appimagelint-x86_64.AppImage ./artifacts/sunshine.AppImage
|
||||
|
||||
- name: Upload Artifacts
|
||||
@@ -491,6 +471,56 @@ jobs:
|
||||
name: sunshine-linux-${{ matrix.type }}-${{ matrix.dist }}
|
||||
path: artifacts/
|
||||
|
||||
- name: Install test deps
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
doxygen \
|
||||
graphviz \
|
||||
python3-venv \
|
||||
x11-xserver-utils \
|
||||
xvfb
|
||||
|
||||
# clean apt cache
|
||||
sudo apt-get clean
|
||||
sudo rm -rf /var/lib/apt/lists/*
|
||||
|
||||
- name: Run tests
|
||||
id: test
|
||||
working-directory: build/tests
|
||||
run: |
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
|
||||
./test_sunshine --gtest_color=yes
|
||||
|
||||
- name: Generate gcov report
|
||||
# any except canceled or skipped
|
||||
if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
|
||||
id: test_report
|
||||
working-directory: build
|
||||
run: |
|
||||
${{ steps.python.outputs.python-path }} -m pip install gcovr
|
||||
${{ steps.python.outputs.python-path }} -m gcovr -r .. \
|
||||
--exclude '.*tests/.*' \
|
||||
--exclude '.*tests/.*' \
|
||||
--xml-pretty \
|
||||
-o coverage.xml
|
||||
|
||||
- name: Upload coverage
|
||||
# any except canceled or skipped
|
||||
if: >-
|
||||
always() &&
|
||||
(steps.test_report.outcome == 'success') &&
|
||||
startsWith(github.repository, 'LizardByte/')
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
disable_search: true
|
||||
fail_ci_if_error: true
|
||||
files: ./build/coverage.xml
|
||||
flags: ${{ runner.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Create/Update GitHub Release
|
||||
if: ${{ needs.setup_release.outputs.create_release == 'true' }}
|
||||
uses: ncipollo/release-action@v1
|
||||
@@ -538,12 +568,17 @@ jobs:
|
||||
if [ -z "$branch" ]
|
||||
then
|
||||
echo "This is a PUSH event"
|
||||
build_version=${{ needs.check_changelog.outputs.next_version }}
|
||||
clone_url=${{ github.event.repository.clone_url }}
|
||||
branch="${{ github.ref_name }}"
|
||||
commit=${{ github.sha }}
|
||||
default_branch="${{ github.event.repository.default_branch }}"
|
||||
else
|
||||
echo "This is a PR event"
|
||||
clone_url=${{ github.event.pull_request.head.repo.clone_url }}
|
||||
branch="${{ github.event.pull_request.head.ref }}"
|
||||
commit=${{ github.event.pull_request.head.sha }}
|
||||
default_branch="${{ github.event.pull_request.head.repo.default_branch }}"
|
||||
fi
|
||||
echo "Branch: ${branch}"
|
||||
echo "Clone URL: ${clone_url}"
|
||||
@@ -551,8 +586,11 @@ jobs:
|
||||
mkdir build
|
||||
cd build
|
||||
cmake \
|
||||
-DBUILD_VERSION="${build_version}" \
|
||||
-DGITHUB_BRANCH="${branch}" \
|
||||
-DGITHUB_COMMIT="${commit}" \
|
||||
-DGITHUB_CLONE_URL="${clone_url}" \
|
||||
-DGITHUB_DEFAULT_BRANCH="${default_branch}" \
|
||||
-DSUNSHINE_CONFIGURE_HOMEBREW=ON \
|
||||
-DSUNSHINE_CONFIGURE_ONLY=ON \
|
||||
..
|
||||
@@ -588,7 +626,7 @@ jobs:
|
||||
echo "publish=${PUBLISH}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Validate and Publish Homebrew Formula
|
||||
uses: LizardByte/homebrew-release-action@v2024.311.172824
|
||||
uses: LizardByte/homebrew-release-action@v2024.417.220943
|
||||
with:
|
||||
formula_file: ${{ github.workspace }}/homebrew/sunshine.rb
|
||||
git_email: ${{ secrets.GH_BOT_EMAIL }}
|
||||
@@ -633,6 +671,12 @@ jobs:
|
||||
# install dependencies using homebrew
|
||||
brew install cmake
|
||||
|
||||
- name: Setup python
|
||||
id: python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Configure Portfile
|
||||
run: |
|
||||
# variables for Portfile
|
||||
@@ -642,6 +686,8 @@ jobs:
|
||||
if [ -z "$branch" ]
|
||||
then
|
||||
echo "This is a PUSH event"
|
||||
branch="${{ github.ref_name }}"
|
||||
build_version=${{ needs.check_changelog.outputs.next_version }}
|
||||
commit=${{ github.sha }}
|
||||
clone_url=${{ github.event.repository.clone_url }}
|
||||
else
|
||||
@@ -655,6 +701,8 @@ jobs:
|
||||
mkdir build
|
||||
cd build
|
||||
cmake \
|
||||
-DBUILD_VERSION=${build_version} \
|
||||
-DGITHUB_BRANCH=${branch} \
|
||||
-DGITHUB_COMMIT=${commit} \
|
||||
-DGITHUB_CLONE_URL=${clone_url} \
|
||||
-DSUNSHINE_CONFIGURE_PORTFILE=ON \
|
||||
@@ -690,6 +738,7 @@ jobs:
|
||||
- name: Build port
|
||||
env:
|
||||
subportlist: ${{ steps.subportlist.outputs.subportlist }}
|
||||
id: build
|
||||
run: |
|
||||
subport="Sunshine"
|
||||
|
||||
@@ -711,6 +760,13 @@ jobs:
|
||||
"$subport"
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Build Logs
|
||||
if: always()
|
||||
run: |
|
||||
logfile="/opt/local/var/macports/logs/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/main.log"
|
||||
cat "$logfile"
|
||||
sudo mv "${logfile}" "${logfile}.bak"
|
||||
|
||||
- name: Upload Artifacts
|
||||
if: ${{ matrix.release }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -718,6 +774,91 @@ jobs:
|
||||
name: sunshine-macports
|
||||
path: artifacts/
|
||||
|
||||
- name: Fix screen capture permissions
|
||||
if: ${{ matrix.os_version != 12 }} # macOS-12 is okay
|
||||
# can be removed if the following is fixed in the runner image
|
||||
# https://github.com/actions/runner-images/issues/9529
|
||||
# https://github.com/actions/runner-images/pull/9530
|
||||
run: |
|
||||
# https://apple.stackexchange.com/questions/362865/macos-list-apps-authorized-for-full-disk-access
|
||||
|
||||
# permissions for screen capture
|
||||
values="'kTCCServiceScreenCapture','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
|
||||
if [[ "${{ matrix.os_version }}" == "14" ]]; then
|
||||
# TCC access table in Sonoma has extra 4 columns: pid, pid_version, boot_uuid, last_reminded
|
||||
values="${values},NULL,NULL,'UNUSED',${values##*,}"
|
||||
fi
|
||||
|
||||
# system and user databases
|
||||
dbPaths=(
|
||||
"/Library/Application Support/com.apple.TCC/TCC.db"
|
||||
"$HOME/Library/Application Support/com.apple.TCC/TCC.db"
|
||||
)
|
||||
|
||||
sqlQuery="INSERT OR IGNORE INTO access VALUES($values);"
|
||||
|
||||
for dbPath in "${dbPaths[@]}"; do
|
||||
echo "Column names for $dbPath"
|
||||
echo "-------------------"
|
||||
sudo sqlite3 "$dbPath" "PRAGMA table_info(access);"
|
||||
echo "Current permissions for $dbPath"
|
||||
echo "-------------------"
|
||||
sudo sqlite3 "$dbPath" "SELECT * FROM access WHERE service='kTCCServiceScreenCapture';"
|
||||
sudo sqlite3 "$dbPath" "$sqlQuery"
|
||||
echo "Updated permissions for $dbPath"
|
||||
echo "-------------------"
|
||||
sudo sqlite3 "$dbPath" "SELECT * FROM access WHERE service='kTCCServiceScreenCapture';"
|
||||
done
|
||||
|
||||
- name: Run tests
|
||||
id: test
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
sudo port test "Sunshine"
|
||||
|
||||
- name: Test Logs
|
||||
if: always()
|
||||
run: |
|
||||
logfile="/opt/local/var/macports/logs/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/main.log"
|
||||
cat "$logfile"
|
||||
|
||||
- name: Generate gcov report
|
||||
# any except canceled or skipped
|
||||
if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
|
||||
id: test_report
|
||||
working-directory:
|
||||
/opt/local/var/macports/build/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/work
|
||||
run: |
|
||||
base_dir=$(pwd)
|
||||
build_dir=${base_dir}/build
|
||||
|
||||
# get the directory name that starts with Sunshine-*
|
||||
dir=$(ls -d Sunshine-*)
|
||||
|
||||
cd ${build_dir}
|
||||
${{ steps.python.outputs.python-path }} -m pip install gcovr
|
||||
sudo ${{ steps.python.outputs.python-path }} -m gcovr -r ../${dir} \
|
||||
--exclude '.*${dir}/tests/.*' \
|
||||
--exclude '.*${dir}/third-party/.*' \
|
||||
--gcov-object-directory $(pwd) \
|
||||
--verbose \
|
||||
--xml-pretty \
|
||||
-o ${{ github.workspace }}/build/coverage.xml
|
||||
|
||||
- name: Upload coverage
|
||||
# any except canceled or skipped
|
||||
if: >-
|
||||
always() &&
|
||||
(steps.test_report.outcome == 'success') &&
|
||||
startsWith(github.repository, 'LizardByte/')
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
disable_search: true
|
||||
fail_ci_if_error: false # todo: re-enable this when action is fixed
|
||||
files: ./build/coverage.xml
|
||||
flags: ${{ runner.os }}-${{ matrix.os_version }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Create/Update GitHub Release
|
||||
if: ${{ needs.setup_release.outputs.create_release == 'true' && matrix.release }}
|
||||
uses: ncipollo/release-action@v1
|
||||
@@ -743,6 +884,110 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Prepare tests
|
||||
id: prepare-tests
|
||||
if: false # todo: DirectX11 is not available, so even software encoder fails
|
||||
run: |
|
||||
# function to download and extract a zip file
|
||||
function DownloadAndExtract {
|
||||
param (
|
||||
[string]$Uri,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$maxRetries = 5
|
||||
$retryCount = 0
|
||||
$success = $false
|
||||
|
||||
while (-not $success -and $retryCount -lt $maxRetries) {
|
||||
$retryCount++
|
||||
Write-Host "Downloading $Uri to $OutFile, attempt $retryCount of $maxRetries"
|
||||
try {
|
||||
Invoke-WebRequest -Uri $Uri -OutFile $OutFile
|
||||
$success = $true
|
||||
} catch {
|
||||
Write-Host "Attempt $retryCount of $maxRetries failed with error: $($_.Exception.Message). Retrying..."
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $success) {
|
||||
Write-Host "Failed to download the file after $maxRetries attempts."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# use .NET to get the base name of the file
|
||||
$baseName = (Get-Item $OutFile).BaseName
|
||||
|
||||
# Extract the zip file
|
||||
Expand-Archive -Path $OutFile -DestinationPath $baseName
|
||||
}
|
||||
|
||||
# virtual display driver
|
||||
DownloadAndExtract `
|
||||
-Uri "https://www.amyuni.com/downloads/usbmmidd_v2.zip" `
|
||||
-OutFile "usbmmidd_v2.zip"
|
||||
|
||||
# install
|
||||
Set-Location -Path usbmmidd_v2/usbmmidd_v2
|
||||
./deviceinstaller64 install usbmmidd.inf usbmmidd
|
||||
|
||||
# create the virtual display
|
||||
./deviceinstaller64 enableidd 1
|
||||
|
||||
# move up a directory
|
||||
Set-Location -Path ../..
|
||||
|
||||
# install devcon
|
||||
DownloadAndExtract `
|
||||
-Uri "https://github.com/Drawbackz/DevCon-Installer/releases/download/1.4-rc/Devcon.Installer.zip" `
|
||||
-OutFile "Devcon.Installer.zip"
|
||||
Set-Location -Path Devcon.Installer
|
||||
# hash needs to match OS version
|
||||
# https://github.com/Drawbackz/DevCon-Installer/blob/master/devcon_sources.json
|
||||
Start-Process -FilePath "./Devcon Installer.exe" -Wait -ArgumentList `
|
||||
'install', `
|
||||
'-hash', '54004C83EE34F6A55380528A8B29F4C400E61FBB947A19E0AB9E5A193D7D961E', `
|
||||
'-addpath', `
|
||||
'-update', `
|
||||
'-dir', 'C:\Windows\System32'
|
||||
|
||||
# disable Hyper-V Video
|
||||
# https://stackoverflow.com/a/59490940
|
||||
C:\Windows\System32\devcon.exe disable "VMBUS\{da0a7802-e377-4aac-8e77-0558eb1073f8}"
|
||||
|
||||
# move up a directory
|
||||
Set-Location -Path ..
|
||||
|
||||
# multi monitor tool
|
||||
DownloadAndExtract `
|
||||
-Uri "http://www.nirsoft.net/utils/multimonitortool-x64.zip" `
|
||||
-OutFile "multimonitortool.zip"
|
||||
|
||||
# enable the virtual display
|
||||
# http://www.nirsoft.net/utils/multi_monitor_tool.html
|
||||
Set-Location -Path multimonitortool
|
||||
|
||||
# Original Hyper-V is \\.\DISPLAY1, it will recreate itself as \\.\DISPLAY6 (or something higher than 2)
|
||||
# USB Mobile Monitor Virtual Display is \\.\DISPLAY2
|
||||
|
||||
# these don't seem to work if not using runAs
|
||||
# todo: do they work if not using runAs?
|
||||
Start-Process powershell -Verb runAs -ArgumentList '-Command ./MultiMonitorTool.exe /enable \\.\DISPLAY2'
|
||||
Start-Process powershell -Verb runAs -ArgumentList '-Command ./MultiMonitorTool.exe /SetPrimary \\.\DISPLAY2'
|
||||
|
||||
# wait a few seconds
|
||||
Start-Sleep -s 5
|
||||
|
||||
# list monitors
|
||||
./MultiMonitorTool.exe /stext monitor_list.txt
|
||||
|
||||
# wait a few seconds
|
||||
Start-Sleep -s 5
|
||||
|
||||
# print the monitor list
|
||||
Get-Content -Path monitor_list.txt
|
||||
|
||||
- name: Setup Dependencies Windows
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@@ -750,12 +995,14 @@ jobs:
|
||||
install: >-
|
||||
base-devel
|
||||
diffutils
|
||||
doxygen
|
||||
git
|
||||
make
|
||||
mingw-w64-x86_64-binutils
|
||||
mingw-w64-x86_64-boost
|
||||
mingw-w64-x86_64-cmake
|
||||
mingw-w64-x86_64-curl
|
||||
mingw-w64-x86_64-graphviz
|
||||
mingw-w64-x86_64-miniupnpc
|
||||
mingw-w64-x86_64-nlohmann-json
|
||||
mingw-w64-x86_64-nodejs
|
||||
@@ -768,11 +1015,29 @@ jobs:
|
||||
wget
|
||||
yasm
|
||||
|
||||
- name: Setup python
|
||||
# use this instead of msys2 python due to known issues using wheels, https://www.msys2.org/docs/python/
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Python Path
|
||||
id: python-path
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
# replace backslashes with double backslashes
|
||||
python_path=$(echo "${{ steps.setup-python.outputs.python-path }}" | sed 's/\\/\\\\/g')
|
||||
|
||||
# step output
|
||||
echo "python-path=${python_path}"
|
||||
echo "python-path=${python_path}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Windows
|
||||
shell: msys2 {0}
|
||||
env:
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version }}
|
||||
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
mkdir build
|
||||
@@ -781,6 +1046,8 @@ jobs:
|
||||
-DBUILD_WERROR=ON \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DSUNSHINE_ASSETS_DIR=assets \
|
||||
-DTESTS_PYTHON_EXECUTABLE='${{ steps.python-path.outputs.python-path }}' \
|
||||
-DTESTS_SOFTWARE_ENCODER_UNAVAILABLE='skip' \
|
||||
-G "MinGW Makefiles" \
|
||||
..
|
||||
mingw32-make -j$(nproc)
|
||||
@@ -799,6 +1066,41 @@ jobs:
|
||||
mv ./cpack_artifacts/Sunshine.exe ../artifacts/sunshine-windows-installer.exe
|
||||
mv ./cpack_artifacts/Sunshine.zip ../artifacts/sunshine-windows-portable.zip
|
||||
|
||||
- name: Run tests
|
||||
id: test
|
||||
shell: msys2 {0}
|
||||
working-directory: build/tests
|
||||
run: |
|
||||
./test_sunshine.exe --gtest_color=yes
|
||||
|
||||
- name: Generate gcov report
|
||||
# any except canceled or skipped
|
||||
if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure')
|
||||
id: test_report
|
||||
shell: msys2 {0}
|
||||
working-directory: build
|
||||
run: |
|
||||
${{ steps.python-path.outputs.python-path }} -m pip install gcovr
|
||||
${{ steps.python-path.outputs.python-path }} -m gcovr -r .. \
|
||||
--exclude '.*tests/.*' \
|
||||
--exclude '.*tests/.*' \
|
||||
--xml-pretty \
|
||||
-o coverage.xml
|
||||
|
||||
- name: Upload coverage
|
||||
# any except canceled or skipped
|
||||
if: >-
|
||||
always() &&
|
||||
(steps.test_report.outcome == 'success') &&
|
||||
startsWith(github.repository, 'LizardByte/')
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
disable_search: true
|
||||
fail_ci_if_error: true
|
||||
files: ./build/coverage.xml
|
||||
flags: ${{ runner.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Package Windows Debug Info
|
||||
working-directory: build
|
||||
run: |
|
||||
|
||||
4
.github/workflows/automerge.yml
vendored
4
.github/workflows/automerge.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Autoapproving
|
||||
uses: hmarr/auto-approve-action@v3
|
||||
uses: hmarr/auto-approve-action@v4
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Automerging
|
||||
uses: pascalgn/automerge-action@v0.15.6
|
||||
uses: pascalgn/automerge-action@v0.16.3
|
||||
env:
|
||||
BASE_BRANCHES: nightly
|
||||
GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
|
||||
|
||||
51
.github/workflows/codeql.yml
vendored
51
.github/workflows/codeql.yml
vendored
@@ -16,7 +16,7 @@ on:
|
||||
- cron: '00 12 * * 0' # every Sunday at 12:00 UTC
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
@@ -57,10 +57,25 @@ jobs:
|
||||
console.log(`Remapping language: ${key} to ${remap_languages[key.toLowerCase()]}`)
|
||||
key = remap_languages[key.toLowerCase()]
|
||||
}
|
||||
if (supported_languages.includes(key.toLowerCase()) &&
|
||||
!matrix['include'].includes({"language": key.toLowerCase()})) {
|
||||
if (supported_languages.includes(key.toLowerCase())) {
|
||||
console.log(`Found supported language: ${key}`)
|
||||
matrix['include'].push({"language": key.toLowerCase()})
|
||||
let osList = ['ubuntu-latest'];
|
||||
if (key.toLowerCase() === 'swift') {
|
||||
osList = ['macos-latest'];
|
||||
} else if (key.toLowerCase() === 'cpp') {
|
||||
osList = ['macos-latest', 'ubuntu-latest', 'windows-latest'];
|
||||
}
|
||||
for (let os of osList) {
|
||||
// set name for matrix
|
||||
if (osList.length == 1) {
|
||||
name = key.toLowerCase()
|
||||
} else {
|
||||
name = `${key.toLowerCase()}, ${os}`
|
||||
}
|
||||
|
||||
// add to matrix
|
||||
matrix['include'].push({"language": key.toLowerCase(), "os": os, "name": name})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +99,15 @@ jobs:
|
||||
}
|
||||
|
||||
analyze:
|
||||
name: Analyze
|
||||
name: Analyze (${{ matrix.name }})
|
||||
if: ${{ needs.languages.outputs.continue == 'true' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.os == 'windows-latest' && 'msys2 {0}' || 'bash' }}
|
||||
env:
|
||||
GITHUB_CODEQL_BUILD: true
|
||||
needs: [languages]
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -100,6 +120,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Maximize build space
|
||||
if: runner.os == 'Linux'
|
||||
uses: easimon/maximize-build-space@v8
|
||||
with:
|
||||
root-reserve-mb: 20480
|
||||
@@ -114,6 +135,12 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup msys2
|
||||
if: runner.os == 'Windows'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
@@ -129,16 +156,20 @@ jobs:
|
||||
|
||||
# Pre autobuild
|
||||
# create a file named .codeql-prebuild-${{ matrix.language }}.sh in the root of your repository
|
||||
# create a file named .codeql-build-${{ matrix.language }}.sh in the root of your repository
|
||||
- name: Prebuild
|
||||
id: prebuild
|
||||
run: |
|
||||
# check if .qodeql-prebuild-${{ matrix.language }}.sh exists
|
||||
if [ -f "./.codeql-prebuild-${{ matrix.language }}.sh" ]; then
|
||||
echo "Running .codeql-prebuild-${{ matrix.language }}.sh"
|
||||
./.codeql-prebuild-${{ matrix.language }}.sh
|
||||
# check if prebuild script exists
|
||||
filename=".codeql-prebuild-${{ matrix.language }}-${{ runner.os }}.sh"
|
||||
if [ -f "./${filename}" ]; then
|
||||
echo "Running prebuild script: ${filename}"
|
||||
./${filename}
|
||||
fi
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
- name: Autobuild
|
||||
if: steps.prebuild.outputs.skip_autobuild != 'true'
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
|
||||
2
.github/workflows/cpp-lint.yml
vendored
2
.github/workflows/cpp-lint.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
- name: Clang format lint
|
||||
if: ${{ steps.find_files.outputs.found_files }}
|
||||
uses: DoozyX/clang-format-lint-action@v0.16.2
|
||||
uses: DoozyX/clang-format-lint-action@v0.17
|
||||
with:
|
||||
source: ${{ steps.find_files.outputs.found_files }}
|
||||
extensions: 'cpp,h,m,mm'
|
||||
|
||||
2
.github/workflows/update-pages.yml
vendored
2
.github/workflows/update-pages.yml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
if: >-
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
|
||||
(github.event_name == 'workflow_dispatch')
|
||||
uses: actions-js/push@v1.4
|
||||
uses: actions-js/push@v1.5
|
||||
with:
|
||||
github_token: ${{ secrets.GH_BOT_TOKEN }}
|
||||
author_email: ${{ secrets.GH_BOT_EMAIL }}
|
||||
|
||||
50
.gitignore
vendored
50
.gitignore
vendored
@@ -1,13 +1,45 @@
|
||||
build
|
||||
cmake-build*
|
||||
.DS_Store
|
||||
.vscode
|
||||
.vs
|
||||
*.swp
|
||||
*.kdev4
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
.cache
|
||||
.idea
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# JetBrains IDE
|
||||
.idea/
|
||||
|
||||
# VSCode IDE
|
||||
.vscode/
|
||||
|
||||
# build directories
|
||||
build/
|
||||
cmake-*/
|
||||
|
||||
# npm
|
||||
node_modules/
|
||||
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -1,7 +1,19 @@
|
||||
[submodule "packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp"]
|
||||
path = packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp
|
||||
url = https://github.com/flathub/org.flatpak.Builder.BaseApp
|
||||
branch = branch/23.08
|
||||
[submodule "packaging/linux/flatpak/deps/shared-modules"]
|
||||
path = packaging/linux/flatpak/deps/shared-modules
|
||||
url = https://github.com/flathub/shared-modules
|
||||
branch = master
|
||||
[submodule "third-party/build-deps"]
|
||||
path = third-party/build-deps
|
||||
url = https://github.com/LizardByte/build-deps.git
|
||||
branch = dist
|
||||
[submodule "third-party/googletest"]
|
||||
path = third-party/googletest
|
||||
url = https://github.com/google/googletest/
|
||||
branch = v1.14.x
|
||||
[submodule "third-party/moonlight-common-c"]
|
||||
path = third-party/moonlight-common-c
|
||||
url = https://github.com/moonlight-stream/moonlight-common-c.git
|
||||
|
||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,5 +1,65 @@
|
||||
# Changelog
|
||||
|
||||
## [0.23.1] - 2024-04-20
|
||||
**Fixed**
|
||||
- (Capture/Windows) Disable HRD and CBR encoding options by default for AMD GPUs due to video quality regressions in v0.23.0
|
||||
- (UI) Fix incorrect strings for QuickSync 'fast' and 'faster' presets
|
||||
- (UI/Linux) Fix update prompt appearing even when running the latest version
|
||||
- (Input) Fix crash when absolute input events are received prior to the display viewport being set
|
||||
- (Input/Linux) Fix missing clamping of rumble intensity to valid range
|
||||
- (Build/Tests) Fix error when attempting to disable compilation of tests
|
||||
- (Build/Linux) Fix some compilation errors when using Musl libc
|
||||
- (Logging) Fix broken debug messages for codec capability flags
|
||||
- (Logging/Linux) Fix log messages to include the correct setcap command for resolving KMS permission errors
|
||||
|
||||
**Added**
|
||||
- (Capture/Linux) Improve frame time consistency for all capture backends
|
||||
- (UI) Set focus to the PIN textbox when navigating to the PIN tab
|
||||
|
||||
**Dependencies**
|
||||
- Remove libavdevice dependency
|
||||
|
||||
**Misc**
|
||||
- (Linux) Prefer ayatana-appindicator3 over appindicator3 if both are available
|
||||
|
||||
## [0.23.0] - 2024-04-06
|
||||
Attention, this release contains critical security fixes. Please update as soon as possible.
|
||||
|
||||
**Breaking**
|
||||
- (Linux) Drop support for Ubuntu 20.04
|
||||
- (Linux) No longer provide arm64 rpm packages, due to extreme compile time on GitHub hosted runners
|
||||
|
||||
**Fixed**
|
||||
- (Network) Ensure unpairing takes effect without restart
|
||||
- (Capture/Linux) Fix logical comparison of texture size
|
||||
- (Service/Windows) Quote the path to sunshinesvc.exe when launching the termination helper
|
||||
|
||||
**Added**
|
||||
- (WebUI) Localization support
|
||||
- (Capture/Linux) Populate host latency for kmx/x11 grab
|
||||
- (Capture/Windows) AMF rate control improvements
|
||||
- (Linux) Add support for Ubuntu 24.04 (x86_64 only)
|
||||
|
||||
**Dependencies**
|
||||
- Bump rstcheck from 6.2.0 to 6.2.1
|
||||
- Bump org.flatpak.Builder.BaseApp from 644487f to 6e295e6
|
||||
- Bump ffmpeg
|
||||
- Bump @fortawesome/fontawesome-free from 6.5.1 to 6.5.2
|
||||
|
||||
**Misc**
|
||||
- (Style) Refactored video encoder declarations
|
||||
- (CI) Refactored Linux build in CI
|
||||
- (CI) Added unit testing and code coverage
|
||||
- (Docs/macOS) Update curl command for Portfile install
|
||||
- (Style) Refactor logging initialization
|
||||
|
||||
|
||||
## [0.22.2] - 2024-03-15
|
||||
**Fixed**
|
||||
- (Tray/Windows) Fix broken system tray icon on some systems
|
||||
- (Linux) Fix crash when XDG_CONFIG_HOME or CONFIGURATION_DIRECTORY are set
|
||||
- (Linux) Fix config migration across filesystems and with non-existent parent directories
|
||||
|
||||
## [0.22.1] - 2024-03-13
|
||||
**Breaking**
|
||||
- (ArchLinux) Drop support for standalone PKGBUILD files. Use the binary Arch package or install via AUR instead.
|
||||
@@ -759,3 +819,6 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
[0.21.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.21.0
|
||||
[0.22.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.22.0
|
||||
[0.22.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.22.1
|
||||
[0.22.2]: https://github.com/LizardByte/Sunshine/releases/tag/v0.22.2
|
||||
[0.23.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.23.0
|
||||
[0.23.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.23.1
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
# `CMAKE_CUDA_ARCHITECTURES` requires 3.18
|
||||
# set_source_files_properties requires 3.18
|
||||
# todo - set this conditionally
|
||||
|
||||
# todo - set version to 0.0.0 once confident in automated versioning
|
||||
project(Sunshine VERSION 0.22.1
|
||||
project(Sunshine VERSION 0.23.1
|
||||
DESCRIPTION "Self-hosted game stream host for Moonlight"
|
||||
HOMEPAGE_URL "https://app.lizardbyte.dev/Sunshine")
|
||||
|
||||
@@ -28,6 +29,9 @@ include(${CMAKE_MODULE_PATH}/prep/build_version.cmake)
|
||||
# cmake build flags
|
||||
include(${CMAKE_MODULE_PATH}/prep/options.cmake)
|
||||
|
||||
# initial prep
|
||||
include(${CMAKE_MODULE_PATH}/prep/init.cmake)
|
||||
|
||||
# configure special package files, such as sunshine.desktop, Flatpak manifest, Portfile , etc.
|
||||
include(${CMAKE_MODULE_PATH}/prep/special_package_configuration.cmake)
|
||||
|
||||
|
||||
101
README.rst
101
README.rst
@@ -17,69 +17,48 @@ System Requirements
|
||||
|
||||
**Minimum Requirements**
|
||||
|
||||
+------------+------------------------------------------------------------+
|
||||
| GPU | AMD: VCE 1.0 or higher, see `obs-amd hardware support`_ |
|
||||
| +------------------------------------------------------------+
|
||||
| | Intel: VAAPI-compatible, see: `VAAPI hardware support`_ |
|
||||
| +------------------------------------------------------------+
|
||||
| | Nvidia: NVENC enabled cards, see `nvenc support matrix`_ |
|
||||
+------------+------------------------------------------------------------+
|
||||
| CPU | AMD: Ryzen 3 or higher |
|
||||
| +------------------------------------------------------------+
|
||||
| | Intel: Core i3 or higher |
|
||||
+------------+------------------------------------------------------------+
|
||||
| RAM | 4GB or more |
|
||||
+------------+------------------------------------------------------------+
|
||||
| OS | Windows: 10+ (Windows Server not supported) |
|
||||
| +------------------------------------------------------------+
|
||||
| | macOS: 12+ |
|
||||
| +------------------------------------------------------------+
|
||||
| | Linux/Debian: 11 (bullseye) |
|
||||
| +------------------------------------------------------------+
|
||||
| | Linux/Fedora: 38+ |
|
||||
| +------------------------------------------------------------+
|
||||
| | Linux/Ubuntu: 20.04+ (focal) |
|
||||
+------------+------------------------------------------------------------+
|
||||
| Network | Host: 5GHz, 802.11ac |
|
||||
| +------------------------------------------------------------+
|
||||
| | Client: 5GHz, 802.11ac |
|
||||
+------------+------------------------------------------------------------+
|
||||
.. csv-table::
|
||||
:widths: 15, 60
|
||||
|
||||
"GPU", "AMD: VCE 1.0 or higher, see: `obs-amd hardware support <https://github.com/obsproject/obs-amd-encoder/wiki/Hardware-Support>`_"
|
||||
"", "Intel: VAAPI-compatible, see: `VAAPI hardware support <https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html>`_"
|
||||
"", "Nvidia: NVENC enabled cards, see: `nvenc support matrix <https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new>`_"
|
||||
"CPU", "AMD: Ryzen 3 or higher"
|
||||
"", "Intel: Core i3 or higher"
|
||||
"RAM", "4GB or more"
|
||||
"OS", "Windows: 10+ (Windows Server does not support virtual gamepads)"
|
||||
"", "macOS: 12+"
|
||||
"", "Linux/Debian: 11 (bullseye)"
|
||||
"", "Linux/Fedora: 38+"
|
||||
"", "Linux/Ubuntu: 22.04+ (jammy)"
|
||||
"Network", "Host: 5GHz, 802.11ac"
|
||||
"", "Client: 5GHz, 802.11ac"
|
||||
|
||||
**4k Suggestions**
|
||||
|
||||
+------------+------------------------------------------------------------+
|
||||
| GPU | AMD: Video Coding Engine 3.1 or higher |
|
||||
| +------------------------------------------------------------+
|
||||
| | Intel: HD Graphics 510 or higher |
|
||||
| +------------------------------------------------------------+
|
||||
| | Nvidia: GeForce GTX 1080 or higher |
|
||||
+------------+------------------------------------------------------------+
|
||||
| CPU | AMD: Ryzen 5 or higher |
|
||||
| +------------------------------------------------------------+
|
||||
| | Intel: Core i5 or higher |
|
||||
+------------+------------------------------------------------------------+
|
||||
| Network | Host: CAT5e ethernet or better |
|
||||
| +------------------------------------------------------------+
|
||||
| | Client: CAT5e ethernet or better |
|
||||
+------------+------------------------------------------------------------+
|
||||
.. csv-table::
|
||||
:widths: 15, 60
|
||||
|
||||
"GPU", "AMD: Video Coding Engine 3.1 or higher"
|
||||
"", "Intel: HD Graphics 510 or higher"
|
||||
"", "Nvidia: GeForce GTX 1080 or higher"
|
||||
"CPU", "AMD: Ryzen 5 or higher"
|
||||
"", "Intel: Core i5 or higher"
|
||||
"Network", "Host: CAT5e ethernet or better"
|
||||
"", "Client: CAT5e ethernet or better"
|
||||
|
||||
**HDR Suggestions**
|
||||
|
||||
+------------+------------------------------------------------------------+
|
||||
| GPU | AMD: Video Coding Engine 3.4 or higher |
|
||||
| +------------------------------------------------------------+
|
||||
| | Intel: UHD Graphics 730 or higher |
|
||||
| +------------------------------------------------------------+
|
||||
| | Nvidia: Pascal-based GPU (GTX 10-series) or higher |
|
||||
+------------+------------------------------------------------------------+
|
||||
| CPU | AMD: todo |
|
||||
| +------------------------------------------------------------+
|
||||
| | Intel: todo |
|
||||
+------------+------------------------------------------------------------+
|
||||
| Network | Host: CAT5e ethernet or better |
|
||||
| +------------------------------------------------------------+
|
||||
| | Client: CAT5e ethernet or better |
|
||||
+------------+------------------------------------------------------------+
|
||||
.. csv-table::
|
||||
:widths: 15, 60
|
||||
|
||||
"GPU", "AMD: Video Coding Engine 3.4 or higher"
|
||||
"", "Intel: UHD Graphics 730 or higher"
|
||||
"", "Nvidia: Pascal-based GPU (GTX 10-series) or higher"
|
||||
"CPU", "AMD: todo"
|
||||
"", "Intel: todo"
|
||||
"Network", "Host: CAT5e ethernet or better"
|
||||
"", "Client: CAT5e ethernet or better"
|
||||
|
||||
Integrations
|
||||
------------
|
||||
@@ -96,6 +75,10 @@ Integrations
|
||||
:alt: Read the Docs
|
||||
:target: http://sunshinestream.readthedocs.io/
|
||||
|
||||
.. image:: https://img.shields.io/codecov/c/gh/LizardByte/Sunshine?token=SMGXQ5NVMJ&style=for-the-badge&logo=codecov&label=codecov
|
||||
:alt: Codecov
|
||||
:target: https://codecov.io/gh/LizardByte/Sunshine
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
@@ -122,7 +105,3 @@ Stats
|
||||
.. image:: https://img.shields.io/github/stars/lizardbyte/sunshine.svg?logo=github&style=for-the-badge
|
||||
:alt: GitHub stars
|
||||
:target: https://github.com/LizardByte/Sunshine
|
||||
|
||||
.. _nvenc support matrix: https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new
|
||||
.. _obs-amd hardware support: https://github.com/obsproject/obs-amd-encoder/wiki/Hardware-Support
|
||||
.. _VAAPI hardware support: https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html
|
||||
|
||||
@@ -110,11 +110,6 @@ set(SUNSHINE_TARGET_FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/stat_trackers.cpp"
|
||||
${PLATFORM_TARGET_FILES})
|
||||
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/upnp.cpp" PROPERTIES COMPILE_FLAGS -Wno-pedantic)
|
||||
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/nanors/rs.c"
|
||||
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")
|
||||
|
||||
if(NOT SUNSHINE_ASSETS_DIR_DEF)
|
||||
set(SUNSHINE_ASSETS_DIR_DEF "${SUNSHINE_ASSETS_DIR}")
|
||||
endif()
|
||||
@@ -134,15 +129,6 @@ include_directories(
|
||||
${PLATFORM_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
if("${BUILD_TYPE}" STREQUAL "XDEBUG")
|
||||
if(WIN32)
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/nvhttp.cpp" PROPERTIES COMPILE_FLAGS -O2)
|
||||
endif()
|
||||
else()
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
|
||||
${MINIUPNP_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
|
||||
@@ -8,10 +8,6 @@ if(${SUNSHINE_BUILD_APPIMAGE})
|
||||
string(REPLACE "${CMAKE_INSTALL_PREFIX}" ".${CMAKE_INSTALL_PREFIX}" SUNSHINE_ASSETS_DIR_DEF ${SUNSHINE_ASSETS_DIR})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH)
|
||||
set(SUNSHINE_EXECUTABLE_PATH "sunshine")
|
||||
endif()
|
||||
|
||||
# cuda
|
||||
set(CUDA_FOUND OFF)
|
||||
if(${SUNSHINE_ENABLE_CUDA})
|
||||
@@ -203,13 +199,13 @@ endif()
|
||||
|
||||
# tray icon
|
||||
if(${SUNSHINE_ENABLE_TRAY})
|
||||
pkg_check_modules(APPINDICATOR appindicator3-0.1)
|
||||
pkg_check_modules(APPINDICATOR ayatana-appindicator3-0.1)
|
||||
if(APPINDICATOR_FOUND)
|
||||
list(APPEND SUNSHINE_DEFINITIONS TRAY_LEGACY_APPINDICATOR=1)
|
||||
list(APPEND SUNSHINE_DEFINITIONS TRAY_AYATANA_APPINDICATOR=1)
|
||||
else()
|
||||
pkg_check_modules(APPINDICATOR ayatana-appindicator3-0.1)
|
||||
pkg_check_modules(APPINDICATOR appindicator3-0.1)
|
||||
if(APPINDICATOR_FOUND)
|
||||
list(APPEND SUNSHINE_DEFINITIONS TRAY_AYATANA_APPINDICATOR=1)
|
||||
list(APPEND SUNSHINE_DEFINITIONS TRAY_LEGACY_APPINDICATOR=1)
|
||||
endif ()
|
||||
endif()
|
||||
pkg_check_modules(LIBNOTIFY libnotify)
|
||||
|
||||
@@ -29,16 +29,6 @@ file(GLOB NVPREFS_FILES CONFIGURE_DEPENDS
|
||||
|
||||
# vigem
|
||||
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include")
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
|
||||
PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650")
|
||||
set(VIGEM_COMPILE_FLAGS "")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unknown-pragmas ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-misleading-indentation ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-class-memaccess ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-function ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-variable ")
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
|
||||
PROPERTIES COMPILE_FLAGS ${VIGEM_COMPILE_FLAGS})
|
||||
|
||||
# sunshine icon
|
||||
if(NOT DEFINED SUNSHINE_ICON_PATH)
|
||||
|
||||
@@ -12,10 +12,18 @@ set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/sunshine.png)
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}")
|
||||
set(CPACK_STRIP_FILES YES)
|
||||
|
||||
#install common assets
|
||||
# install common assets
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}"
|
||||
PATTERN "web" EXCLUDE)
|
||||
# copy assets to build directory, for running without install
|
||||
file(GLOB_RECURSE ALL_ASSETS
|
||||
RELATIVE "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/*")
|
||||
list(FILTER ALL_ASSETS EXCLUDE REGEX "^web/.*$") # Filter out the web directory
|
||||
foreach(asset ${ALL_ASSETS}) # Copy assets to build directory, excluding the web directory
|
||||
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/${asset}"
|
||||
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/assets")
|
||||
endforeach()
|
||||
|
||||
# install built vite assets
|
||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/assets/web"
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/assets/"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}")
|
||||
# copy assets to build directory, for running without install
|
||||
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/assets/"
|
||||
DESTINATION "${CMAKE_BINARY_DIR}/assets")
|
||||
if(${SUNSHINE_BUILD_APPIMAGE} OR ${SUNSHINE_BUILD_FLATPAK})
|
||||
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/60-sunshine.rules"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}/udev/rules.d")
|
||||
|
||||
@@ -10,15 +10,16 @@ if(SUNSHINE_PACKAGE_MACOS) # todo
|
||||
set(MAC_PREFIX "${CMAKE_PROJECT_NAME}.app/Contents")
|
||||
set(INSTALL_RUNTIME_DIR "${MAC_PREFIX}/MacOS")
|
||||
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}")
|
||||
|
||||
install(TARGETS sunshine
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR} COMPONENT Runtime)
|
||||
else()
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}")
|
||||
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/misc/uninstall_pkg.sh"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}")
|
||||
endif()
|
||||
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}")
|
||||
# copy assets to build directory, for running without install
|
||||
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/"
|
||||
DESTINATION "${CMAKE_BINARY_DIR}/assets")
|
||||
|
||||
@@ -39,6 +39,9 @@ install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/gamepad/"
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/assets/"
|
||||
DESTINATION "${SUNSHINE_ASSETS_DIR}"
|
||||
COMPONENT assets)
|
||||
# copy assets to build directory, for running without install
|
||||
file(COPY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/assets/"
|
||||
DESTINATION "${CMAKE_BINARY_DIR}/assets")
|
||||
|
||||
# set(CPACK_NSIS_MUI_HEADERIMAGE "") # TODO: image should be 150x57 bmp
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}\\\\sunshine.ico")
|
||||
|
||||
9
cmake/prep/init.cmake
Normal file
9
cmake/prep/init.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
if (WIN32)
|
||||
elseif (APPLE)
|
||||
elseif (UNIX)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH)
|
||||
set(SUNSHINE_EXECUTABLE_PATH "sunshine")
|
||||
endif()
|
||||
endif ()
|
||||
@@ -1,3 +1,10 @@
|
||||
option(BUILD_TESTS "Build tests" ON)
|
||||
option(TESTS_ENABLE_PYTHON_TESTS "Enable Python tests" ON)
|
||||
|
||||
# DirectX11 is not available in GitHub runners, so even software encoding fails
|
||||
set(TESTS_SOFTWARE_ENCODER_UNAVAILABLE "fail"
|
||||
CACHE STRING "How to handle unavailable software encoders in tests. 'fail/skip'")
|
||||
|
||||
option(BUILD_WERROR "Enable -Werror flag." OFF)
|
||||
|
||||
# if this option is set, the build will exit after configuring special package configuration files
|
||||
|
||||
@@ -6,8 +6,6 @@ if (APPLE)
|
||||
configure_file(packaging/macos/sunshine.rb sunshine.rb @ONLY)
|
||||
endif()
|
||||
elseif (UNIX)
|
||||
include(GNUInstallDirs) # this needs to be included prior to configuring the desktop files
|
||||
|
||||
# configure the .desktop file
|
||||
if(${SUNSHINE_BUILD_APPIMAGE})
|
||||
configure_file(packaging/linux/AppImage/sunshine.desktop sunshine.desktop @ONLY)
|
||||
@@ -35,6 +33,7 @@ elseif (UNIX)
|
||||
# configure the flatpak manifest
|
||||
if(${SUNSHINE_CONFIGURE_FLATPAK_MAN})
|
||||
configure_file(packaging/linux/flatpak/dev.lizardbyte.sunshine.yml dev.lizardbyte.sunshine.yml @ONLY)
|
||||
file(COPY packaging/linux/flatpak/deps/ DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
|
||||
add_executable(sunshine ${SUNSHINE_TARGET_FILES})
|
||||
|
||||
# Homebrew build fails the vite build if we set these environment variables
|
||||
# this block must be before the platform specific code
|
||||
if(${SUNSHINE_BUILD_HOMEBREW})
|
||||
set(NPM_SOURCE_ASSETS_DIR "")
|
||||
set(NPM_ASSETS_DIR "")
|
||||
set(NPM_BUILD_HOMEBREW "true")
|
||||
else()
|
||||
set(NPM_SOURCE_ASSETS_DIR ${SUNSHINE_SOURCE_ASSETS_DIR})
|
||||
set(NPM_ASSETS_DIR ${CMAKE_BINARY_DIR})
|
||||
set(NPM_BUILD_HOMEBREW "")
|
||||
endif()
|
||||
|
||||
# platform specific target definitions
|
||||
if(WIN32)
|
||||
include(${CMAKE_MODULE_PATH}/targets/windows.cmake)
|
||||
@@ -37,19 +49,50 @@ endif()
|
||||
|
||||
target_compile_options(sunshine PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SUNSHINE_COMPILE_OPTIONS}>;$<$<COMPILE_LANGUAGE:CUDA>:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301
|
||||
|
||||
# Homebrew build fails the vite build if we set these environment variables
|
||||
if(${SUNSHINE_BUILD_HOMEBREW})
|
||||
set(NPM_SOURCE_ASSETS_DIR "")
|
||||
set(NPM_ASSETS_DIR "")
|
||||
set(NPM_BUILD_HOMEBREW "true")
|
||||
else()
|
||||
set(NPM_SOURCE_ASSETS_DIR ${SUNSHINE_SOURCE_ASSETS_DIR})
|
||||
set(NPM_ASSETS_DIR ${CMAKE_BINARY_DIR})
|
||||
set(NPM_BUILD_HOMEBREW "")
|
||||
# tests
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
#WebUI build
|
||||
add_custom_target(web-ui ALL
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Installing NPM Dependencies and Building the Web UI"
|
||||
COMMAND bash -c \"npm install && SUNSHINE_BUILD_HOMEBREW=${NPM_BUILD_HOMEBREW} SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR} SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR} npm run build\") # cmake-lint: disable=C0301
|
||||
# custom compile flags, must be after adding tests
|
||||
|
||||
if (NOT BUILD_TESTS)
|
||||
set(TEST_DIR "")
|
||||
else()
|
||||
set(TEST_DIR "${CMAKE_SOURCE_DIR}/tests")
|
||||
endif()
|
||||
|
||||
# src/upnp
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/upnp.cpp"
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}"
|
||||
PROPERTIES COMPILE_FLAGS -Wno-pedantic)
|
||||
|
||||
# third-party/nanors
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/nanors/rs.c"
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}"
|
||||
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")
|
||||
|
||||
# third-party/ViGEmClient
|
||||
set(VIGEM_COMPILE_FLAGS "")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unknown-pragmas ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-misleading-indentation ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-class-memaccess ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-function ")
|
||||
string(APPEND VIGEM_COMPILE_FLAGS "-Wno-unused-variable ")
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}"
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650"
|
||||
COMPILE_FLAGS ${VIGEM_COMPILE_FLAGS})
|
||||
|
||||
# src/nvhttp
|
||||
string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
if("${BUILD_TYPE}" STREQUAL "XDEBUG")
|
||||
if(WIN32)
|
||||
set_source_files_properties("${CMAKE_SOURCE_DIR}/src/nvhttp.cpp"
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/tests"
|
||||
PROPERTIES COMPILE_FLAGS -O2)
|
||||
endif()
|
||||
else()
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
# unix specific target definitions
|
||||
# put anything here that applies to both linux and macos
|
||||
|
||||
#WebUI build
|
||||
add_custom_target(web-ui ALL
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Installing NPM Dependencies and Building the Web UI"
|
||||
COMMAND sh -c \"npm install && SUNSHINE_BUILD_HOMEBREW=${NPM_BUILD_HOMEBREW} SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR} SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR} npm run build\") # cmake-lint: disable=C0301
|
||||
|
||||
@@ -4,3 +4,9 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
|
||||
find_library(ZLIB ZLIB1)
|
||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
|
||||
Wtsapi32.lib)
|
||||
|
||||
#WebUI build
|
||||
add_custom_target(web-ui ALL
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Installing NPM Dependencies and Building the Web UI"
|
||||
COMMAND cmd /C "npm install && set \"SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR}\" && set \"SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR}\" && npm run build") # cmake-lint: disable=C0301
|
||||
|
||||
19
codecov.yml
Normal file
19
codecov.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
codecov:
|
||||
branch: nightly
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 10%
|
||||
|
||||
comment:
|
||||
layout: "diff, flags, files"
|
||||
behavior: default
|
||||
require_changes: false # if true: only post the comment if coverage changes
|
||||
|
||||
ignore:
|
||||
- "tests"
|
||||
- "third-party"
|
||||
12
crowdin.yml
12
crowdin.yml
@@ -1,7 +1,7 @@
|
||||
---
|
||||
"base_path": "."
|
||||
"base_url": "https://api.crowdin.com" # optional (for Crowdin Enterprise only)
|
||||
"preserve_hierarchy": false # flatten tree on crowdin
|
||||
"preserve_hierarchy": true # false will flatten tree on crowdin, but doesn't work with dest option
|
||||
"pull_request_labels": [
|
||||
"crowdin",
|
||||
"l10n"
|
||||
@@ -10,6 +10,7 @@
|
||||
"files": [
|
||||
{
|
||||
"source": "/locale/*.po",
|
||||
"dest": "/%original_file_name%",
|
||||
"translation": "/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%",
|
||||
"languages_mapping": {
|
||||
"two_letters_code": {
|
||||
@@ -17,6 +18,13 @@
|
||||
"en-GB": "en_GB",
|
||||
"en-US": "en_US"
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_option": "update_as_unapproved"
|
||||
},
|
||||
{
|
||||
"source": "/src_assets/common/assets/web/public/assets/locale/en.json",
|
||||
"dest": "/sunshine.json",
|
||||
"translation": "/src_assets/common/assets/web/public/assets/locale/%two_letters_code%.%file_extension%",
|
||||
"update_option": "update_as_unapproved"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -43,7 +43,8 @@ pacman -Syu --disable-download-timeout --needed --noconfirm \
|
||||
cmake \
|
||||
cuda \
|
||||
git \
|
||||
namcap
|
||||
namcap \
|
||||
xorg-server-xvfb
|
||||
_DEPS
|
||||
|
||||
# Setup builder user
|
||||
@@ -84,6 +85,8 @@ RUN mv /build/sunshine/build/sunshine.install .
|
||||
RUN <<_PKGBUILD
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
namcap -i PKGBUILD
|
||||
makepkg -si --noconfirm
|
||||
rm -f /build/sunshine/pkg/sunshine-debug*.pkg.tar.zst
|
||||
|
||||
@@ -14,6 +14,8 @@ FROM toolchain-base as toolchain
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
ENV DISPLAY=:0
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
RUN <<_DEPS
|
||||
@@ -24,12 +26,13 @@ apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake=3.22.* \
|
||||
ca-certificates \
|
||||
doxygen \
|
||||
gcc=4:11.2.* \
|
||||
g++=4:11.2.* \
|
||||
gdb \
|
||||
git \
|
||||
graphviz \
|
||||
libayatana-appindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev=1.74.* \
|
||||
libboost-locale-dev=1.74.* \
|
||||
libboost-log-dev=1.74.* \
|
||||
@@ -54,8 +57,12 @@ apt-get install -y --no-install-recommends \
|
||||
libxfixes-dev \
|
||||
libxrandr-dev \
|
||||
libxtst-dev \
|
||||
python3.10 \
|
||||
python3.10-venv \
|
||||
udev \
|
||||
wget
|
||||
wget \
|
||||
x11-xserver-utils \
|
||||
xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
apt-get install -y --no-install-recommends \
|
||||
libmfx-dev
|
||||
@@ -98,3 +105,28 @@ chmod a+x ./cuda.run
|
||||
./cuda.run --silent --toolkit --toolkitpath=/usr/local --no-opengl-libs --no-man-page --no-drm
|
||||
rm ./cuda.run
|
||||
_INSTALL_CUDA
|
||||
|
||||
WORKDIR /
|
||||
# Write a shell script that starts Xvfb and then runs a shell
|
||||
RUN <<_ENTRYPOINT
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cat <<EOF > /entrypoint.sh
|
||||
#!/bin/bash
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
if [ "\$#" -eq 0 ]; then
|
||||
exec "/bin/bash"
|
||||
else
|
||||
exec "\$@"
|
||||
fi
|
||||
EOF
|
||||
_ENTRYPOINT
|
||||
|
||||
# Make the script executable
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# Note about CLion
|
||||
RUN echo "ATTENTION: CLion will override the entrypoint, you can disable this in the toolchain settings"
|
||||
|
||||
# Use the shell script as the entrypoint
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -32,8 +32,9 @@ apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake=3.25.* \
|
||||
doxygen \
|
||||
git \
|
||||
libavdevice-dev \
|
||||
graphviz \
|
||||
libayatana-appindicator3-dev \
|
||||
libboost-filesystem-dev=1.74.* \
|
||||
libboost-locale-dev=1.74.* \
|
||||
@@ -61,8 +62,12 @@ apt-get install -y --no-install-recommends \
|
||||
libxtst-dev \
|
||||
nodejs \
|
||||
npm \
|
||||
python3.11 \
|
||||
python3.11-venv \
|
||||
udev \
|
||||
wget
|
||||
wget \
|
||||
x11-xserver-utils \
|
||||
xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
apt-get install -y --no-install-recommends \
|
||||
libmfx-dev
|
||||
@@ -120,6 +125,17 @@ make -j "$(nproc)"
|
||||
cpack -G DEB
|
||||
_MAKE
|
||||
|
||||
# run tests
|
||||
WORKDIR /build/sunshine/build/tests
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_TEST
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
./test_sunshine --gtest_color=yes
|
||||
_TEST
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
|
||||
@@ -33,8 +33,9 @@ apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
cmake=3.18.* \
|
||||
doxygen \
|
||||
git \
|
||||
libavdevice-dev \
|
||||
graphviz \
|
||||
libayatana-appindicator3-dev \
|
||||
libboost-filesystem-dev=1.74.* \
|
||||
libboost-locale-dev=1.74.* \
|
||||
@@ -60,8 +61,12 @@ apt-get install -y --no-install-recommends \
|
||||
libxfixes-dev \
|
||||
libxrandr-dev \
|
||||
libxtst-dev \
|
||||
python3.9 \
|
||||
python3.9-venv \
|
||||
udev \
|
||||
wget
|
||||
wget \
|
||||
x11-xserver-utils \
|
||||
xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
apt-get install -y --no-install-recommends \
|
||||
libmfx-dev
|
||||
@@ -134,6 +139,17 @@ make -j "$(nproc)"
|
||||
cpack -G DEB
|
||||
_MAKE
|
||||
|
||||
# run tests
|
||||
WORKDIR /build/sunshine/build/tests
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_TEST
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
./test_sunshine --gtest_color=yes
|
||||
_TEST
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms: linux/amd64
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=fedora
|
||||
@@ -32,9 +32,11 @@ dnf -y group install "Development Tools"
|
||||
dnf -y install \
|
||||
boost-devel-1.78.0* \
|
||||
cmake-3.27.* \
|
||||
doxygen \
|
||||
gcc-13.2.* \
|
||||
gcc-c++-13.2.* \
|
||||
git \
|
||||
graphviz \
|
||||
libappindicator-gtk3-devel \
|
||||
libcap-devel \
|
||||
libcurl-devel \
|
||||
@@ -58,9 +60,11 @@ dnf -y install \
|
||||
openssl-devel \
|
||||
opus-devel \
|
||||
pulseaudio-libs-devel \
|
||||
python3.10 \
|
||||
rpm-build \
|
||||
wget \
|
||||
which
|
||||
which \
|
||||
xorg-x11-server-Xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
dnf -y install intel-mediasdk-devel
|
||||
fi
|
||||
@@ -117,6 +121,17 @@ make -j "$(nproc)"
|
||||
cpack -G RPM
|
||||
_MAKE
|
||||
|
||||
# run tests
|
||||
WORKDIR /build/sunshine/build/tests
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_TEST
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
./test_sunshine --gtest_color=yes
|
||||
_TEST
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms: linux/amd64
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=fedora
|
||||
@@ -32,9 +32,11 @@ dnf -y group install "Development Tools"
|
||||
dnf -y install \
|
||||
boost-devel-1.81.0* \
|
||||
cmake-3.27.* \
|
||||
doxygen \
|
||||
gcc-13.2.* \
|
||||
gcc-c++-13.2.* \
|
||||
git \
|
||||
graphviz \
|
||||
libappindicator-gtk3-devel \
|
||||
libcap-devel \
|
||||
libcurl-devel \
|
||||
@@ -58,9 +60,11 @@ dnf -y install \
|
||||
openssl-devel \
|
||||
opus-devel \
|
||||
pulseaudio-libs-devel \
|
||||
python3.11 \
|
||||
rpm-build \
|
||||
wget \
|
||||
which
|
||||
which \
|
||||
xorg-x11-server-Xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
dnf -y install intel-mediasdk-devel
|
||||
fi
|
||||
@@ -124,6 +128,17 @@ make -j "$(nproc)"
|
||||
cpack -G RPM
|
||||
_MAKE
|
||||
|
||||
# run tests
|
||||
WORKDIR /build/sunshine/build/tests
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_TEST
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
./test_sunshine --gtest_color=yes
|
||||
_TEST
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
|
||||
@@ -33,9 +33,10 @@ apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake=3.22.* \
|
||||
ca-certificates \
|
||||
doxygen \
|
||||
git \
|
||||
graphviz \
|
||||
libayatana-appindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev=1.74.* \
|
||||
libboost-locale-dev=1.74.* \
|
||||
libboost-log-dev=1.74.* \
|
||||
@@ -60,8 +61,12 @@ apt-get install -y --no-install-recommends \
|
||||
libxfixes-dev \
|
||||
libxrandr-dev \
|
||||
libxtst-dev \
|
||||
python3.10 \
|
||||
python3.10-venv \
|
||||
udev \
|
||||
wget
|
||||
wget \
|
||||
x11-xserver-utils \
|
||||
xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
apt-get install -y --no-install-recommends \
|
||||
libmfx-dev
|
||||
@@ -135,6 +140,17 @@ make -j "$(nproc)"
|
||||
cpack -G DEB
|
||||
_MAKE
|
||||
|
||||
# run tests
|
||||
WORKDIR /build/sunshine/build/tests
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_TEST
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
./test_sunshine --gtest_color=yes
|
||||
_TEST
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms: linux/amd64
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=ubuntu
|
||||
ARG TAG=20.04
|
||||
ARG TAG=24.04
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
@@ -31,16 +31,18 @@ set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake=3.28.* \
|
||||
ca-certificates \
|
||||
gcc-10=10.5.* \
|
||||
g++-10=10.5.* \
|
||||
doxygen \
|
||||
gcc-11 \
|
||||
g++-11 \
|
||||
git \
|
||||
graphviz \
|
||||
libayatana-appindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev=1.71.* \
|
||||
libboost-locale-dev=1.71.* \
|
||||
libboost-log-dev=1.71.* \
|
||||
libboost-program-options-dev=1.71.* \
|
||||
libboost-filesystem-dev=1.83.* \
|
||||
libboost-locale-dev=1.83.* \
|
||||
libboost-log-dev=1.83.* \
|
||||
libboost-program-options-dev=1.83.* \
|
||||
libcap-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libdrm-dev \
|
||||
@@ -61,8 +63,12 @@ apt-get install -y --no-install-recommends \
|
||||
libxfixes-dev \
|
||||
libxrandr-dev \
|
||||
libxtst-dev \
|
||||
python3.12 \
|
||||
python3.12-venv \
|
||||
udev \
|
||||
wget
|
||||
wget \
|
||||
x11-xserver-utils \
|
||||
xvfb
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
apt-get install -y --no-install-recommends \
|
||||
libmfx-dev
|
||||
@@ -71,6 +77,7 @@ apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
_DEPS
|
||||
|
||||
|
||||
#Install Node
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_INSTALL_NODE
|
||||
@@ -88,35 +95,13 @@ RUN <<_GCC_ALIAS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
update-alternatives --install \
|
||||
/usr/bin/gcc gcc /usr/bin/gcc-10 100 \
|
||||
--slave /usr/bin/g++ g++ /usr/bin/g++-10 \
|
||||
--slave /usr/bin/gcov gcov /usr/bin/gcov-10 \
|
||||
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-10 \
|
||||
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-10
|
||||
/usr/bin/gcc gcc /usr/bin/gcc-11 100 \
|
||||
--slave /usr/bin/g++ g++ /usr/bin/g++-11 \
|
||||
--slave /usr/bin/gcov gcov /usr/bin/gcov-11 \
|
||||
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-11 \
|
||||
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-11
|
||||
_GCC_ALIAS
|
||||
|
||||
# install cmake
|
||||
# sunshine requires cmake >= 3.18
|
||||
WORKDIR /build/cmake
|
||||
# https://cmake.org/download/
|
||||
ENV CMAKE_VERSION="3.25.1"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CMAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cmake_prefix="https://github.com/Kitware/CMake/releases/download/v"
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/amd64' ]]; then
|
||||
cmake_arch="x86_64"
|
||||
elif [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
cmake_arch="aarch64"
|
||||
fi
|
||||
url="${cmake_prefix}${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-${cmake_arch}.sh"
|
||||
echo "cmake url: ${url}"
|
||||
wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cmake.sh
|
||||
sh ./cmake.sh --prefix=/usr/local --skip-license
|
||||
rm ./cmake.sh
|
||||
_INSTALL_CMAKE
|
||||
|
||||
# install cuda
|
||||
WORKDIR /build/cuda
|
||||
# versions: https://developer.nvidia.com/cuda-toolkit-archive
|
||||
@@ -154,6 +139,7 @@ set -e
|
||||
#Set Node version
|
||||
source "$HOME/.nvm/nvm.sh"
|
||||
nvm use 20.9.0
|
||||
#Actually build
|
||||
cmake \
|
||||
-DBUILD_WERROR=ON \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
@@ -170,6 +156,17 @@ make -j "$(nproc)"
|
||||
cpack -G DEB
|
||||
_MAKE
|
||||
|
||||
# run tests
|
||||
WORKDIR /build/sunshine/build/tests
|
||||
# hadolint ignore=SC1091
|
||||
RUN <<_TEST
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export DISPLAY=:1
|
||||
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
|
||||
./test_sunshine --gtest_color=yes
|
||||
_TEST
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
@@ -197,9 +194,9 @@ EXPOSE 48010
|
||||
EXPOSE 47998-48000/udp
|
||||
|
||||
# setup user
|
||||
ARG PGID=1000
|
||||
ARG PGID=1001
|
||||
ENV PGID=${PGID}
|
||||
ARG PUID=1000
|
||||
ARG PUID=1001
|
||||
ENV PUID=${PUID}
|
||||
ENV TZ="UTC"
|
||||
ARG UNAME=lizard
|
||||
247
docs/Doxyfile
247
docs/Doxyfile
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.9.6
|
||||
# Doxyfile 1.10.0
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = "Sunshine"
|
||||
PROJECT_NAME = Sunshine
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
@@ -63,6 +63,12 @@ PROJECT_BRIEF = "Sunshine is a Gamestream host for Moonlight."
|
||||
|
||||
PROJECT_LOGO = ../sunshine.png
|
||||
|
||||
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
|
||||
# when the HTML document is shown. Doxygen will copy the logo to the output
|
||||
# directory.
|
||||
|
||||
PROJECT_ICON =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
@@ -365,6 +371,17 @@ MARKDOWN_SUPPORT = YES
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
|
||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
||||
# unique.
|
||||
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
|
||||
# sequence number starting at 0 and GITHUB use the lower case version of title
|
||||
# with any whitespace replaced by '-' and punctuation characters removed.
|
||||
# The default value is: DOXYGEN.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
MARKDOWN_ID_STYLE = DOXYGEN
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
# be prevented in individual cases by putting a % sign in front of the word or
|
||||
@@ -489,6 +506,14 @@ LOOKUP_CACHE_SIZE = 0
|
||||
|
||||
NUM_PROC_THREADS = 0
|
||||
|
||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
||||
# contain the date or date and time when the page was generated. Setting this to
|
||||
# NO can help when comparing the output of multiple runs.
|
||||
# Possible values are: YES, NO, DATETIME and DATE.
|
||||
# The default value is: NO.
|
||||
|
||||
TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -874,7 +899,14 @@ WARN_IF_UNDOC_ENUM_VAL = NO
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
||||
# write the warning messages in between other messages but write them at the end
|
||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
||||
# besides being in the defined file also be shown at the end of a run, unless
|
||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
@@ -953,12 +985,12 @@ INPUT_FILE_ENCODING =
|
||||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
|
||||
# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
|
||||
# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
|
||||
# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
|
||||
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
@@ -1043,9 +1075,6 @@ EXCLUDE_PATTERNS =
|
||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
@@ -1159,7 +1188,8 @@ FORTRAN_COMMENT_AFTER = 72
|
||||
SOURCE_BROWSER = NO
|
||||
|
||||
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
|
||||
# classes and enums directly into the documentation.
|
||||
# multi-line macros, enums or list initialized variables directly into the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_SOURCES = NO
|
||||
@@ -1428,15 +1458,6 @@ HTML_COLORSTYLE_SAT = 100
|
||||
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to YES can help to show when doxygen was last run and thus if the
|
||||
# documentation is up to date.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
@@ -1456,6 +1477,33 @@ HTML_DYNAMIC_MENUS = YES
|
||||
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
|
||||
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
|
||||
# dynamically folded and expanded in the generated HTML source code.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_CODE_FOLDING = YES
|
||||
|
||||
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
|
||||
# the top right corner of code and text fragments that allows the user to copy
|
||||
# its content to the clipboard. Note this only works if supported by the browser
|
||||
# and the web page is served via a secure context (see:
|
||||
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
|
||||
# protocol.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COPY_CLIPBOARD = YES
|
||||
|
||||
# Doxygen stores a couple of settings persistently in the browser (via e.g.
|
||||
# cookies). By default these settings apply to all HTML pages generated by
|
||||
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
|
||||
# the settings under a project specific key, such that the user preferences will
|
||||
# be stored separately.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_PROJECT_COOKIE =
|
||||
|
||||
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
||||
# shown in the various tree structured indices initially; the user can expand
|
||||
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
||||
@@ -1586,6 +1634,16 @@ BINARY_TOC = NO
|
||||
|
||||
TOC_EXPAND = NO
|
||||
|
||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
||||
# generated documentation will be placed on the server by the user during the
|
||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
||||
# is specified no sitemap is generated. For information about the sitemap
|
||||
# protocol see https://www.sitemaps.org
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SITEMAP_URL =
|
||||
|
||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||
@@ -2074,9 +2132,16 @@ PDF_HYPERLINKS = YES
|
||||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
|
||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
||||
# hit at every error; missing files that TeX tries to input or request from
|
||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
||||
# but there is no possibility of user interaction just like in batch mode,
|
||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
||||
# each error, asking for user intervention.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@@ -2097,14 +2162,6 @@ LATEX_HIDE_INDICES = NO
|
||||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
@@ -2270,13 +2327,39 @@ DOCBOOK_OUTPUT = doxydocbook
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to Sqlite3 output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
|
||||
# database with symbols found by doxygen stored in tables.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_SQLITE3 = NO
|
||||
|
||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||
# in front of it.
|
||||
# The default directory is: sqlite3.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_OUTPUT = sqlite3
|
||||
|
||||
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||
# will warn if a database file is already found and not modify it.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_RECREATE_DB = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2419,15 +2502,15 @@ TAGFILES =
|
||||
|
||||
GENERATE_TAGFILE =
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||
# the class index. If set to NO, only the inherited external classes will be
|
||||
# listed.
|
||||
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
|
||||
# will be listed in the class and namespace index. If set to NO, only the
|
||||
# inherited external classes will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
ALLEXTERNALS = NO
|
||||
|
||||
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
||||
# in the modules index. If set to NO, only the current project's groups will be
|
||||
# in the topic index. If set to NO, only the current project's groups will be
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
@@ -2441,16 +2524,9 @@ EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
# Configuration options related to diagram generator tools
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
# The default value is: YES.
|
||||
@@ -2459,7 +2535,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
|
||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||
# available from the path. This tool is part of Graphviz (see:
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
@@ -2512,13 +2588,19 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
||||
# generate a graph for each documented class showing the direct and indirect
|
||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
||||
# relations will be shown as texts / links. Explicit enabling an inheritance
|
||||
# graph or choosing a different representation for an inheritance graph of a
|
||||
# specific class, can be accomplished by means of the command \inheritancegraph.
|
||||
# Disabling an inheritance graph can be accomplished by means of the command
|
||||
# \hideinheritancegraph.
|
||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_GRAPH = YES
|
||||
@@ -2526,15 +2608,21 @@ CLASS_GRAPH = YES
|
||||
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect implementation
|
||||
# dependencies (inheritance, containment, and class references variables) of the
|
||||
# class with other documented classes.
|
||||
# class with other documented classes. Explicit enabling a collaboration graph,
|
||||
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
|
||||
# command \collaborationgraph. Disabling a collaboration graph can be
|
||||
# accomplished by means of the command \hidecollaborationgraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
COLLABORATION_GRAPH = YES
|
||||
|
||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||
# in the manual.
|
||||
# groups, showing the direct groups dependencies. Explicit enabling a group
|
||||
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
|
||||
# of the command \groupgraph. Disabling a directory graph can be accomplished by
|
||||
# means of the command \hidegroupgraph. See also the chapter Grouping in the
|
||||
# manual.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2576,8 +2664,8 @@ DOT_UML_DETAILS = NO
|
||||
|
||||
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
|
||||
# to display on a single line. If the actual line length exceeds this threshold
|
||||
# significantly it will wrapped across multiple lines. Some heuristics are apply
|
||||
# to avoid ugly line breaks.
|
||||
# significantly it will be wrapped across multiple lines. Some heuristics are
|
||||
# applied to avoid ugly line breaks.
|
||||
# Minimum value: 0, maximum value: 1000, default value: 17.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2594,7 +2682,9 @@ TEMPLATE_RELATIONS = NO
|
||||
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
||||
# YES then doxygen will generate a graph for each documented file showing the
|
||||
# direct and indirect include dependencies of the file with other documented
|
||||
# files.
|
||||
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
|
||||
# can be accomplished by means of the command \includegraph. Disabling an
|
||||
# include graph can be accomplished by means of the command \hideincludegraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2603,7 +2693,10 @@ INCLUDE_GRAPH = YES
|
||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
# the direct and indirect include dependencies of the file with other documented
|
||||
# files.
|
||||
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
|
||||
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
|
||||
# an included by graph can be accomplished by means of the command
|
||||
# \hideincludedbygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2643,7 +2736,10 @@ GRAPHICAL_HIERARCHY = YES
|
||||
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
||||
# dependencies a directory has on other directories in a graphical way. The
|
||||
# dependency relations are determined by the #include relations between the
|
||||
# files in the directories.
|
||||
# files in the directories. Explicit enabling a directory graph, when
|
||||
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
|
||||
# \directorygraph. Disabling a directory graph can be accomplished by means of
|
||||
# the command \hidedirectorygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2659,7 +2755,7 @@ DIR_GRAPH_MAX_DEPTH = 1
|
||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
# http://www.graphviz.org/)).
|
||||
# https://www.graphviz.org/)).
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
@@ -2696,11 +2792,12 @@ DOT_PATH =
|
||||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
MSCFILE_DIRS =
|
||||
DIA_PATH =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
@@ -2777,3 +2874,19 @@ GENERATE_LEGEND = YES
|
||||
# The default value is: YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
||||
# output file formats "png", "eps", "svg", and "ismap".
|
||||
|
||||
MSCGEN_TOOL =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXOPTS ?= -W --keep-going
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
@@ -9,6 +9,7 @@ if "%SPHINXBUILD%" == "" (
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
set "SPHINXOPTS=-W --keep-going"
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
@@ -25,11 +26,11 @@ if errorlevel 9009 (
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL%
|
||||
|
||||
:end
|
||||
popd
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
breathe==4.35.0
|
||||
furo==2024.1.29
|
||||
m2r2==0.3.3.post2
|
||||
rstcheck[sphinx]==6.2.0
|
||||
rstcheck[sphinx]==6.2.1
|
||||
rstfmt==0.0.14
|
||||
setuptools # required by m2r2, Ubuntu 24.04 doesn't include this
|
||||
Sphinx==7.2.6
|
||||
sphinx-copybutton==0.5.2
|
||||
sphinx_inline_tabs==2023.4.21
|
||||
|
||||
@@ -47,6 +47,42 @@ editing the `conf` file in a text editor. Use the examples as reference.
|
||||
`General <https://localhost:47990/config/#general>`__
|
||||
-----------------------------------------------------
|
||||
|
||||
`locale <https://localhost:47990/config/#locale>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The locale used for Sunshine's user interface.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
======= ===========
|
||||
Value Description
|
||||
======= ===========
|
||||
de German
|
||||
en English
|
||||
en_GB English (UK)
|
||||
en_US English (United States)
|
||||
es Spanish
|
||||
fr French
|
||||
it Italian
|
||||
ja Japanese
|
||||
pt Portuguese
|
||||
ru Russian
|
||||
sv Swedish
|
||||
zh Chinese (Simplified)
|
||||
======= ===========
|
||||
|
||||
**Default**
|
||||
``en``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
locale = en
|
||||
|
||||
`sunshine_name <https://localhost:47990/config/#sunshine_name>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -1437,11 +1473,115 @@ keybindings
|
||||
`AMD AMF Encoder <https://localhost:47990/config/#amd-amf-encoder>`__
|
||||
---------------------------------------------------------------------
|
||||
|
||||
`amd_usage <https://localhost:47990/config/#amd_usage>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The encoder usage profile is used to set the base set of encoding
|
||||
parameters.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
.. note:: The other AMF options that follow will override a subset
|
||||
of the settings applied by your usage profile, but there are
|
||||
hidden parameters set in usage profiles that cannot be
|
||||
overridden elsewhere.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
======================= ===========
|
||||
Value Description
|
||||
======================= ===========
|
||||
transcoding transcoding (slowest)
|
||||
webcam webcam (slow)
|
||||
lowlatency_high_quality low latency, high quality (fast)
|
||||
lowlatency low latency (faster)
|
||||
ultralowlatency ultra low latency (fastest)
|
||||
======================= ===========
|
||||
|
||||
**Default**
|
||||
``ultralowlatency``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_usage = ultralowlatency
|
||||
|
||||
`amd_rc <https://localhost:47990/config/#amd_rc>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The encoder rate control.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
.. warning:: The 'vbr_latency' option generally works best, but
|
||||
some bitrate overshoots may still occur. Enabling HRD allows
|
||||
all bitrate based rate controls to better constrain peak bitrate,
|
||||
but may result in encoding artifacts depending on your card.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
=========== ===========
|
||||
Value Description
|
||||
=========== ===========
|
||||
cqp constant qp mode
|
||||
cbr constant bitrate
|
||||
vbr_latency variable bitrate, latency constrained
|
||||
vbr_peak variable bitrate, peak constrained
|
||||
=========== ===========
|
||||
|
||||
**Default**
|
||||
``vbr_latency``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_rc = vbr_latency
|
||||
|
||||
`amd_enforce_hrd <https://localhost:47990/config/#amd_enforce_hrd>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
Enable Hypothetical Reference Decoder (HRD) enforcement to help constrain the target bitrate.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
.. warning:: HRD is known to cause encoding artifacts or negatively affect
|
||||
encoding quality on certain cards.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
======== ===========
|
||||
Value Description
|
||||
======== ===========
|
||||
enabled enable HRD
|
||||
disabled disable HRD
|
||||
======== ===========
|
||||
|
||||
**Default**
|
||||
``disabled``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_enforce_hrd = disabled
|
||||
|
||||
`amd_quality <https://localhost:47990/config/#amd_quality>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The encoder preset to use.
|
||||
The quality profile controls the tradeoff between
|
||||
speed and quality of encoding.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
@@ -1466,65 +1606,6 @@ keybindings
|
||||
|
||||
amd_quality = balanced
|
||||
|
||||
`amd_rc <https://localhost:47990/config/#amd_rc>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The encoder rate control.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
=========== ===========
|
||||
Value Description
|
||||
=========== ===========
|
||||
cqp constant qp mode
|
||||
cbr constant bitrate
|
||||
vbr_latency variable bitrate, latency constrained
|
||||
vbr_peak variable bitrate, peak constrained
|
||||
=========== ===========
|
||||
|
||||
**Default**
|
||||
``vbr_latency``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_rc = vbr_latency
|
||||
|
||||
`amd_usage <https://localhost:47990/config/#amd_usage>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The encoder usage profile, used to balance latency with encoding quality.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
=============== ===========
|
||||
Value Description
|
||||
=============== ===========
|
||||
transcoding transcoding (slowest)
|
||||
webcam webcam (slow)
|
||||
lowlatency low latency (fast)
|
||||
ultralowlatency ultra low latency (fastest)
|
||||
=============== ===========
|
||||
|
||||
**Default**
|
||||
``ultralowlatency``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_usage = ultralowlatency
|
||||
|
||||
`amd_preanalysis <https://localhost:47990/config/#amd_preanalysis>`__
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -1546,7 +1627,9 @@ keybindings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
Variance Based Adaptive Quantization (VBAQ) can increase subjective visual quality.
|
||||
Variance Based Adaptive Quantization (VBAQ) can increase subjective
|
||||
visual quality by prioritizing allocation of more bits to smooth
|
||||
areas compared to more textured areas.
|
||||
|
||||
.. note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
|
||||
30
docs/source/about/guides/linux/discord_calls.rst
Normal file
30
docs/source/about/guides/linux/discord_calls.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
How to not stream Discord call audio
|
||||
====================================
|
||||
|
||||
#. Set your normal `Sound Output` volume to 100%
|
||||
|
||||
.. image:: ../../../images/discord_calls_01.png
|
||||
|
||||
#. Start Sunshine
|
||||
|
||||
#. Set `Sound Output` to `sink-sunshine-stereo` (if it isn't automatic)
|
||||
|
||||
.. image:: ../../../images/discord_calls_02.png
|
||||
|
||||
#. In Discord - `Right Click` - `Deafen` - Select your normal `Output Device`
|
||||
|
||||
This is also where you will need to adjust output volume for Discord calls
|
||||
|
||||
.. image:: ../../../images/discord_calls_03.png
|
||||
|
||||
#. Open `qpwgraph`
|
||||
|
||||
.. image:: ../../../images/discord_calls_04.png
|
||||
|
||||
#. Connect `sunshine [sunshine-record]` to your normal `Output Device`
|
||||
|
||||
* Drag `monitor_FL` to `playback_FL`
|
||||
|
||||
* Drag `monitor_FR` to `playback_FR`
|
||||
|
||||
.. image:: ../../../images/discord_calls_05.png
|
||||
@@ -45,8 +45,8 @@ Install
|
||||
sunshine-debian-bullseye-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-fedora-38-{arch}.rpm 12.4.0 525.60.13 50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-fedora-39-{arch}.rpm 12.4.0 525.60.13 50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-ubuntu-20.04-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-ubuntu-22.04-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-ubuntu-24.04-{arch}.deb 11.8.0 450.80.02 35;50;52;60;61;62;70;75;80;86;90
|
||||
=========================================== ============== ============== ================================
|
||||
|
||||
.. tab:: AppImage
|
||||
@@ -307,7 +307,7 @@ Install
|
||||
|
||||
mkdir -p ~/ports/multimedia/sunshine
|
||||
cd ~/ports/multimedia/sunshine
|
||||
curl -O https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile
|
||||
curl -OL https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile
|
||||
cd ~/ports
|
||||
portindex
|
||||
sudo port install sunshine
|
||||
|
||||
@@ -15,7 +15,6 @@ Install Requirements
|
||||
sudo apt update && sudo apt install \
|
||||
build-essential \
|
||||
cmake \
|
||||
libavdevice-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
@@ -88,61 +87,8 @@ Install Requirements
|
||||
wget \ # necessary for cuda install with `run` file
|
||||
which # necessary for cuda install with `run` file
|
||||
|
||||
Ubuntu 20.04
|
||||
^^^^^^^^^^^^
|
||||
End of Life: April 2030
|
||||
|
||||
Install Requirements
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt update && sudo apt install \
|
||||
build-essential \
|
||||
cmake \
|
||||
g++-10 \
|
||||
libayatana-appindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-program-options-dev \
|
||||
libcap-dev \ # KMS
|
||||
libcurl4-openssl-dev \
|
||||
libdrm-dev \ # KMS
|
||||
libevdev-dev \
|
||||
libminiupnpc-dev \
|
||||
libmfx-dev \ # x86_64 only
|
||||
libnotify-dev \
|
||||
libnuma-dev \
|
||||
libopus-dev \
|
||||
libpulse-dev \
|
||||
libssl-dev \
|
||||
libva-dev \ # VA-API
|
||||
libvdpau-dev \
|
||||
libwayland-dev \ # Wayland
|
||||
libx11-dev \ # X11
|
||||
libxcb-shm0-dev \ # X11
|
||||
libxcb-xfixes0-dev \ # X11
|
||||
libxcb1-dev \ # X11
|
||||
libxfixes-dev \ # X11
|
||||
libxrandr-dev \ # X11
|
||||
libxtst-dev \ # X11
|
||||
nodejs \
|
||||
npm \
|
||||
wget # necessary for cuda install with `run` file
|
||||
|
||||
Update gcc alias
|
||||
.. code-block:: bash
|
||||
|
||||
update-alternatives --install \
|
||||
/usr/bin/gcc gcc /usr/bin/gcc-10 100 \
|
||||
--slave /usr/bin/g++ g++ /usr/bin/g++-10 \
|
||||
--slave /usr/bin/gcov gcov /usr/bin/gcov-10 \
|
||||
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-10 \
|
||||
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-10
|
||||
|
||||
Ubuntu 22.04
|
||||
^^^^^^^^^^^^
|
||||
End of Life: April 2027
|
||||
|
||||
Install Requirements
|
||||
.. code-block:: bash
|
||||
@@ -151,7 +97,6 @@ Install Requirements
|
||||
build-essential \
|
||||
cmake \
|
||||
libappindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
@@ -181,11 +126,63 @@ Install Requirements
|
||||
nvidia-cuda-dev \ # CUDA, NvFBC
|
||||
nvidia-cuda-toolkit # CUDA, NvFBC
|
||||
|
||||
Ubuntu 24.04
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Install Requirements
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt update && sudo apt install \
|
||||
build-essential \
|
||||
cmake \
|
||||
gcc-11 \
|
||||
g++-11 \
|
||||
libappindicator3-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-program-options-dev \
|
||||
libcap-dev \ # KMS
|
||||
libcurl4-openssl-dev \
|
||||
libdrm-dev \ # KMS
|
||||
libevdev-dev \
|
||||
libminiupnpc-dev \
|
||||
libmfx-dev \ # x86_64 only
|
||||
libnotify-dev \
|
||||
libnuma-dev \
|
||||
libopus-dev \
|
||||
libpulse-dev \
|
||||
libssl-dev \
|
||||
libva-dev \ # VA-API
|
||||
libwayland-dev \ # Wayland
|
||||
libx11-dev \ # X11
|
||||
libxcb-shm0-dev \ # X11
|
||||
libxcb-xfixes0-dev \ # X11
|
||||
libxcb1-dev \ # X11
|
||||
libxfixes-dev \ # X11
|
||||
libxrandr-dev \ # X11
|
||||
libxtst-dev \ # X11
|
||||
nodejs \
|
||||
npm \
|
||||
nvidia-cuda-dev \ # CUDA, NvFBC
|
||||
nvidia-cuda-toolkit # CUDA, NvFBC
|
||||
|
||||
Update gcc alias
|
||||
.. code-block:: bash
|
||||
|
||||
update-alternatives --install \
|
||||
/usr/bin/gcc gcc /usr/bin/gcc-11 100 \
|
||||
--slave /usr/bin/g++ g++ /usr/bin/g++-11 \
|
||||
--slave /usr/bin/gcov gcov /usr/bin/gcov-11 \
|
||||
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-11 \
|
||||
--slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-11
|
||||
|
||||
CUDA
|
||||
----
|
||||
If the version of CUDA available from your distro is not adequate, manually install CUDA.
|
||||
|
||||
.. tip:: The version of CUDA you use will determine compatibility with various GPU generations.
|
||||
At the time of writing, the recommended version to use is CUDA ~11.8.
|
||||
See `CUDA compatibility <https://docs.nvidia.com/deploy/cuda-compatibility/index.html>`__ for more info.
|
||||
|
||||
Select the appropriate run file based on your desired CUDA version and architecture according to
|
||||
|
||||
@@ -12,14 +12,14 @@ MacPorts
|
||||
Install Requirements
|
||||
.. code-block:: bash
|
||||
|
||||
sudo port install avahi boost180 cmake curl libopus miniupnpc npm9 pkgconfig
|
||||
sudo port install avahi boost180 cmake curl doxygen graphviz libopus miniupnpc npm9 pkgconfig python311 py311-pip
|
||||
|
||||
Homebrew
|
||||
""""""""
|
||||
Install Requirements
|
||||
.. code-block:: bash
|
||||
|
||||
brew install boost cmake miniupnpc node opus pkg-config
|
||||
brew install boost cmake doxygen graphviz miniupnpc node opus pkg-config python@3.11
|
||||
|
||||
If there are issues with an SSL header that is not found:
|
||||
.. tab:: Intel
|
||||
@@ -45,7 +45,7 @@ Build
|
||||
.. code-block:: bash
|
||||
|
||||
cmake ..
|
||||
make -j ${nproc}
|
||||
make -j $(sysctl -n hw.ncpu)
|
||||
|
||||
cpack -G DragNDrop # optionally, create a macOS dmg package
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ Install dependencies:
|
||||
base-devel \
|
||||
cmake \
|
||||
diffutils \
|
||||
doxygen \
|
||||
gcc \
|
||||
git \
|
||||
make \
|
||||
@@ -25,13 +26,17 @@ Install dependencies:
|
||||
mingw-w64-x86_64-boost \
|
||||
mingw-w64-x86_64-cmake \
|
||||
mingw-w64-x86_64-curl \
|
||||
mingw-w64-x86_64-graphviz \
|
||||
mingw-w64-x86_64-miniupnpc \
|
||||
mingw-w64-x86_64-nlohmann-json \
|
||||
mingw-w64-x86_64-nodejs \
|
||||
mingw-w64-x86_64-onevpl \
|
||||
mingw-w64-x86_64-openssl \
|
||||
mingw-w64-x86_64-opus \
|
||||
mingw-w64-x86_64-toolchain
|
||||
mingw-w64-x86_64-rust \
|
||||
mingw-w64-x86_64-toolchain \
|
||||
python \
|
||||
python-pip
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
# standard imports
|
||||
from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
||||
@@ -27,16 +26,8 @@ project_copyright = f'{datetime.now ().year}, {project}'
|
||||
author = 'ReenigneArcher'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
with open(os.path.join(root_dir, 'CMakeLists.txt'), 'r') as f:
|
||||
version = re.search(r"project\(Sunshine VERSION ((\d+)\.(\d+)\.(\d+))", str(f.read())).group(1)
|
||||
"""
|
||||
To use cmake method for obtaining version instead of regex,
|
||||
1. Within CMakeLists.txt add the following line without backticks:
|
||||
``configure_file(docs/source/conf.py.in "${CMAKE_CURRENT_SOURCE_DIR}/docs/source/conf.py" @ONLY)``
|
||||
2. Rename this file to ``conf.py.in``
|
||||
3. Uncomment the next line
|
||||
"""
|
||||
# version = '@PROJECT_VERSION@' # use this for cmake configure_file method
|
||||
# https://docs.readthedocs.io/en/stable/reference/environment-variables.html#envvar-READTHEDOCS_VERSION
|
||||
version = os.getenv('READTHEDOCS_VERSION', 'dirty')
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
@@ -105,6 +96,17 @@ doxy_proc = subprocess.run('doxygen --version', shell=True, cwd=source_dir, capt
|
||||
doxy_version = doxy_proc.stdout.decode('utf-8').strip()
|
||||
print('doxygen version: ' + doxy_version)
|
||||
|
||||
# create build directories, as doxygen fails to create it in macports and docker
|
||||
directories = [
|
||||
os.path.join(source_dir, 'build'),
|
||||
os.path.join(source_dir, 'build', 'doxyxml'),
|
||||
]
|
||||
for d in directories:
|
||||
os.makedirs(
|
||||
name=d,
|
||||
exist_ok=True,
|
||||
)
|
||||
|
||||
# run doxygen
|
||||
doxy_proc = subprocess.run('doxygen Doxyfile', shell=True, cwd=source_dir)
|
||||
if doxy_proc.returncode != 0:
|
||||
|
||||
@@ -30,42 +30,84 @@ localization there.
|
||||
|
||||
Extraction
|
||||
----------
|
||||
There should be minimal cases where strings need to be extracted from source code; however it may be necessary in some
|
||||
situations. For example if a system tray icon is added it should be localized as it is user interfacing.
|
||||
|
||||
- Wrap the string to be extracted in a function as shown.
|
||||
.. code-block:: cpp
|
||||
.. tab:: UI
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <string>
|
||||
Sunshine uses `Vue I18n <https://vue-i18n.intlify.dev/>`__ for localizing the UI.
|
||||
The following is a simple example of how to use it.
|
||||
|
||||
std::string msg = boost::locale::translate("Hello world!");
|
||||
- Add the string to `src_assets/common/assets/web/public/assets/locale/en.json`, in English.
|
||||
.. code-block:: json
|
||||
|
||||
.. tip:: More examples can be found in the documentation for
|
||||
`boost locale <https://www.boost.org/doc/libs/1_70_0/libs/locale/doc/html/messages_formatting.html>`__.
|
||||
{
|
||||
"index": {
|
||||
"welcome": "Hello, Sunshine!"
|
||||
}
|
||||
}
|
||||
|
||||
.. warning:: This is for information only. Contributors should never include manually updated template files, or
|
||||
manually compiled language files in Pull Requests.
|
||||
.. note:: The json keys should be sorted alphabetically. You can use `jsonabc <https://novicelab.org/jsonabc/>`__
|
||||
to sort the keys.
|
||||
|
||||
Strings are automatically extracted from the code to the `locale/sunshine.po` template file. The generated file is
|
||||
used by CrowdIn to generate language specific template files. The file is generated using the
|
||||
`.github/workflows/localize.yml` workflow and is run on any push event into the `nightly` branch. Jobs are only run if
|
||||
any of the following paths are modified.
|
||||
.. attention:: Due to the integration with Crowdin, it is important to only add strings to the `en.json` file,
|
||||
and to not modify any other language files. After the PR is merged, the translations can take place
|
||||
on `CrowdIn <https://translate.lizardbyte.dev/>`__. Once the translations are complete, a PR will be made
|
||||
to merge the translations into Sunshine.
|
||||
|
||||
.. code-block:: yaml
|
||||
- Use the string in a Vue component.
|
||||
.. code-block:: html
|
||||
|
||||
- 'src/**'
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ $t('index.welcome') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
When testing locally it may be desirable to manually extract, initialize, update, and compile strings. Python is
|
||||
required for this, along with the python dependencies in the `./scripts/requirements.txt` file. Additionally,
|
||||
`xgettext <https://www.gnu.org/software/gettext/>`__ must be installed.
|
||||
.. tip:: More formatting examples can be found in the
|
||||
`Vue I18n guide <https://kazupon.github.io/vue-i18n/guide/formatting.html>`__.
|
||||
|
||||
**Extract, initialize, and update**
|
||||
.. code-block:: bash
|
||||
.. tab:: C++
|
||||
|
||||
python ./scripts/_locale.py --extract --init --update
|
||||
There should be minimal cases where strings need to be extracted from C++ source code; however it may be necessary in
|
||||
some situations. For example the system tray icon could be localized as it is user interfacing.
|
||||
|
||||
**Compile**
|
||||
.. code-block:: bash
|
||||
- Wrap the string to be extracted in a function as shown.
|
||||
.. code-block:: cpp
|
||||
|
||||
python ./scripts/_locale.py --compile
|
||||
#include <boost/locale.hpp>
|
||||
#include <string>
|
||||
|
||||
std::string msg = boost::locale::translate("Hello world!");
|
||||
|
||||
.. tip:: More examples can be found in the documentation for
|
||||
`boost locale <https://www.boost.org/doc/libs/1_70_0/libs/locale/doc/html/messages_formatting.html>`__.
|
||||
|
||||
.. warning:: This is for information only. Contributors should never include manually updated template files, or
|
||||
manually compiled language files in Pull Requests.
|
||||
|
||||
Strings are automatically extracted from the code to the `locale/sunshine.po` template file. The generated file is
|
||||
used by CrowdIn to generate language specific template files. The file is generated using the
|
||||
`.github/workflows/localize.yml` workflow and is run on any push event into the `nightly` branch. Jobs are only run if
|
||||
any of the following paths are modified.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- 'src/**'
|
||||
|
||||
When testing locally it may be desirable to manually extract, initialize, update, and compile strings. Python is
|
||||
required for this, along with the python dependencies in the `./scripts/requirements.txt` file. Additionally,
|
||||
`xgettext <https://www.gnu.org/software/gettext/>`__ must be installed.
|
||||
|
||||
**Extract, initialize, and update**
|
||||
.. code-block:: bash
|
||||
|
||||
python ./scripts/_locale.py --extract --init --update
|
||||
|
||||
**Compile**
|
||||
.. code-block:: bash
|
||||
|
||||
python ./scripts/_locale.py --compile
|
||||
|
||||
.. attention:: Due to the integration with Crowdin, it is important to not include any extracted or compiled files in
|
||||
Pull Requests. The files are automatically generated and updated by the workflow. Once the PR is merged, the
|
||||
translations can take place on `CrowdIn <https://translate.lizardbyte.dev/>`__. Once the translations are
|
||||
complete, a PR will be made to merge the translations into Sunshine.
|
||||
|
||||
@@ -59,5 +59,81 @@ Format inplace with rstfmt
|
||||
|
||||
Unit Testing
|
||||
------------
|
||||
.. todo:: Sunshine does not currently have any unit tests. If you would like to help us improve please get in contact
|
||||
with us, or make a PR with suggested changes.
|
||||
Sunshine uses `Google Test <https://github.com/google/googletest>`__ for unit testing. Google Test is included in the
|
||||
repo as a submodule. The test sources are located in the `./tests` directory.
|
||||
|
||||
The tests need to be compiled into an executable, and then run. The tests are built using the normal build process, but
|
||||
can be disabled by setting the `BUILD_TESTS` CMake option to `OFF`.
|
||||
|
||||
To run the tests, execute the following command from the build directory:
|
||||
|
||||
.. tab:: Linux
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pushd tests
|
||||
./test_sunshine
|
||||
popd
|
||||
|
||||
.. tab:: macOS
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pushd tests
|
||||
./test_sunshine
|
||||
popd
|
||||
|
||||
.. tab:: Windows
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pushd tests
|
||||
test_sunshine.exe
|
||||
popd
|
||||
|
||||
To see all available options, run the tests with the `--help` option.
|
||||
|
||||
.. tab:: Linux
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pushd tests
|
||||
./test_sunshine --help
|
||||
popd
|
||||
|
||||
.. tab:: macOS
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pushd tests
|
||||
./test_sunshine --help
|
||||
popd
|
||||
|
||||
.. tab:: Windows
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pushd tests
|
||||
test_sunshine.exe --help
|
||||
popd
|
||||
|
||||
Some tests rely on Python to run. CMake will search for Python and enable the docs tests if it is found, otherwise
|
||||
cmake will fail. You can manually disable the tests by setting the `TESTS_ENABLE_PYTHON_TESTS` CMake option to
|
||||
`OFF`.
|
||||
|
||||
.. tip::
|
||||
|
||||
See the googletest `FAQ <https://google.github.io/googletest/faq.html>`__ for more information on how to use
|
||||
Google Test.
|
||||
|
||||
We use `gcovr <https://www.gcovr.com/>`__ to generate code coverage reports,
|
||||
and `Codecov <https://about.codecov.io/>`__ to analyze the reports for all PRs and commits.
|
||||
|
||||
Codecov will fail a PR if the total coverage is reduced too much, or if not enough of the diff is covered by tests.
|
||||
In some cases, the code cannot be covered when running the tests inside of GitHub runners. For example, any test that
|
||||
needs access to the GPU will not be able to run. In these cases, the coverage can be omitted by adding comments to the
|
||||
code. See the `gcovr documentation <https://gcovr.com/en/stable/guide/exclusion-markers.html#exclusion-markers>`__ for
|
||||
more information.
|
||||
|
||||
Even if your changes cannot be covered in the CI, we still encourage you to write the tests for them. This will allow
|
||||
maintainers to run the tests locally.
|
||||
|
||||
BIN
docs/source/images/discord_calls_01.png
Normal file
BIN
docs/source/images/discord_calls_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/source/images/discord_calls_02.png
Normal file
BIN
docs/source/images/discord_calls_02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
docs/source/images/discord_calls_03.png
Normal file
BIN
docs/source/images/discord_calls_03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/source/images/discord_calls_04.png
Normal file
BIN
docs/source/images/discord_calls_04.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
BIN
docs/source/images/discord_calls_05.png
Normal file
BIN
docs/source/images/discord_calls_05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
@@ -4,12 +4,13 @@
|
||||
"dev": "vite build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.5.1",
|
||||
"@fortawesome/fontawesome-free": "6.5.2",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@vitejs/plugin-vue": "4.6.2",
|
||||
"bootstrap": "5.3.3",
|
||||
"vite": "4.5.2",
|
||||
"vite-plugin-ejs": "1.6.4",
|
||||
"vue": "3.4.5"
|
||||
"vue": "3.4.23",
|
||||
"vue-i18n": "9.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ pkgrel=1
|
||||
pkgdesc="@PROJECT_DESCRIPTION@"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url=@PROJECT_HOMEPAGE_URL@
|
||||
license=('GPL3')
|
||||
license=('GPL-3.0-only')
|
||||
install=sunshine.install
|
||||
|
||||
depends=('avahi'
|
||||
@@ -31,16 +31,21 @@ depends=('avahi'
|
||||
'numactl'
|
||||
'openssl'
|
||||
'opus'
|
||||
'python'
|
||||
'udev')
|
||||
checkdepends=('doxygen'
|
||||
'graphviz')
|
||||
makedepends=('boost'
|
||||
'cmake'
|
||||
'gcc12'
|
||||
'git'
|
||||
'make'
|
||||
'nodejs'
|
||||
'npm')
|
||||
optdepends=('cuda: Nvidia GPU encoding support'
|
||||
'libva-mesa-driver: AMD GPU encoding support'
|
||||
'intel-media-driver: Intel GPU encoding support')
|
||||
'intel-media-driver: Intel GPU encoding support'
|
||||
'xorg-server-xvfb: Virtual X server for headless testing')
|
||||
|
||||
provides=('sunshine')
|
||||
|
||||
@@ -57,6 +62,9 @@ build() {
|
||||
export BUILD_VERSION="@GITHUB_BUILD_VERSION@"
|
||||
export COMMIT="@GITHUB_COMMIT@"
|
||||
|
||||
export CC=gcc-12
|
||||
export CXX=g++-12
|
||||
|
||||
export CFLAGS="${CFLAGS/-Werror=format-security/}"
|
||||
export CXXFLAGS="${CXXFLAGS/-Werror=format-security/}"
|
||||
|
||||
@@ -72,6 +80,14 @@ build() {
|
||||
make -C build
|
||||
}
|
||||
|
||||
check() {
|
||||
export CC=gcc-12
|
||||
export CXX=g++-12
|
||||
|
||||
cd "${srcdir}/build/tests"
|
||||
./test_sunshine --gtest_color=yes
|
||||
}
|
||||
|
||||
package() {
|
||||
make -C build install DESTDIR="$pkgdir"
|
||||
}
|
||||
|
||||
Submodule packaging/linux/flatpak/deps/org.flatpak.Builder.BaseApp added at 5532d4354e
1
packaging/linux/flatpak/deps/shared-modules
Submodule
1
packaging/linux/flatpak/deps/shared-modules
Submodule
Submodule packaging/linux/flatpak/deps/shared-modules added at ec91811777
@@ -34,6 +34,8 @@ build-options:
|
||||
prepend-ld-library-path: /usr/lib/sdk/vala/lib
|
||||
|
||||
modules:
|
||||
- "org.flatpak.Builder.BaseApp/xvfb.json"
|
||||
|
||||
- name: boost
|
||||
disabled: false
|
||||
buildsystem: simple
|
||||
@@ -327,6 +329,9 @@ modules:
|
||||
build-args:
|
||||
- --share=network
|
||||
env:
|
||||
BUILD_VERSION: "@BUILD_VERSION@"
|
||||
BRANCH: "@GITHUB_BRANCH@"
|
||||
COMMIT: "@GITHUB_COMMIT@"
|
||||
npm_config_nodedir: /usr/lib/sdk/node18
|
||||
NPM_CONFIG_LOGLEVEL: info
|
||||
config-opts:
|
||||
@@ -341,6 +346,7 @@ modules:
|
||||
- -DSUNSHINE_ENABLE_DRM=ON
|
||||
- -DSUNSHINE_ENABLE_CUDA=ON
|
||||
- -DSUNSHINE_BUILD_FLATPAK=ON
|
||||
- -DTESTS_ENABLE_PYTHON_TESTS=OFF
|
||||
sources:
|
||||
- type: git
|
||||
url: "@GITHUB_CLONE_URL@"
|
||||
@@ -358,3 +364,7 @@ modules:
|
||||
's%/app/bin/sunshine%flatpak run dev.lizardbyte.sunshine\nExecStop=flatpak kill dev.lizardbyte.sunshine%g'
|
||||
/app/share/sunshine/systemd/user/sunshine.service
|
||||
- install -D $FLATPAK_BUILDER_BUILDDIR/packaging/linux/flatpak/scripts/* /app/bin
|
||||
run-tests: true
|
||||
test-rule: "" # empty to disable
|
||||
test-commands:
|
||||
- xvfb-run tests/test_sunshine --gtest_color=yes
|
||||
|
||||
@@ -31,13 +31,19 @@ post-fetch {
|
||||
system -W ${worksrcpath} "${git.cmd} submodule update --init --recursive"
|
||||
}
|
||||
|
||||
# https://guide.macports.org/chunked/reference.dependencies.html
|
||||
depends_build-append port:npm9 \
|
||||
port:pkgconfig
|
||||
|
||||
depends_lib port:avahi \
|
||||
port:curl \
|
||||
port:libopus \
|
||||
port:miniupnpc
|
||||
port:miniupnpc \
|
||||
port:python311 \
|
||||
port:py311-pip
|
||||
|
||||
depends_test port:doxygen \
|
||||
port:graphviz
|
||||
|
||||
boost.version 1.81
|
||||
|
||||
@@ -45,6 +51,10 @@ configure.args -DBUILD_WERROR=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=${prefix} \
|
||||
-DSUNSHINE_ASSETS_DIR=etc/sunshine/assets
|
||||
|
||||
configure.env-append BRANCH=@GITHUB_BRANCH@
|
||||
configure.env-append BUILD_VERSION=@BUILD_VERSION@
|
||||
configure.env-append COMMIT=@GITHUB_COMMIT@
|
||||
|
||||
startupitem.create yes
|
||||
startupitem.executable "${prefix}/bin/{$name}"
|
||||
startupitem.location LaunchDaemons
|
||||
@@ -62,3 +72,9 @@ notes-append "Run @PROJECT_NAME@ by executing 'sunshine <path to user config>',
|
||||
notes-append "The config file will be created if it doesn't exist."
|
||||
notes-append "It is recommended to set a location for the apps file in the config."
|
||||
notes-append "See our documentation at 'https://docs.lizardbyte.dev/projects/sunshine/en/v@PROJECT_VERSION@/' for further info."
|
||||
|
||||
test.run yes
|
||||
test.dir ${build.dir}/tests
|
||||
test.target ""
|
||||
test.cmd ./test_sunshine
|
||||
test.args --gtest_color=yes
|
||||
|
||||
@@ -7,30 +7,36 @@ class @PROJECT_NAME@ < Formula
|
||||
tag: "@GITHUB_BRANCH@"
|
||||
version "@PROJECT_VERSION@"
|
||||
license all_of: ["GPL-3.0-only"]
|
||||
head "@GITHUB_CLONE_URL@", branch: "nightly"
|
||||
head "@GITHUB_CLONE_URL@", branch: "@GITHUB_DEFAULT_BRANCH@"
|
||||
|
||||
depends_on "boost" => :build
|
||||
depends_on "cmake" => :build
|
||||
depends_on "node" => :build
|
||||
depends_on "pkg-config" => :build
|
||||
depends_on "curl"
|
||||
depends_on "miniupnpc"
|
||||
depends_on "node"
|
||||
depends_on "openssl"
|
||||
depends_on "opus"
|
||||
|
||||
def install
|
||||
ENV["BRANCH"] = "@GITHUB_BRANCH@"
|
||||
ENV["BUILD_VERSION"] = "@BUILD_VERSION@"
|
||||
ENV["COMMIT"] = "@GITHUB_COMMIT@"
|
||||
|
||||
args = %W[
|
||||
-DBUIld_WERROR=ON
|
||||
-DBUILD_WERROR=ON
|
||||
-DCMAKE_INSTALL_PREFIX=#{prefix}
|
||||
-DOPENSSL_ROOT_DIR=#{Formula["openssl"].opt_prefix}
|
||||
-DSUNSHINE_ASSETS_DIR=sunshine/assets
|
||||
-DSUNSHINE_BUILD_HOMEBREW=ON
|
||||
-DTESTS_ENABLE_PYTHON_TESTS=OFF
|
||||
]
|
||||
system "cmake", "-S", ".", "-B", "build", *std_cmake_args, *args
|
||||
|
||||
cd "build" do
|
||||
system "make", "-j"
|
||||
system "make", "install"
|
||||
bin.install "tests/test_sunshine"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,9 +60,10 @@ class @PROJECT_NAME@ < Formula
|
||||
|
||||
test do
|
||||
# test that the binary runs at all
|
||||
output = shell_output("#{bin}/sunshine --version").strip
|
||||
puts output
|
||||
system "#{bin}/sunshine", "--version"
|
||||
|
||||
# TODO: add unit tests
|
||||
# run the test suite
|
||||
# cannot build tests with python tests because homebrew destroys the source directory
|
||||
system "#{bin}/test_sunshine", "--gtest_color=yes"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,16 +22,20 @@ project_dir = os.path.join(root_dir, 'src')
|
||||
|
||||
year = datetime.datetime.now().year
|
||||
|
||||
# retroarcher target locales
|
||||
# target locales
|
||||
target_locales = [
|
||||
'de', # Deutsch
|
||||
'de', # German
|
||||
'en', # English
|
||||
'en_GB', # English (United Kingdom)
|
||||
'en_US', # English (United States)
|
||||
'es', # español
|
||||
'fr', # français
|
||||
'it', # italiano
|
||||
'ru', # русский
|
||||
'es', # Spanish
|
||||
'fr', # French
|
||||
'it', # Italian
|
||||
'ja', # Japanese
|
||||
'pt', # Portuguese
|
||||
'ru', # Russian
|
||||
'sv', # Swedish
|
||||
'zh', # Chinese
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
Babel==2.14.0
|
||||
clang-format
|
||||
|
||||
@@ -5,6 +5,7 @@ import subprocess
|
||||
# variables
|
||||
directories = [
|
||||
'src',
|
||||
'tests',
|
||||
'tools',
|
||||
os.path.join('third-party', 'glad'),
|
||||
os.path.join('third-party', 'nvfbc'),
|
||||
|
||||
105
src/config.cpp
105
src/config.cpp
@@ -85,14 +85,17 @@ namespace config {
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY 1
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY 2
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM 3
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING 0
|
||||
#define AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY_HIGH_QUALITY 5
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING 0
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY 1
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY 2
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM 3
|
||||
#define AMF_VIDEO_ENCODER_USAGE_TRANSCONDING 0
|
||||
#define AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY 5
|
||||
#define AMF_VIDEO_ENCODER_USAGE_TRANSCODING 0
|
||||
#define AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY 1
|
||||
#define AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY 2
|
||||
#define AMF_VIDEO_ENCODER_USAGE_WEBCAM 3
|
||||
#define AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY 5
|
||||
#define AMF_VIDEO_ENCODER_UNDEFINED 0
|
||||
#define AMF_VIDEO_ENCODER_CABAC 1
|
||||
#define AMF_VIDEO_ENCODER_CALV 2
|
||||
@@ -121,43 +124,46 @@ namespace config {
|
||||
};
|
||||
|
||||
enum class rc_av1_e : int {
|
||||
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR,
|
||||
cqp = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP,
|
||||
vbr_latency = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
|
||||
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
|
||||
cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR
|
||||
vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
|
||||
};
|
||||
|
||||
enum class rc_hevc_e : int {
|
||||
cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR,
|
||||
cqp = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP,
|
||||
vbr_latency = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
|
||||
vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
|
||||
cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR
|
||||
vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
|
||||
};
|
||||
|
||||
enum class rc_h264_e : int {
|
||||
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR,
|
||||
cqp = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP,
|
||||
vbr_latency = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,
|
||||
vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,
|
||||
cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR
|
||||
vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR
|
||||
};
|
||||
|
||||
enum class usage_av1_e : int {
|
||||
transcoding = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING,
|
||||
webcam = AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM,
|
||||
lowlatency_high_quality = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY_HIGH_QUALITY,
|
||||
lowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY,
|
||||
ultralowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY
|
||||
};
|
||||
|
||||
enum class usage_hevc_e : int {
|
||||
transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING,
|
||||
transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING,
|
||||
webcam = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM,
|
||||
lowlatency_high_quality = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY,
|
||||
lowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY,
|
||||
ultralowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY
|
||||
};
|
||||
|
||||
enum class usage_h264_e : int {
|
||||
transcoding = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING,
|
||||
transcoding = AMF_VIDEO_ENCODER_USAGE_TRANSCODING,
|
||||
webcam = AMF_VIDEO_ENCODER_USAGE_WEBCAM,
|
||||
lowlatency_high_quality = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY,
|
||||
lowlatency = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY,
|
||||
ultralowlatency = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY
|
||||
};
|
||||
@@ -170,40 +176,41 @@ namespace config {
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
quality_from_view(const std::string_view &quality_type) {
|
||||
quality_from_view(const std::string_view &quality_type, const std::optional<int>(&original)) {
|
||||
#define _CONVERT_(x) \
|
||||
if (quality_type == #x##sv) return (int) T::x
|
||||
_CONVERT_(balanced);
|
||||
_CONVERT_(quality);
|
||||
_CONVERT_(speed);
|
||||
_CONVERT_(balanced);
|
||||
#undef _CONVERT_
|
||||
return std::nullopt;
|
||||
return original;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
rc_from_view(const std::string_view &rc) {
|
||||
rc_from_view(const std::string_view &rc, const std::optional<int>(&original)) {
|
||||
#define _CONVERT_(x) \
|
||||
if (rc == #x##sv) return (int) T::x
|
||||
_CONVERT_(cbr);
|
||||
_CONVERT_(cqp);
|
||||
_CONVERT_(vbr_latency);
|
||||
_CONVERT_(vbr_peak);
|
||||
_CONVERT_(cbr);
|
||||
#undef _CONVERT_
|
||||
return std::nullopt;
|
||||
return original;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
usage_from_view(const std::string_view &rc) {
|
||||
usage_from_view(const std::string_view &usage, const std::optional<int>(&original)) {
|
||||
#define _CONVERT_(x) \
|
||||
if (rc == #x##sv) return (int) T::x
|
||||
_CONVERT_(transcoding);
|
||||
_CONVERT_(webcam);
|
||||
if (usage == #x##sv) return (int) T::x
|
||||
_CONVERT_(lowlatency);
|
||||
_CONVERT_(lowlatency_high_quality);
|
||||
_CONVERT_(transcoding);
|
||||
_CONVERT_(ultralowlatency);
|
||||
_CONVERT_(webcam);
|
||||
#undef _CONVERT_
|
||||
return std::nullopt;
|
||||
return original;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -212,7 +219,7 @@ namespace config {
|
||||
if (coder == "cabac"sv || coder == "ac"sv) return cabac;
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
|
||||
|
||||
return -1;
|
||||
return _auto;
|
||||
}
|
||||
} // namespace amd
|
||||
|
||||
@@ -343,15 +350,16 @@ namespace config {
|
||||
}, // qsv
|
||||
|
||||
{
|
||||
(int) amd::quality_h264_e::balanced, // quality (h264)
|
||||
(int) amd::quality_hevc_e::balanced, // quality (hevc)
|
||||
(int) amd::quality_av1_e::balanced, // quality (av1)
|
||||
(int) amd::rc_h264_e::vbr_latency, // rate control (h264)
|
||||
(int) amd::rc_hevc_e::vbr_latency, // rate control (hevc)
|
||||
(int) amd::rc_av1_e::vbr_latency, // rate control (av1)
|
||||
(int) amd::usage_h264_e::ultralowlatency, // usage (h264)
|
||||
(int) amd::usage_hevc_e::ultralowlatency, // usage (hevc)
|
||||
(int) amd::usage_av1_e::ultralowlatency, // usage (av1)
|
||||
(int) amd::rc_h264_e::vbr_latency, // rate control (h264)
|
||||
(int) amd::rc_hevc_e::vbr_latency, // rate control (hevc)
|
||||
(int) amd::rc_av1_e::vbr_latency, // rate control (av1)
|
||||
0, // enforce_hrd
|
||||
(int) amd::quality_h264_e::balanced, // quality (h264)
|
||||
(int) amd::quality_hevc_e::balanced, // quality (hevc)
|
||||
(int) amd::quality_av1_e::balanced, // quality (av1)
|
||||
0, // preanalysis
|
||||
1, // vbaq
|
||||
(int) amd::coder_e::_auto, // coder
|
||||
@@ -441,6 +449,7 @@ namespace config {
|
||||
};
|
||||
|
||||
sunshine_t sunshine {
|
||||
"en", // locale
|
||||
2, // min_log_level
|
||||
0, // flags
|
||||
{}, // User file
|
||||
@@ -973,30 +982,31 @@ namespace config {
|
||||
std::string quality;
|
||||
string_f(vars, "amd_quality", quality);
|
||||
if (!quality.empty()) {
|
||||
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality);
|
||||
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality);
|
||||
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality);
|
||||
video.amd.amd_quality_h264 = amd::quality_from_view<amd::quality_h264_e>(quality, video.amd.amd_quality_h264);
|
||||
video.amd.amd_quality_hevc = amd::quality_from_view<amd::quality_hevc_e>(quality, video.amd.amd_quality_hevc);
|
||||
video.amd.amd_quality_av1 = amd::quality_from_view<amd::quality_av1_e>(quality, video.amd.amd_quality_av1);
|
||||
}
|
||||
|
||||
std::string rc;
|
||||
string_f(vars, "amd_rc", rc);
|
||||
int_f(vars, "amd_coder", video.amd.amd_coder, amd::coder_from_view);
|
||||
if (!rc.empty()) {
|
||||
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc);
|
||||
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc);
|
||||
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc);
|
||||
video.amd.amd_rc_h264 = amd::rc_from_view<amd::rc_h264_e>(rc, video.amd.amd_rc_h264);
|
||||
video.amd.amd_rc_hevc = amd::rc_from_view<amd::rc_hevc_e>(rc, video.amd.amd_rc_hevc);
|
||||
video.amd.amd_rc_av1 = amd::rc_from_view<amd::rc_av1_e>(rc, video.amd.amd_rc_av1);
|
||||
}
|
||||
|
||||
std::string usage;
|
||||
string_f(vars, "amd_usage", usage);
|
||||
if (!usage.empty()) {
|
||||
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(rc);
|
||||
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(rc);
|
||||
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(rc);
|
||||
video.amd.amd_usage_h264 = amd::usage_from_view<amd::usage_h264_e>(usage, video.amd.amd_usage_h264);
|
||||
video.amd.amd_usage_hevc = amd::usage_from_view<amd::usage_hevc_e>(usage, video.amd.amd_usage_hevc);
|
||||
video.amd.amd_usage_av1 = amd::usage_from_view<amd::usage_av1_e>(usage, video.amd.amd_usage_av1);
|
||||
}
|
||||
|
||||
bool_f(vars, "amd_preanalysis", (bool &) video.amd.amd_preanalysis);
|
||||
bool_f(vars, "amd_vbaq", (bool &) video.amd.amd_vbaq);
|
||||
bool_f(vars, "amd_enforce_hrd", (bool &) video.amd.amd_enforce_hrd);
|
||||
|
||||
int_f(vars, "vt_coder", video.vt.vt_coder, vt::coder_from_view);
|
||||
int_f(vars, "vt_software", video.vt.vt_allow_sw, vt::allow_software_from_view);
|
||||
@@ -1101,6 +1111,21 @@ namespace config {
|
||||
config::sunshine.flags[config::flag::UPNP].flip();
|
||||
}
|
||||
|
||||
string_restricted_f(vars, "locale", config::sunshine.locale, {
|
||||
"de"sv, // German
|
||||
"en"sv, // English
|
||||
"en_GB"sv, // English (UK)
|
||||
"en_US"sv, // English (US)
|
||||
"es"sv, // Spanish
|
||||
"fr"sv, // French
|
||||
"it"sv, // Italian
|
||||
"ja"sv, // Japanese
|
||||
"pt"sv, // Portuguese
|
||||
"ru"sv, // Russian
|
||||
"sv"sv, // Swedish
|
||||
"zh"sv, // Chinese
|
||||
});
|
||||
|
||||
std::string log_level_string;
|
||||
string_f(vars, "min_log_level", log_level_string);
|
||||
|
||||
@@ -1161,7 +1186,7 @@ namespace config {
|
||||
auto line = argv[x];
|
||||
|
||||
if (line == "--help"sv) {
|
||||
print_help(*argv);
|
||||
logging::print_help(*argv);
|
||||
return 1;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
@@ -1181,7 +1206,7 @@ namespace config {
|
||||
break;
|
||||
}
|
||||
if (apply_flags(line + 1)) {
|
||||
print_help(*argv);
|
||||
logging::print_help(*argv);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1195,7 +1220,7 @@ namespace config {
|
||||
else {
|
||||
TUPLE_EL(var, 1, parse_option(line, line_end));
|
||||
if (!var) {
|
||||
print_help(*argv);
|
||||
logging::print_help(*argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
14
src/config.h
14
src/config.h
@@ -48,15 +48,16 @@ namespace config {
|
||||
} qsv;
|
||||
|
||||
struct {
|
||||
std::optional<int> amd_quality_h264;
|
||||
std::optional<int> amd_quality_hevc;
|
||||
std::optional<int> amd_quality_av1;
|
||||
std::optional<int> amd_rc_h264;
|
||||
std::optional<int> amd_rc_hevc;
|
||||
std::optional<int> amd_rc_av1;
|
||||
std::optional<int> amd_usage_h264;
|
||||
std::optional<int> amd_usage_hevc;
|
||||
std::optional<int> amd_usage_av1;
|
||||
std::optional<int> amd_rc_h264;
|
||||
std::optional<int> amd_rc_hevc;
|
||||
std::optional<int> amd_rc_av1;
|
||||
std::optional<int> amd_enforce_hrd;
|
||||
std::optional<int> amd_quality_h264;
|
||||
std::optional<int> amd_quality_hevc;
|
||||
std::optional<int> amd_quality_av1;
|
||||
std::optional<int> amd_preanalysis;
|
||||
std::optional<int> amd_vbaq;
|
||||
int amd_coder;
|
||||
@@ -160,6 +161,7 @@ namespace config {
|
||||
bool elevated;
|
||||
};
|
||||
struct sunshine_t {
|
||||
std::string locale;
|
||||
int min_log_level;
|
||||
std::bitset<flag::FLAG_SIZE> flags;
|
||||
std::string credentials_file;
|
||||
|
||||
@@ -550,6 +550,24 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
getLocale(resp_https_t response, req_https_t request) {
|
||||
// we need to return the locale whether authenticated or not
|
||||
|
||||
print_req(request);
|
||||
|
||||
pt::ptree outputTree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
|
||||
pt::write_json(data, outputTree);
|
||||
response->write(data.str());
|
||||
});
|
||||
|
||||
outputTree.put("status", "true");
|
||||
outputTree.put("locale", config::sunshine.locale);
|
||||
}
|
||||
|
||||
void
|
||||
saveConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -743,6 +761,7 @@ namespace confighttp {
|
||||
server.resource["^/api/apps$"]["POST"] = saveApp;
|
||||
server.resource["^/api/config$"]["GET"] = getConfig;
|
||||
server.resource["^/api/config$"]["POST"] = saveConfig;
|
||||
server.resource["^/api/configLocale$"]["GET"] = getLocale;
|
||||
server.resource["^/api/restart$"]["POST"] = restart;
|
||||
server.resource["^/api/password$"]["POST"] = savePassword;
|
||||
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
|
||||
|
||||
@@ -17,6 +17,10 @@ namespace crypto {
|
||||
X509_STORE_add_cert(x509_store.get(), cert.get());
|
||||
_certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
|
||||
}
|
||||
void
|
||||
cert_chain_t::clear() {
|
||||
_certs.clear();
|
||||
}
|
||||
|
||||
static int
|
||||
openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
|
||||
|
||||
@@ -73,6 +73,9 @@ namespace crypto {
|
||||
void
|
||||
add(x509_t &&cert);
|
||||
|
||||
void
|
||||
clear();
|
||||
|
||||
const char *
|
||||
verify(x509_t::element_type *cert);
|
||||
|
||||
|
||||
@@ -87,12 +87,12 @@ namespace args {
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* print_help("sunshine", 0, nullptr);
|
||||
* help("sunshine", 0, nullptr);
|
||||
* ```
|
||||
*/
|
||||
int
|
||||
help(const char *name, int argc, char *argv[]) {
|
||||
print_help(name);
|
||||
logging::print_help(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace args {
|
||||
*/
|
||||
int
|
||||
version(const char *name, int argc, char *argv[]) {
|
||||
std::cout << PROJECT_NAME << " version: v" << PROJECT_VER << std::endl;
|
||||
// version was already logged at startup
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -469,15 +469,19 @@ namespace input {
|
||||
* @param input The input context.
|
||||
* @param val The cartesian coordinate pair to convert.
|
||||
* @param size The size of the client's surface containing the value.
|
||||
* @return The host-relative coordinate pair.
|
||||
* @return The host-relative coordinate pair if a touchport is available.
|
||||
*/
|
||||
std::pair<float, float>
|
||||
std::optional<std::pair<float, float>>
|
||||
client_to_touchport(std::shared_ptr<input_t> &input, const std::pair<float, float> &val, const std::pair<float, float> &size) {
|
||||
auto &touch_port_event = input->touch_port_event;
|
||||
auto &touch_port = input->touch_port;
|
||||
if (touch_port_event->peek()) {
|
||||
touch_port = *touch_port_event->pop();
|
||||
}
|
||||
if (!touch_port) {
|
||||
BOOST_LOG(verbose) << "Ignoring early absolute input without a touch port"sv;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto scalarX = touch_port.width / size.first;
|
||||
auto scalarY = touch_port.height / size.second;
|
||||
@@ -491,7 +495,7 @@ namespace input {
|
||||
x = std::clamp(x, offsetX, (size.first * scalarX) - offsetX);
|
||||
y = std::clamp(y, offsetY, (size.second * scalarY) - offsetY);
|
||||
|
||||
return { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv };
|
||||
return std::pair { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,6 +565,9 @@ namespace input {
|
||||
auto height = (float) util::endian::big(packet->height);
|
||||
|
||||
auto tpcoords = client_to_touchport(input, { x, y }, { width, height });
|
||||
if (!tpcoords) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &touch_port = input->touch_port;
|
||||
platf::touch_port_t abs_port {
|
||||
@@ -568,7 +575,7 @@ namespace input {
|
||||
touch_port.env_width, touch_port.env_height
|
||||
};
|
||||
|
||||
platf::abs_mouse(platf_input, abs_port, tpcoords.first, tpcoords.second);
|
||||
platf::abs_mouse(platf_input, abs_port, tpcoords->first, tpcoords->second);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -918,6 +925,9 @@ namespace input {
|
||||
{ from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f,
|
||||
from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f },
|
||||
{ 65535.f, 65535.f });
|
||||
if (!coords) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &touch_port = input->touch_port;
|
||||
platf::touch_port_t abs_port {
|
||||
@@ -926,8 +936,8 @@ namespace input {
|
||||
};
|
||||
|
||||
// Renormalize the coordinates
|
||||
coords.first /= abs_port.width;
|
||||
coords.second /= abs_port.height;
|
||||
coords->first /= abs_port.width;
|
||||
coords->second /= abs_port.height;
|
||||
|
||||
// Normalize rotation value to 0-359 degree range
|
||||
auto rotation = util::endian::little(packet->rotation);
|
||||
@@ -946,8 +956,8 @@ namespace input {
|
||||
packet->eventType,
|
||||
rotation,
|
||||
util::endian::little(packet->pointerId),
|
||||
coords.first,
|
||||
coords.second,
|
||||
coords->first,
|
||||
coords->second,
|
||||
from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f),
|
||||
contact_area.first,
|
||||
contact_area.second,
|
||||
@@ -972,6 +982,9 @@ namespace input {
|
||||
{ from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f,
|
||||
from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f },
|
||||
{ 65535.f, 65535.f });
|
||||
if (!coords) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &touch_port = input->touch_port;
|
||||
platf::touch_port_t abs_port {
|
||||
@@ -980,8 +993,8 @@ namespace input {
|
||||
};
|
||||
|
||||
// Renormalize the coordinates
|
||||
coords.first /= abs_port.width;
|
||||
coords.second /= abs_port.height;
|
||||
coords->first /= abs_port.width;
|
||||
coords->second /= abs_port.height;
|
||||
|
||||
// Normalize rotation value to 0-359 degree range
|
||||
auto rotation = util::endian::little(packet->rotation);
|
||||
@@ -1002,8 +1015,8 @@ namespace input {
|
||||
packet->penButtons,
|
||||
packet->tilt,
|
||||
rotation,
|
||||
coords.first,
|
||||
coords.second,
|
||||
coords->first,
|
||||
coords->second,
|
||||
from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f),
|
||||
contact_area.first,
|
||||
contact_area.second,
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace input {
|
||||
float client_offsetX, client_offsetY;
|
||||
|
||||
float scalar_inv;
|
||||
|
||||
explicit
|
||||
operator bool() const {
|
||||
return width != 0 && height != 0 && env_width != 0 && env_height != 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::pair<float, float>
|
||||
|
||||
225
src/logging.cpp
225
src/logging.cpp
@@ -4,9 +4,11 @@
|
||||
*/
|
||||
|
||||
// standard includes
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
// lib includes
|
||||
#include <boost/core/null_deleter.hpp>
|
||||
#include <boost/log/attributes/clock.hpp>
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
@@ -16,6 +18,10 @@
|
||||
// local includes
|
||||
#include "logging.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/log.h>
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace bl = boost::log;
|
||||
@@ -29,45 +35,182 @@ bl::sources::severity_logger<int> warning(3); // Strange events
|
||||
bl::sources::severity_logger<int> error(4); // Recoverable errors
|
||||
bl::sources::severity_logger<int> fatal(5); // Unrecoverable errors
|
||||
|
||||
/**
|
||||
* @brief Flush the log.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* log_flush();
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
log_flush() {
|
||||
sink->flush();
|
||||
}
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
|
||||
|
||||
/**
|
||||
* @brief Print help to stdout.
|
||||
* @param name The name of the program.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* print_help("sunshine");
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
print_help(const char *name) {
|
||||
std::cout
|
||||
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
|
||||
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
|
||||
<< std::endl
|
||||
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
|
||||
<< std::endl
|
||||
<< " --help | print help"sv << std::endl
|
||||
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
|
||||
<< " --version | print the version of sunshine"sv << std::endl
|
||||
<< std::endl
|
||||
<< " flags"sv << std::endl
|
||||
<< " -0 | Read PIN from stdin"sv << std::endl
|
||||
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
|
||||
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
|
||||
<< " -2 | Force replacement of headers in video stream"sv << std::endl
|
||||
<< " -p | Enable/Disable UPnP"sv << std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
namespace logging {
|
||||
/**
|
||||
* @brief A destructor that restores the initial state.
|
||||
*/
|
||||
deinit_t::~deinit_t() {
|
||||
deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the logging system.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* deinit();
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
deinit() {
|
||||
log_flush();
|
||||
bl::core::get()->remove_sink(sink);
|
||||
sink.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the logging system.
|
||||
* @param min_log_level The minimum log level to output.
|
||||
* @param log_file The log file to write to.
|
||||
* @returns A deinit_t object that will deinitialize the logging system when it goes out of scope.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* log_init(2, "sunshine.log");
|
||||
* ```
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<deinit_t>
|
||||
init(int min_log_level, const std::string &log_file) {
|
||||
if (sink) {
|
||||
// Deinitialize the logging system before reinitializing it. This can probably only ever be hit in tests.
|
||||
deinit();
|
||||
}
|
||||
|
||||
setup_av_logging(min_log_level);
|
||||
|
||||
sink = boost::make_shared<text_sink>();
|
||||
|
||||
boost::shared_ptr<std::ostream> stream { &std::cout, boost::null_deleter() };
|
||||
sink->locked_backend()->add_stream(stream);
|
||||
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(log_file));
|
||||
sink->set_filter(severity >= min_log_level);
|
||||
|
||||
sink->set_formatter([](const bl::record_view &view, bl::formatting_ostream &os) {
|
||||
constexpr const char *message = "Message";
|
||||
constexpr const char *severity = "Severity";
|
||||
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"
|
||||
|
||||
auto log_level = view.attribute_values()[severity].extract<int>().get();
|
||||
|
||||
std::string_view log_type;
|
||||
switch (log_level) {
|
||||
case 0:
|
||||
log_type = "Verbose: "sv;
|
||||
break;
|
||||
case 1:
|
||||
log_type = "Debug: "sv;
|
||||
break;
|
||||
case 2:
|
||||
log_type = "Info: "sv;
|
||||
break;
|
||||
case 3:
|
||||
log_type = "Warning: "sv;
|
||||
break;
|
||||
case 4:
|
||||
log_type = "Error: "sv;
|
||||
break;
|
||||
case 5:
|
||||
log_type = "Fatal: "sv;
|
||||
break;
|
||||
};
|
||||
|
||||
char _date[DATE_BUFFER_SIZE];
|
||||
std::time_t t = std::time(nullptr);
|
||||
strftime(_date, DATE_BUFFER_SIZE, "[%Y:%m:%d:%H:%M:%S]: ", std::localtime(&t));
|
||||
|
||||
os << _date << log_type << view.attribute_values()[message].extract<std::string>();
|
||||
});
|
||||
|
||||
// Flush after each log record to ensure log file contents on disk isn't stale.
|
||||
// This is particularly important when running from a Windows service.
|
||||
sink->locked_backend()->auto_flush(true);
|
||||
|
||||
bl::core::get()->add_sink(sink);
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup AV logging.
|
||||
* @param min_log_level The log level.
|
||||
*/
|
||||
void
|
||||
setup_av_logging(int min_log_level) {
|
||||
if (min_log_level >= 1) {
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
}
|
||||
else {
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
}
|
||||
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
|
||||
static int print_prefix = 1;
|
||||
char buffer[1024];
|
||||
|
||||
av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
|
||||
if (level <= AV_LOG_ERROR) {
|
||||
// We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that
|
||||
// are expected in some cases, such as lack of codec support or similar things.
|
||||
BOOST_LOG(error) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_WARNING) {
|
||||
BOOST_LOG(warning) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_INFO) {
|
||||
BOOST_LOG(info) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_VERBOSE) {
|
||||
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
|
||||
BOOST_LOG(debug) << buffer;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(verbose) << buffer;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Flush the log.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* log_flush();
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
log_flush() {
|
||||
if (sink) {
|
||||
sink->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print help to stdout.
|
||||
* @param name The name of the program.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* print_help("sunshine");
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
print_help(const char *name) {
|
||||
std::cout
|
||||
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
|
||||
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
|
||||
<< std::endl
|
||||
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
|
||||
<< std::endl
|
||||
<< " --help | print help"sv << std::endl
|
||||
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
|
||||
<< " --version | print the version of sunshine"sv << std::endl
|
||||
<< std::endl
|
||||
<< " flags"sv << std::endl
|
||||
<< " -0 | Read PIN from stdin"sv << std::endl
|
||||
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
|
||||
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
|
||||
<< " -2 | Force replacement of headers in video stream"sv << std::endl
|
||||
<< " -p | Enable/Disable UPnP"sv << std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
} // namespace logging
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
|
||||
extern boost::shared_ptr<boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>> sink;
|
||||
using text_sink = boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>;
|
||||
|
||||
extern boost::log::sources::severity_logger<int> verbose;
|
||||
@@ -20,8 +19,20 @@ extern boost::log::sources::severity_logger<int> warning;
|
||||
extern boost::log::sources::severity_logger<int> error;
|
||||
extern boost::log::sources::severity_logger<int> fatal;
|
||||
|
||||
// functions
|
||||
void
|
||||
log_flush();
|
||||
void
|
||||
print_help(const char *name);
|
||||
namespace logging {
|
||||
class deinit_t {
|
||||
public:
|
||||
~deinit_t();
|
||||
};
|
||||
|
||||
void
|
||||
deinit();
|
||||
[[nodiscard]] std::unique_ptr<deinit_t>
|
||||
init(int min_log_level, const std::string &log_file);
|
||||
void
|
||||
setup_av_logging(int min_log_level);
|
||||
void
|
||||
log_flush();
|
||||
void
|
||||
print_help(const char *name);
|
||||
} // namespace logging
|
||||
|
||||
103
src/main.cpp
103
src/main.cpp
@@ -4,13 +4,11 @@
|
||||
*/
|
||||
|
||||
// standard includes
|
||||
#include <codecvt>
|
||||
#include <csignal>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
// lib includes
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
||||
// local includes
|
||||
#include "confighttp.h"
|
||||
#include "entry_handler.h"
|
||||
@@ -26,19 +24,10 @@
|
||||
#include "video.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/log.h>
|
||||
#include <rs.h>
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
namespace bl = boost::log;
|
||||
|
||||
struct NoDelete {
|
||||
void
|
||||
operator()(void *) {}
|
||||
};
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
|
||||
|
||||
std::map<int, std::function<void()>> signal_handlers;
|
||||
void
|
||||
@@ -118,84 +107,10 @@ main(int argc, char *argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (config::sunshine.min_log_level >= 1) {
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
auto log_deinit_guard = logging::init(config::sunshine.min_log_level, config::sunshine.log_file);
|
||||
if (!log_deinit_guard) {
|
||||
BOOST_LOG(error) << "Logging failed to initialize"sv;
|
||||
}
|
||||
else {
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
}
|
||||
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
|
||||
static int print_prefix = 1;
|
||||
char buffer[1024];
|
||||
|
||||
av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
|
||||
if (level <= AV_LOG_ERROR) {
|
||||
// We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that
|
||||
// are expected in some cases, such as lack of codec support or similar things.
|
||||
BOOST_LOG(error) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_WARNING) {
|
||||
BOOST_LOG(warning) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_INFO) {
|
||||
BOOST_LOG(info) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_VERBOSE) {
|
||||
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
|
||||
BOOST_LOG(debug) << buffer;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(verbose) << buffer;
|
||||
}
|
||||
});
|
||||
|
||||
sink = boost::make_shared<text_sink>();
|
||||
|
||||
boost::shared_ptr<std::ostream> stream { &std::cout, NoDelete {} };
|
||||
sink->locked_backend()->add_stream(stream);
|
||||
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(config::sunshine.log_file));
|
||||
sink->set_filter(severity >= config::sunshine.min_log_level);
|
||||
|
||||
sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) {
|
||||
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"
|
||||
|
||||
auto log_level = view.attribute_values()[severity].extract<int>().get();
|
||||
|
||||
std::string_view log_type;
|
||||
switch (log_level) {
|
||||
case 0:
|
||||
log_type = "Verbose: "sv;
|
||||
break;
|
||||
case 1:
|
||||
log_type = "Debug: "sv;
|
||||
break;
|
||||
case 2:
|
||||
log_type = "Info: "sv;
|
||||
break;
|
||||
case 3:
|
||||
log_type = "Warning: "sv;
|
||||
break;
|
||||
case 4:
|
||||
log_type = "Error: "sv;
|
||||
break;
|
||||
case 5:
|
||||
log_type = "Fatal: "sv;
|
||||
break;
|
||||
};
|
||||
|
||||
char _date[DATE_BUFFER_SIZE];
|
||||
std::time_t t = std::time(nullptr);
|
||||
strftime(_date, DATE_BUFFER_SIZE, "[%Y:%m:%d:%H:%M:%S]: ", std::localtime(&t));
|
||||
|
||||
os << _date << log_type << view.attribute_values()[message].extract<std::string>();
|
||||
});
|
||||
|
||||
// Flush after each log record to ensure log file contents on disk isn't stale.
|
||||
// This is particularly important when running from a Windows service.
|
||||
sink->locked_backend()->auto_flush(true);
|
||||
|
||||
bl::core::get()->add_sink(sink);
|
||||
auto fg = util::fail_guard(log_flush);
|
||||
|
||||
// logging can begin at this point
|
||||
// if anything is logged prior to this point, it will appear in stdout, but not in the log viewer in the UI
|
||||
@@ -227,7 +142,7 @@ main(int argc, char *argv[]) {
|
||||
nvprefs_instance.modify_application_profile();
|
||||
// Modify global settings, undo file is produced in the process to restore after improper termination
|
||||
nvprefs_instance.modify_global_profile();
|
||||
// Unload dynamic library to survive driver reinstallation
|
||||
// Unload dynamic library to survive driver re-installation
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
|
||||
@@ -320,7 +235,7 @@ main(int argc, char *argv[]) {
|
||||
|
||||
auto task = []() {
|
||||
BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv;
|
||||
log_flush();
|
||||
logging::log_flush();
|
||||
lifetime::debug_trap();
|
||||
};
|
||||
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
|
||||
@@ -333,7 +248,7 @@ main(int argc, char *argv[]) {
|
||||
|
||||
auto task = []() {
|
||||
BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv;
|
||||
log_flush();
|
||||
logging::log_flush();
|
||||
lifetime::debug_trap();
|
||||
};
|
||||
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
|
||||
@@ -346,8 +261,8 @@ main(int argc, char *argv[]) {
|
||||
// If any of the following fail, we log an error and continue event though sunshine will not function correctly.
|
||||
// This allows access to the UI to fix configuration problems or view the logs.
|
||||
|
||||
auto deinit_guard = platf::init();
|
||||
if (!deinit_guard) {
|
||||
auto platf_deinit_guard = platf::init();
|
||||
if (!platf_deinit_guard) {
|
||||
BOOST_LOG(error) << "Platform failed to initialize"sv;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace nvhttp {
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
crypto::cert_chain_t cert_chain;
|
||||
|
||||
class SunshineHttpsServer: public SimpleWeb::Server<SimpleWeb::HTTPS> {
|
||||
public:
|
||||
SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file):
|
||||
@@ -1017,7 +1019,6 @@ namespace nvhttp {
|
||||
conf_intern.pkey = file_handler::read_file(config::nvhttp.pkey.c_str());
|
||||
conf_intern.servercert = file_handler::read_file(config::nvhttp.cert.c_str());
|
||||
|
||||
crypto::cert_chain_t cert_chain;
|
||||
for (auto &[_, client] : map_id_client) {
|
||||
for (auto &cert : client.certs) {
|
||||
cert_chain.add(crypto::x509(cert));
|
||||
@@ -1026,15 +1027,15 @@ namespace nvhttp {
|
||||
|
||||
auto add_cert = std::make_shared<safe::queue_t<crypto::x509_t>>(30);
|
||||
|
||||
// /resume doesn't always get the parameter "localAudioPlayMode"
|
||||
// /launch will store it in host_audio
|
||||
// resume doesn't always get the parameter "localAudioPlayMode"
|
||||
// launch will store it in host_audio
|
||||
bool host_audio {};
|
||||
|
||||
https_server_t https_server { config::nvhttp.cert, config::nvhttp.pkey };
|
||||
http_server_t http_server;
|
||||
|
||||
// Verify certificates after establishing connection
|
||||
https_server.verify = [&cert_chain, add_cert](SSL *ssl) {
|
||||
https_server.verify = [add_cert](SSL *ssl) {
|
||||
crypto::x509_t x509 { SSL_get_peer_certificate(ssl) };
|
||||
if (!x509) {
|
||||
BOOST_LOG(info) << "unknown -- denied"sv;
|
||||
@@ -1148,6 +1149,7 @@ namespace nvhttp {
|
||||
void
|
||||
erase_all_clients() {
|
||||
map_id_client.clear();
|
||||
cert_chain.clear();
|
||||
save_state();
|
||||
}
|
||||
} // namespace nvhttp
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/stat_trackers.h"
|
||||
#include "src/thread_safe.h"
|
||||
#include "src/utility.h"
|
||||
#include "src/video_colorspace.h"
|
||||
@@ -19,6 +21,8 @@ extern "C" {
|
||||
#include <moonlight-common-c/src/Limelight.h>
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
struct sockaddr;
|
||||
struct AVFrame;
|
||||
struct AVBufferRef;
|
||||
@@ -499,6 +503,22 @@ namespace platf {
|
||||
int env_width, env_height;
|
||||
|
||||
int width, height;
|
||||
|
||||
protected:
|
||||
// collect capture timing data (at loglevel debug)
|
||||
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
|
||||
void
|
||||
log_sleep_overshoot(std::chrono::nanoseconds overshoot_ns) {
|
||||
if (config::sunshine.min_log_level <= 1) {
|
||||
// Print sleep overshoot stats to debug log every 20 seconds
|
||||
auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) {
|
||||
auto f = stat_trackers::one_digit_after_decimal();
|
||||
BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms";
|
||||
};
|
||||
// std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - next_frame;
|
||||
sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class mic_t {
|
||||
|
||||
@@ -800,16 +800,21 @@ namespace cuda {
|
||||
handle.reset();
|
||||
});
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 150ms, *cursor);
|
||||
|
||||
@@ -22,7 +22,9 @@ extern "C" {
|
||||
#define fourcc_mod_code(vendor, val) ((((uint64_t) vendor) << 56) | ((val) &0x00ffffffffffffffULL))
|
||||
#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1))
|
||||
|
||||
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/opengl"
|
||||
#if !defined(SUNSHINE_SHADERS_DIR) // for testing this needs to be defined in cmake as we don't do an install
|
||||
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/opengl"
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
namespace gl {
|
||||
@@ -37,7 +39,7 @@ namespace gl {
|
||||
}
|
||||
|
||||
tex_t::~tex_t() {
|
||||
if (!size() == 0) {
|
||||
if (size() != 0) {
|
||||
ctx.DeleteTextures(size(), begin());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,8 +587,8 @@ namespace platf {
|
||||
weak_strong += data.rumble(tp);
|
||||
}
|
||||
|
||||
std::clamp<std::uint32_t>(weak_strong.first, 0, 0xFFFF);
|
||||
std::clamp<std::uint32_t>(weak_strong.second, 0, 0xFFFF);
|
||||
weak_strong.first = std::clamp<std::uint32_t>(weak_strong.first, 0, 0xFFFF);
|
||||
weak_strong.second = std::clamp<std::uint32_t>(weak_strong.second, 0, 0xFFFF);
|
||||
|
||||
old_rumble = weak_strong * gain / 0xFFFF;
|
||||
return old_rumble;
|
||||
|
||||
@@ -1069,7 +1069,7 @@ namespace platf {
|
||||
}
|
||||
|
||||
inline capture_e
|
||||
refresh(file_t *file, egl::surface_descriptor_t *sd) {
|
||||
refresh(file_t *file, egl::surface_descriptor_t *sd, std::optional<std::chrono::steady_clock::time_point> &frame_timestamp) {
|
||||
// Check for a change in HDR metadata
|
||||
if (connector_id) {
|
||||
auto connector_props = card.connector_props(*connector_id);
|
||||
@@ -1080,6 +1080,7 @@ namespace platf {
|
||||
}
|
||||
|
||||
plane_t plane = drmModeGetPlane(card.fd.el, plane_id);
|
||||
frame_timestamp = std::chrono::steady_clock::now();
|
||||
|
||||
auto fb = card.fb(plane.get());
|
||||
if (!fb) {
|
||||
@@ -1192,17 +1193,22 @@ namespace platf {
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||
@@ -1303,7 +1309,8 @@ namespace platf {
|
||||
|
||||
egl::surface_descriptor_t sd;
|
||||
|
||||
auto status = refresh(fb_fd, &sd);
|
||||
std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
|
||||
auto status = refresh(fb_fd, &sd, frame_timestamp);
|
||||
if (status != capture_e::ok) {
|
||||
return status;
|
||||
}
|
||||
@@ -1330,6 +1337,8 @@ namespace platf {
|
||||
|
||||
gl::ctx.GetTextureSubImage(rgb->tex[0], 0, img_offset_x, img_offset_y, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data);
|
||||
|
||||
img_out->frame_timestamp = frame_timestamp;
|
||||
|
||||
if (cursor && captured_cursor.visible) {
|
||||
blend_cursor(*img_out);
|
||||
}
|
||||
@@ -1408,17 +1417,22 @@ namespace platf {
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||
@@ -1456,7 +1470,7 @@ namespace platf {
|
||||
auto img = (egl::img_descriptor_t *) img_out.get();
|
||||
img->reset();
|
||||
|
||||
auto status = refresh(fb_fd, &img->sd);
|
||||
auto status = refresh(fb_fd, &img->sd, img->frame_timestamp);
|
||||
if (status != capture_e::ok) {
|
||||
return status;
|
||||
}
|
||||
@@ -1671,7 +1685,7 @@ namespace platf {
|
||||
if (!fb->handles[0]) {
|
||||
BOOST_LOG(error) << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Probably not permitted"sv;
|
||||
BOOST_LOG((window_system != window_system_e::X11 || config::video.capture == "kms") ? fatal : error)
|
||||
<< "You must run [sudo setcap cap_sys_admin+p $(readlink -f sunshine)] for KMS display capture to work!"sv;
|
||||
<< "You must run [sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))] for KMS display capture to work!"sv;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
// standard includes
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
// lib includes
|
||||
#include <arpa/inet.h>
|
||||
@@ -98,54 +99,87 @@ namespace platf {
|
||||
return ifaddr_t { p };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs migration if necessary, then returns the appdata directory.
|
||||
* @details This is used for the log directory, so it cannot invoke Boost logging!
|
||||
* @return The path of the appdata directory that should be used.
|
||||
*/
|
||||
fs::path
|
||||
appdata() {
|
||||
bool found = false;
|
||||
bool migrate_config = true;
|
||||
const char *dir;
|
||||
const char *homedir;
|
||||
fs::path config_path;
|
||||
static std::once_flag migration_flag;
|
||||
static fs::path config_path;
|
||||
|
||||
// Get the home directory
|
||||
if ((homedir = getenv("HOME")) == nullptr || strlen(homedir) == 0) {
|
||||
// If HOME is empty or not set, use the current user's home directory
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
}
|
||||
// Ensure migration is only attempted once
|
||||
std::call_once(migration_flag, []() {
|
||||
bool found = false;
|
||||
bool migrate_config = true;
|
||||
const char *dir;
|
||||
const char *homedir;
|
||||
const char *migrate_envvar;
|
||||
|
||||
// May be set if running under a systemd service with the ConfigurationDirectory= option set.
|
||||
if ((dir = getenv("CONFIGURATION_DIRECTORY")) != nullptr && strlen(dir) > 0) {
|
||||
found = true;
|
||||
config_path = fs::path(dir) / "sunshine"sv;
|
||||
}
|
||||
// Otherwise, follow the XDG base directory specification:
|
||||
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
if (!found && (dir = getenv("XDG_CONFIG_HOME")) != nullptr && strlen(dir) > 0) {
|
||||
found = true;
|
||||
config_path = fs::path(dir) / "sunshine"sv;
|
||||
}
|
||||
// As a last resort, use the home directory
|
||||
if (!found) {
|
||||
migrate_config = false;
|
||||
config_path = fs::path(homedir) / ".config/sunshine"sv;
|
||||
}
|
||||
// Get the home directory
|
||||
if ((homedir = getenv("HOME")) == nullptr || strlen(homedir) == 0) {
|
||||
// If HOME is empty or not set, use the current user's home directory
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
}
|
||||
|
||||
// migrate from the old config location if necessary
|
||||
if (migrate_config && found && getenv("SUNSHINE_MIGRATE_CONFIG") == "1"sv) {
|
||||
fs::path old_config_path = fs::path(homedir) / ".config/sunshine"sv;
|
||||
if (old_config_path != config_path && fs::exists(old_config_path)) {
|
||||
if (!fs::exists(config_path)) {
|
||||
BOOST_LOG(info) << "Migrating config from "sv << old_config_path << " to "sv << config_path;
|
||||
std::error_code ec;
|
||||
fs::rename(old_config_path, config_path, ec);
|
||||
if (ec) {
|
||||
return old_config_path;
|
||||
// May be set if running under a systemd service with the ConfigurationDirectory= option set.
|
||||
if ((dir = getenv("CONFIGURATION_DIRECTORY")) != nullptr && strlen(dir) > 0) {
|
||||
found = true;
|
||||
config_path = fs::path(dir) / "sunshine"sv;
|
||||
}
|
||||
// Otherwise, follow the XDG base directory specification:
|
||||
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
if (!found && (dir = getenv("XDG_CONFIG_HOME")) != nullptr && strlen(dir) > 0) {
|
||||
found = true;
|
||||
config_path = fs::path(dir) / "sunshine"sv;
|
||||
}
|
||||
// As a last resort, use the home directory
|
||||
if (!found) {
|
||||
migrate_config = false;
|
||||
config_path = fs::path(homedir) / ".config/sunshine"sv;
|
||||
}
|
||||
|
||||
// migrate from the old config location if necessary
|
||||
migrate_envvar = getenv("SUNSHINE_MIGRATE_CONFIG");
|
||||
if (migrate_config && found && migrate_envvar && strcmp(migrate_envvar, "1") == 0) {
|
||||
std::error_code ec;
|
||||
fs::path old_config_path = fs::path(homedir) / ".config/sunshine"sv;
|
||||
if (old_config_path != config_path && fs::exists(old_config_path, ec)) {
|
||||
if (!fs::exists(config_path, ec)) {
|
||||
std::cout << "Migrating config from "sv << old_config_path << " to "sv << config_path << std::endl;
|
||||
if (!ec) {
|
||||
// Create the new directory tree if it doesn't already exist
|
||||
fs::create_directories(config_path, ec);
|
||||
}
|
||||
if (!ec) {
|
||||
// Copy the old directory into the new location
|
||||
// NB: We use a copy instead of a move so that cross-volume migrations work
|
||||
fs::copy(old_config_path, config_path, fs::copy_options::recursive | fs::copy_options::copy_symlinks, ec);
|
||||
}
|
||||
if (!ec) {
|
||||
// If the copy was successful, delete the original directory
|
||||
fs::remove_all(old_config_path, ec);
|
||||
if (ec) {
|
||||
std::cerr << "Failed to clean up old config directory: " << ec.message() << std::endl;
|
||||
|
||||
// This is not fatal. Next time we start, we'll warn the user to delete the old one.
|
||||
ec.clear();
|
||||
}
|
||||
}
|
||||
if (ec) {
|
||||
std::cerr << "Migration failed: " << ec.message() << std::endl;
|
||||
config_path = old_config_path;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We cannot use Boost logging because it hasn't been initialized yet!
|
||||
std::cerr << "Config exists in both "sv << old_config_path << " and "sv << config_path << ". Using "sv << config_path << " for config" << std::endl;
|
||||
std::cerr << "It is recommended to remove "sv << old_config_path << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Config exists in both "sv << old_config_path << " and "sv << config_path << ", using "sv << config_path << "... it is recommended to remove "sv << old_config_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return config_path;
|
||||
}
|
||||
|
||||
@@ -129,16 +129,22 @@ namespace wl {
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||
@@ -259,16 +265,22 @@ namespace wl {
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||
|
||||
@@ -481,17 +481,22 @@ namespace platf {
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||
@@ -535,6 +540,7 @@ namespace platf {
|
||||
auto img = (x11_img_t *) img_out.get();
|
||||
|
||||
XImage *x_img { x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) };
|
||||
img->frame_timestamp = std::chrono::steady_clock::now();
|
||||
|
||||
img->width = x_img->width;
|
||||
img->height = x_img->height;
|
||||
@@ -621,17 +627,22 @@ namespace platf {
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
std::this_thread::sleep_for(next_frame - now);
|
||||
}
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds overshoot_ns = now - next_frame;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
next_frame += delay;
|
||||
if (next_frame < now) { // some major slowdown happened; we couldn't keep up
|
||||
next_frame = now + delay;
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_out;
|
||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace platf::dxgi {
|
||||
SetThreadExecutionState(ES_CONTINUOUS);
|
||||
});
|
||||
|
||||
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
|
||||
sleep_overshoot_tracker.reset();
|
||||
|
||||
while (true) {
|
||||
// This will return false if the HDR state changes or for any number of other
|
||||
@@ -184,16 +184,8 @@ namespace platf::dxgi {
|
||||
}
|
||||
else {
|
||||
high_precision_sleep(sleep_period);
|
||||
|
||||
if (config::sunshine.min_log_level <= 1) {
|
||||
// Print sleep overshoot stats to debug log every 20 seconds
|
||||
auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) {
|
||||
auto f = stat_trackers::one_digit_after_decimal();
|
||||
BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms";
|
||||
};
|
||||
std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
|
||||
sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
|
||||
}
|
||||
std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
|
||||
log_sleep_overshoot(overshoot_ns);
|
||||
|
||||
status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor);
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ extern "C" {
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx"
|
||||
#if !defined(SUNSHINE_SHADERS_DIR) // for testing this needs to be defined in cmake as we don't do an install
|
||||
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx"
|
||||
#endif
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
}
|
||||
|
||||
@@ -1814,7 +1814,7 @@ namespace stream {
|
||||
// The alternative is that Sunshine can never start another session until it's manually restarted.
|
||||
auto task = []() {
|
||||
BOOST_LOG(fatal) << "Hang detected! Session failed to terminate in 10 seconds."sv;
|
||||
log_flush();
|
||||
logging::log_flush();
|
||||
lifetime::debug_trap();
|
||||
};
|
||||
auto force_kill = task_pool.pushDelayed(task, 10s).task_id;
|
||||
|
||||
160
src/video.cpp
160
src/video.cpp
@@ -14,7 +14,6 @@ extern "C" {
|
||||
#include <libavutil/mastering_display_metadata.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include "cbs.h"
|
||||
@@ -51,12 +50,6 @@ namespace video {
|
||||
av_buffer_unref(&ref);
|
||||
}
|
||||
|
||||
using avcodec_ctx_t = util::safe_ptr<AVCodecContext, free_ctx>;
|
||||
using avcodec_frame_t = util::safe_ptr<AVFrame, free_frame>;
|
||||
using avcodec_buffer_t = util::safe_ptr<AVBufferRef, free_buffer>;
|
||||
using sws_t = util::safe_ptr<SwsContext, sws_freeContext>;
|
||||
using img_event_t = std::shared_ptr<safe::event_t<std::shared_ptr<platf::img_t>>>;
|
||||
|
||||
namespace nv {
|
||||
|
||||
enum class profile_h264_e : int {
|
||||
@@ -87,11 +80,6 @@ namespace video {
|
||||
};
|
||||
} // namespace qsv
|
||||
|
||||
platf::mem_type_e
|
||||
map_base_dev_type(AVHWDeviceType type);
|
||||
platf::pix_fmt_e
|
||||
map_pix_fmt(AVPixelFormat fmt);
|
||||
|
||||
util::Either<avcodec_buffer_t, int>
|
||||
dxgi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *);
|
||||
util::Either<avcodec_buffer_t, int>
|
||||
@@ -288,137 +276,6 @@ namespace video {
|
||||
ALWAYS_REPROBE = 1 << 9, // This is an encoder of last resort and we want to aggressively probe for a better one
|
||||
};
|
||||
|
||||
struct encoder_platform_formats_t {
|
||||
virtual ~encoder_platform_formats_t() = default;
|
||||
platf::mem_type_e dev_type;
|
||||
platf::pix_fmt_e pix_fmt_8bit, pix_fmt_10bit;
|
||||
};
|
||||
|
||||
struct encoder_platform_formats_avcodec: encoder_platform_formats_t {
|
||||
using init_buffer_function_t = std::function<util::Either<avcodec_buffer_t, int>(platf::avcodec_encode_device_t *)>;
|
||||
|
||||
encoder_platform_formats_avcodec(
|
||||
const AVHWDeviceType &avcodec_base_dev_type,
|
||||
const AVHWDeviceType &avcodec_derived_dev_type,
|
||||
const AVPixelFormat &avcodec_dev_pix_fmt,
|
||||
const AVPixelFormat &avcodec_pix_fmt_8bit,
|
||||
const AVPixelFormat &avcodec_pix_fmt_10bit,
|
||||
const init_buffer_function_t &init_avcodec_hardware_input_buffer_function):
|
||||
avcodec_base_dev_type { avcodec_base_dev_type },
|
||||
avcodec_derived_dev_type { avcodec_derived_dev_type },
|
||||
avcodec_dev_pix_fmt { avcodec_dev_pix_fmt },
|
||||
avcodec_pix_fmt_8bit { avcodec_pix_fmt_8bit },
|
||||
avcodec_pix_fmt_10bit { avcodec_pix_fmt_10bit },
|
||||
init_avcodec_hardware_input_buffer { init_avcodec_hardware_input_buffer_function } {
|
||||
dev_type = map_base_dev_type(avcodec_base_dev_type);
|
||||
pix_fmt_8bit = map_pix_fmt(avcodec_pix_fmt_8bit);
|
||||
pix_fmt_10bit = map_pix_fmt(avcodec_pix_fmt_10bit);
|
||||
}
|
||||
|
||||
AVHWDeviceType avcodec_base_dev_type, avcodec_derived_dev_type;
|
||||
AVPixelFormat avcodec_dev_pix_fmt;
|
||||
AVPixelFormat avcodec_pix_fmt_8bit, avcodec_pix_fmt_10bit;
|
||||
|
||||
init_buffer_function_t init_avcodec_hardware_input_buffer;
|
||||
};
|
||||
|
||||
struct encoder_platform_formats_nvenc: encoder_platform_formats_t {
|
||||
encoder_platform_formats_nvenc(
|
||||
const platf::mem_type_e &dev_type,
|
||||
const platf::pix_fmt_e &pix_fmt_8bit,
|
||||
const platf::pix_fmt_e &pix_fmt_10bit) {
|
||||
encoder_platform_formats_t::dev_type = dev_type;
|
||||
encoder_platform_formats_t::pix_fmt_8bit = pix_fmt_8bit;
|
||||
encoder_platform_formats_t::pix_fmt_10bit = pix_fmt_10bit;
|
||||
}
|
||||
};
|
||||
|
||||
struct encoder_t {
|
||||
std::string_view name;
|
||||
enum flag_e {
|
||||
PASSED, // Is supported
|
||||
REF_FRAMES_RESTRICT, // Set maximum reference frames
|
||||
CBR, // Some encoders don't support CBR, if not supported --> attempt constant quantatication parameter instead
|
||||
DYNAMIC_RANGE, // hdr
|
||||
VUI_PARAMETERS, // AMD encoder with VAAPI doesn't add VUI parameters to SPS
|
||||
MAX_FLAGS
|
||||
};
|
||||
|
||||
static std::string_view
|
||||
from_flag(flag_e flag) {
|
||||
#define _CONVERT(x) \
|
||||
case flag_e::x: \
|
||||
return #x##sv
|
||||
switch (flag) {
|
||||
_CONVERT(PASSED);
|
||||
_CONVERT(REF_FRAMES_RESTRICT);
|
||||
_CONVERT(CBR);
|
||||
_CONVERT(DYNAMIC_RANGE);
|
||||
_CONVERT(VUI_PARAMETERS);
|
||||
_CONVERT(MAX_FLAGS);
|
||||
}
|
||||
#undef _CONVERT
|
||||
|
||||
return "unknown"sv;
|
||||
}
|
||||
|
||||
struct option_t {
|
||||
KITTY_DEFAULT_CONSTR_MOVE(option_t)
|
||||
option_t(const option_t &) = default;
|
||||
|
||||
std::string name;
|
||||
std::variant<int, int *, std::optional<int> *, std::function<int()>, std::string, std::string *> value;
|
||||
|
||||
option_t(std::string &&name, decltype(value) &&value):
|
||||
name { std::move(name) }, value { std::move(value) } {}
|
||||
};
|
||||
|
||||
const std::unique_ptr<const encoder_platform_formats_t> platform_formats;
|
||||
|
||||
struct {
|
||||
std::vector<option_t> common_options;
|
||||
std::vector<option_t> sdr_options;
|
||||
std::vector<option_t> hdr_options;
|
||||
std::vector<option_t> fallback_options;
|
||||
|
||||
// QP option to set in the case that CBR/VBR is not supported
|
||||
// by the encoder. If CBR/VBR is guaranteed to be supported,
|
||||
// don't specify this option to avoid wasteful encoder probing.
|
||||
std::optional<option_t> qp;
|
||||
|
||||
std::string name;
|
||||
std::bitset<MAX_FLAGS> capabilities;
|
||||
|
||||
bool
|
||||
operator[](flag_e flag) const {
|
||||
return capabilities[(std::size_t) flag];
|
||||
}
|
||||
|
||||
std::bitset<MAX_FLAGS>::reference
|
||||
operator[](flag_e flag) {
|
||||
return capabilities[(std::size_t) flag];
|
||||
}
|
||||
} av1, hevc, h264;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct encode_session_t {
|
||||
virtual ~encode_session_t() = default;
|
||||
|
||||
virtual int
|
||||
convert(platf::img_t &img) = 0;
|
||||
|
||||
virtual void
|
||||
request_idr_frame() = 0;
|
||||
|
||||
virtual void
|
||||
request_normal_frame() = 0;
|
||||
|
||||
virtual void
|
||||
invalidate_ref_frames(int64_t first_frame, int64_t last_frame) = 0;
|
||||
};
|
||||
|
||||
class avcodec_encode_session_t: public encode_session_t {
|
||||
public:
|
||||
avcodec_encode_session_t() = default;
|
||||
@@ -586,7 +443,7 @@ namespace video {
|
||||
auto capture_thread_sync = safe::make_shared<capture_thread_sync_ctx_t>(start_capture_sync, end_capture_sync);
|
||||
|
||||
#ifdef _WIN32
|
||||
static encoder_t nvenc {
|
||||
encoder_t nvenc {
|
||||
"nvenc"sv,
|
||||
std::make_unique<encoder_platform_formats_nvenc>(
|
||||
platf::mem_type_e::dxgi,
|
||||
@@ -630,7 +487,7 @@ namespace video {
|
||||
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION // flags
|
||||
};
|
||||
#elif !defined(__APPLE__)
|
||||
static encoder_t nvenc {
|
||||
encoder_t nvenc {
|
||||
"nvenc"sv,
|
||||
std::make_unique<encoder_platform_formats_avcodec>(
|
||||
#ifdef _WIN32
|
||||
@@ -718,7 +575,7 @@ namespace video {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static encoder_t quicksync {
|
||||
encoder_t quicksync {
|
||||
"quicksync"sv,
|
||||
std::make_unique<encoder_platform_formats_avcodec>(
|
||||
AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_QSV,
|
||||
@@ -799,7 +656,7 @@ namespace video {
|
||||
PARALLEL_ENCODING | CBR_WITH_VBR | RELAXED_COMPLIANCE | NO_RC_BUF_LIMIT
|
||||
};
|
||||
|
||||
static encoder_t amdvce {
|
||||
encoder_t amdvce {
|
||||
"amdvce"sv,
|
||||
std::make_unique<encoder_platform_formats_avcodec>(
|
||||
AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_NONE,
|
||||
@@ -815,6 +672,7 @@ namespace video {
|
||||
{ "quality"s, &config::video.amd.amd_quality_av1 },
|
||||
{ "rc"s, &config::video.amd.amd_rc_av1 },
|
||||
{ "usage"s, &config::video.amd.amd_usage_av1 },
|
||||
{ "enforce_hrd"s, &config::video.amd.amd_enforce_hrd },
|
||||
},
|
||||
{}, // SDR-specific options
|
||||
{}, // HDR-specific options
|
||||
@@ -836,6 +694,7 @@ namespace video {
|
||||
{ "rc"s, &config::video.amd.amd_rc_hevc },
|
||||
{ "usage"s, &config::video.amd.amd_usage_hevc },
|
||||
{ "vbaq"s, &config::video.amd.amd_vbaq },
|
||||
{ "enforce_hrd"s, &config::video.amd.amd_enforce_hrd },
|
||||
},
|
||||
{}, // SDR-specific options
|
||||
{}, // HDR-specific options
|
||||
@@ -855,6 +714,7 @@ namespace video {
|
||||
{ "rc"s, &config::video.amd.amd_rc_h264 },
|
||||
{ "usage"s, &config::video.amd.amd_usage_h264 },
|
||||
{ "vbaq"s, &config::video.amd.amd_vbaq },
|
||||
{ "enforce_hrd"s, &config::video.amd.amd_enforce_hrd },
|
||||
},
|
||||
// SDR-specific options
|
||||
{},
|
||||
@@ -871,7 +731,7 @@ namespace video {
|
||||
};
|
||||
#endif
|
||||
|
||||
static encoder_t software {
|
||||
encoder_t software {
|
||||
"software"sv,
|
||||
std::make_unique<encoder_platform_formats_avcodec>(
|
||||
AV_HWDEVICE_TYPE_NONE, AV_HWDEVICE_TYPE_NONE,
|
||||
@@ -936,7 +796,7 @@ namespace video {
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
static encoder_t vaapi {
|
||||
encoder_t vaapi {
|
||||
"vaapi"sv,
|
||||
std::make_unique<encoder_platform_formats_avcodec>(
|
||||
AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_NONE,
|
||||
@@ -1004,7 +864,7 @@ namespace video {
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
static encoder_t videotoolbox {
|
||||
encoder_t videotoolbox {
|
||||
"videotoolbox"sv,
|
||||
std::make_unique<encoder_platform_formats_avcodec>(
|
||||
AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_HWDEVICE_TYPE_NONE,
|
||||
|
||||
172
src/video.h
172
src/video.h
@@ -11,11 +11,181 @@
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
struct AVPacket;
|
||||
namespace video {
|
||||
|
||||
platf::mem_type_e
|
||||
map_base_dev_type(AVHWDeviceType type);
|
||||
platf::pix_fmt_e
|
||||
map_pix_fmt(AVPixelFormat fmt);
|
||||
|
||||
void
|
||||
free_ctx(AVCodecContext *ctx);
|
||||
void
|
||||
free_frame(AVFrame *frame);
|
||||
void
|
||||
free_buffer(AVBufferRef *ref);
|
||||
|
||||
using avcodec_ctx_t = util::safe_ptr<AVCodecContext, free_ctx>;
|
||||
using avcodec_frame_t = util::safe_ptr<AVFrame, free_frame>;
|
||||
using avcodec_buffer_t = util::safe_ptr<AVBufferRef, free_buffer>;
|
||||
using sws_t = util::safe_ptr<SwsContext, sws_freeContext>;
|
||||
using img_event_t = std::shared_ptr<safe::event_t<std::shared_ptr<platf::img_t>>>;
|
||||
|
||||
struct encoder_platform_formats_t {
|
||||
virtual ~encoder_platform_formats_t() = default;
|
||||
platf::mem_type_e dev_type;
|
||||
platf::pix_fmt_e pix_fmt_8bit, pix_fmt_10bit;
|
||||
};
|
||||
|
||||
struct encoder_platform_formats_avcodec: encoder_platform_formats_t {
|
||||
using init_buffer_function_t = std::function<util::Either<avcodec_buffer_t, int>(platf::avcodec_encode_device_t *)>;
|
||||
|
||||
encoder_platform_formats_avcodec(
|
||||
const AVHWDeviceType &avcodec_base_dev_type,
|
||||
const AVHWDeviceType &avcodec_derived_dev_type,
|
||||
const AVPixelFormat &avcodec_dev_pix_fmt,
|
||||
const AVPixelFormat &avcodec_pix_fmt_8bit,
|
||||
const AVPixelFormat &avcodec_pix_fmt_10bit,
|
||||
const init_buffer_function_t &init_avcodec_hardware_input_buffer_function):
|
||||
avcodec_base_dev_type { avcodec_base_dev_type },
|
||||
avcodec_derived_dev_type { avcodec_derived_dev_type },
|
||||
avcodec_dev_pix_fmt { avcodec_dev_pix_fmt },
|
||||
avcodec_pix_fmt_8bit { avcodec_pix_fmt_8bit },
|
||||
avcodec_pix_fmt_10bit { avcodec_pix_fmt_10bit },
|
||||
init_avcodec_hardware_input_buffer { init_avcodec_hardware_input_buffer_function } {
|
||||
dev_type = map_base_dev_type(avcodec_base_dev_type);
|
||||
pix_fmt_8bit = map_pix_fmt(avcodec_pix_fmt_8bit);
|
||||
pix_fmt_10bit = map_pix_fmt(avcodec_pix_fmt_10bit);
|
||||
}
|
||||
|
||||
AVHWDeviceType avcodec_base_dev_type, avcodec_derived_dev_type;
|
||||
AVPixelFormat avcodec_dev_pix_fmt;
|
||||
AVPixelFormat avcodec_pix_fmt_8bit, avcodec_pix_fmt_10bit;
|
||||
|
||||
init_buffer_function_t init_avcodec_hardware_input_buffer;
|
||||
};
|
||||
|
||||
struct encoder_platform_formats_nvenc: encoder_platform_formats_t {
|
||||
encoder_platform_formats_nvenc(
|
||||
const platf::mem_type_e &dev_type,
|
||||
const platf::pix_fmt_e &pix_fmt_8bit,
|
||||
const platf::pix_fmt_e &pix_fmt_10bit) {
|
||||
encoder_platform_formats_t::dev_type = dev_type;
|
||||
encoder_platform_formats_t::pix_fmt_8bit = pix_fmt_8bit;
|
||||
encoder_platform_formats_t::pix_fmt_10bit = pix_fmt_10bit;
|
||||
}
|
||||
};
|
||||
|
||||
struct encoder_t {
|
||||
std::string_view name;
|
||||
enum flag_e {
|
||||
PASSED, // Is supported
|
||||
REF_FRAMES_RESTRICT, // Set maximum reference frames
|
||||
CBR, // Some encoders don't support CBR, if not supported --> attempt constant quantatication parameter instead
|
||||
DYNAMIC_RANGE, // hdr
|
||||
VUI_PARAMETERS, // AMD encoder with VAAPI doesn't add VUI parameters to SPS
|
||||
MAX_FLAGS
|
||||
};
|
||||
|
||||
static std::string_view
|
||||
from_flag(flag_e flag) {
|
||||
#define _CONVERT(x) \
|
||||
case flag_e::x: \
|
||||
return std::string_view(#x)
|
||||
switch (flag) {
|
||||
_CONVERT(PASSED);
|
||||
_CONVERT(REF_FRAMES_RESTRICT);
|
||||
_CONVERT(CBR);
|
||||
_CONVERT(DYNAMIC_RANGE);
|
||||
_CONVERT(VUI_PARAMETERS);
|
||||
_CONVERT(MAX_FLAGS);
|
||||
}
|
||||
#undef _CONVERT
|
||||
|
||||
return { "unknown" };
|
||||
}
|
||||
|
||||
struct option_t {
|
||||
KITTY_DEFAULT_CONSTR_MOVE(option_t)
|
||||
option_t(const option_t &) = default;
|
||||
|
||||
std::string name;
|
||||
std::variant<int, int *, std::optional<int> *, std::function<int()>, std::string, std::string *> value;
|
||||
|
||||
option_t(std::string &&name, decltype(value) &&value):
|
||||
name { std::move(name) }, value { std::move(value) } {}
|
||||
};
|
||||
|
||||
const std::unique_ptr<const encoder_platform_formats_t> platform_formats;
|
||||
|
||||
struct codec_t {
|
||||
std::vector<option_t> common_options;
|
||||
std::vector<option_t> sdr_options;
|
||||
std::vector<option_t> hdr_options;
|
||||
std::vector<option_t> fallback_options;
|
||||
|
||||
// QP option to set in the case that CBR/VBR is not supported
|
||||
// by the encoder. If CBR/VBR is guaranteed to be supported,
|
||||
// don't specify this option to avoid wasteful encoder probing.
|
||||
std::optional<option_t> qp;
|
||||
|
||||
std::string name;
|
||||
std::bitset<MAX_FLAGS> capabilities;
|
||||
|
||||
bool
|
||||
operator[](flag_e flag) const {
|
||||
return capabilities[(std::size_t) flag];
|
||||
}
|
||||
|
||||
std::bitset<MAX_FLAGS>::reference
|
||||
operator[](flag_e flag) {
|
||||
return capabilities[(std::size_t) flag];
|
||||
}
|
||||
} av1, hevc, h264;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct encode_session_t {
|
||||
virtual ~encode_session_t() = default;
|
||||
|
||||
virtual int
|
||||
convert(platf::img_t &img) = 0;
|
||||
|
||||
virtual void
|
||||
request_idr_frame() = 0;
|
||||
|
||||
virtual void
|
||||
request_normal_frame() = 0;
|
||||
|
||||
virtual void
|
||||
invalidate_ref_frames(int64_t first_frame, int64_t last_frame) = 0;
|
||||
};
|
||||
|
||||
// encoders
|
||||
extern encoder_t software;
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
extern encoder_t nvenc; // available for windows and linux
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
extern encoder_t amdvce;
|
||||
extern encoder_t quicksync;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
extern encoder_t vaapi;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
extern encoder_t videotoolbox;
|
||||
#endif
|
||||
|
||||
struct packet_raw_t {
|
||||
virtual ~packet_raw_t() = default;
|
||||
|
||||
@@ -154,6 +324,8 @@ namespace video {
|
||||
config_t config,
|
||||
void *channel_data);
|
||||
|
||||
bool
|
||||
validate_encoder(encoder_t &encoder, bool expect_failure);
|
||||
int
|
||||
probe_encoders();
|
||||
} // namespace video
|
||||
|
||||
@@ -11,22 +11,22 @@
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="fas fa-fw fa-home"></i> Home</a>
|
||||
<a class="nav-link" href="/"><i class="fas fa-fw fa-home"></i> {{ $t('navbar.home') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/pin"><i class="fas fa-fw fa-unlock"></i> PIN</a>
|
||||
<a class="nav-link" href="/pin"><i class="fas fa-fw fa-unlock"></i> {{ $t('navbar.pin') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/apps"><i class="fas fa-fw fa-stream"></i> Applications</a>
|
||||
<a class="nav-link" href="/apps"><i class="fas fa-fw fa-stream"></i> {{ $t('navbar.applications') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/config"><i class="fas fa-fw fa-cog"></i> Configuration</a>
|
||||
<a class="nav-link" href="/config"><i class="fas fa-fw fa-cog"></i> {{ $t('navbar.configuration') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/password"><i class="fas fa-fw fa-user-shield"></i> Change Password</a>
|
||||
<a class="nav-link" href="/password"><i class="fas fa-fw fa-user-shield"></i> {{ $t('navbar.password') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/troubleshooting"><i class="fas fa-fw fa-info"></i> Troubleshooting</a>
|
||||
<a class="nav-link" href="/troubleshooting"><i class="fas fa-fw fa-info"></i> {{ $t('navbar.troubleshoot') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
<template>
|
||||
<div class="card p-2">
|
||||
<div class="card-body">
|
||||
<h2>Resources</h2>
|
||||
<h2>{{ $t('resource_card.resources') }}</h2>
|
||||
<br>
|
||||
<p>
|
||||
Resources for Sunshine!
|
||||
</p>
|
||||
<p>{{ $t('resource_card.resources_desc') }}</p>
|
||||
<div class="card-group p-4 align-items-center">
|
||||
<a class="btn btn-success m-1" href="https://app.lizardbyte.dev" target="_blank">LizardByte Website</a>
|
||||
<a class="btn btn-success m-1" href="https://app.lizardbyte.dev" target="_blank">
|
||||
{{ $t('resource_card.lizardbyte_website') }}</a>
|
||||
<a class="btn btn-primary m-1" href="https://app.lizardbyte.dev/discord" target="_blank">
|
||||
<i class="fab fa-fw fa-discord"></i> Discord</a>
|
||||
<a class="btn btn-secondary m-1" href="https://github.com/LizardByte/Sunshine/discussions" target="_blank">
|
||||
<i class="fab fa-fw fa-github"></i> Github Discussions</a>
|
||||
<i class="fab fa-fw fa-github"></i> {{ $t('resource_card.github_discussions') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Legal -->
|
||||
<div class="card p-2 mt-4">
|
||||
<div class="card-body">
|
||||
<h2>Legal</h2>
|
||||
<h2>{{ $t('resource_card.legal') }}</h2>
|
||||
<br>
|
||||
<p>
|
||||
By continuing to use this software you agree to the terms and conditions in the following documents.
|
||||
</p>
|
||||
<p>{{ $t('resource_card.legal_desc') }}</p>
|
||||
<div class="card-group p-4 align-items-center">
|
||||
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/LICENSE"
|
||||
target="_blank">
|
||||
<i class="fas fa-fw fa-file-alt"></i> License</a>
|
||||
<i class="fas fa-fw fa-file-alt"></i> {{ $t('resource_card.license') }}</a>
|
||||
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/NOTICE"
|
||||
target="_blank">
|
||||
<i class="fas fa-fw fa-exclamation"></i> Third Party Notice</a>
|
||||
<i class="fas fa-fw fa-exclamation"></i> {{ $t('resource_card.third_party_notice') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -70,19 +70,19 @@
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body id="app">
|
||||
<body id="app" v-cloak>
|
||||
<Navbar></Navbar>
|
||||
<div class="container">
|
||||
<div class="my-4">
|
||||
<h1>Applications</h1>
|
||||
<div>Applications are refreshed only when Client is restarted</div>
|
||||
<h1>{{ $t('apps.applications_title') }}</h1>
|
||||
<div>{{ $t('apps.applications_desc') }}</div>
|
||||
</div>
|
||||
<div class="card p-4">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Actions</th>
|
||||
<th scope="col">{{ $t('apps.name') }}</th>
|
||||
<th scope="col">{{ $t('apps.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -90,10 +90,10 @@
|
||||
<td>{{app.name}}</td>
|
||||
<td>
|
||||
<button class="btn btn-primary mx-1" @click="editApp(i)">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
<i class="fas fa-edit"></i> {{ $t('apps.edit') }}
|
||||
</button>
|
||||
<button class="btn btn-danger mx-1" @click="showDeleteForm(i)">
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
<i class="fas fa-trash"></i> {{ $t('apps.delete') }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -104,53 +104,42 @@
|
||||
<div class="p-4">
|
||||
<!-- Application Name -->
|
||||
<div class="mb-3">
|
||||
<label for="appName" class="form-label">Application Name</label>
|
||||
<label for="appName" class="form-label">{{ $t('apps.app_name') }}</label>
|
||||
<input type="text" class="form-control" id="appName" aria-describedby="appNameHelp" v-model="editForm.name" />
|
||||
<div id="appNameHelp" class="form-text">
|
||||
Application Name, as shown on Moonlight
|
||||
</div>
|
||||
<div id="appNameHelp" class="form-text">{{ $t('apps.app_name_desc') }}</div>
|
||||
</div>
|
||||
<!-- output -->
|
||||
<div class="mb-3">
|
||||
<label for="appOutput" class="form-label">Output</label>
|
||||
<label for="appOutput" class="form-label">{{ $t('apps.output_name') }}</label>
|
||||
<input type="text" class="form-control monospace" id="appOutput" aria-describedby="appOutputHelp"
|
||||
v-model="editForm.output" />
|
||||
<div id="appOutputHelp" class="form-text">
|
||||
The file where the output of the command is stored, if it is not
|
||||
specified, the output is ignored
|
||||
</div>
|
||||
<div id="appOutputHelp" class="form-text">{{ $t('apps.output_desc') }}</div>
|
||||
</div>
|
||||
<!-- prep-cmd -->
|
||||
<div class="mb-3">
|
||||
<label for="excludeGlobalPrep" class="form-label">Global Prep Commands</label>
|
||||
<label for="excludeGlobalPrep" class="form-label">{{ $t('apps.global_prep_name') }}</label>
|
||||
<select id="excludeGlobalPrep" class="form-select" v-model="editForm['exclude-global-prep-cmd']">
|
||||
<option v-for="val in [false, true]" :value="val">
|
||||
{{ !val ? 'Enabled' : 'Disabled' }}
|
||||
{{ !val ? $t('_common.enabled') : $t('_common.disabled') }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
Enable/Disable the execution of Global Prep Commands for this
|
||||
application.
|
||||
</div>
|
||||
<div class="form-text">{{ $t('apps.global_prep_desc') }}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="appName" class="form-label">Command Preparations</label>
|
||||
<div class="form-text">
|
||||
A list of commands to be run before/after this application.<br>
|
||||
If any of the prep-commands fail, starting the application is aborted.
|
||||
</div>
|
||||
<label for="appName" class="form-label">{{ $t('apps.cmd_prep_name') }}</label>
|
||||
<div class="form-text">{{ $t('apps.cmd_prep_desc') }}</div>
|
||||
<div class="d-flex justify-content-start mb-3 mt-3" v-if="editForm['prep-cmd'].length === 0">
|
||||
<button class="btn btn-success" @click="addPrepCmd">
|
||||
<i class="fas fa-plus mr-1"></i> Add Commands
|
||||
<i class="fas fa-plus mr-1"></i> {{ $t('apps.add_cmds') }}
|
||||
</button>
|
||||
</div>
|
||||
<table class="table" v-if="editForm['prep-cmd'].length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><i class="fas fa-play"></i> Do Command</th>
|
||||
<th scope="col"><i class="fas fa-undo"></i> Undo Command</th>
|
||||
<th scope="col"><i class="fas fa-play"></i> {{ $t('_common.do_cmd') }}</th>
|
||||
<th scope="col"><i class="fas fa-undo"></i> {{ $t('_common.undo_cmd') }}</th>
|
||||
<th scope="col" v-if="platform === 'windows'">
|
||||
<i class="fas fa-shield-alt"></i> Run as Admin
|
||||
<i class="fas fa-shield-alt"></i> {{ $t('_common.run_as') }}
|
||||
</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
@@ -167,7 +156,7 @@
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" :id="'prep-cmd-admin-' + i" v-model="c.elevated"
|
||||
true-value="true" false-value="false" />
|
||||
<label :for="'prep-cmd-admin-' + i" class="form-check-label">Elevated</label>
|
||||
<label :for="'prep-cmd-admin-' + i" class="form-check-label">{{ $t('_common.elevated') }}</label>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -184,7 +173,7 @@
|
||||
</div>
|
||||
<!-- detached -->
|
||||
<div class="mb-3">
|
||||
<label for="appName" class="form-label">Detached Commands</label>
|
||||
<label for="appName" class="form-label">{{ $t('apps.detached_cmds') }}</label>
|
||||
<div v-for="(c,i) in editForm.detached" class="d-flex justify-content-between my-2">
|
||||
<input type="text" v-model="editForm.detached[i]" class="form-control monospace">
|
||||
<button class="btn btn-danger mx-2" @click="editForm.detached.splice(i,1)">
|
||||
@@ -193,92 +182,72 @@
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button class="btn btn-success" @click="editForm.detached.push('');">
|
||||
<i class="fas fa-plus mr-1"></i> Add Detached Command
|
||||
<i class="fas fa-plus mr-1"></i> {{ $t('apps.detached_cmds_add') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
A list of commands to be run in the background.<br>
|
||||
<b>Note:</b> If the path to the command executable contains spaces, you must enclose it in quotes.
|
||||
{{ $t('apps.detached_cmds_desc') }}<br>
|
||||
<b>{{ $t('_common.note') }}</b> {{ $t('apps.detached_cmds_note') }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- command -->
|
||||
<div class="mb-3">
|
||||
<label for="appCmd" class="form-label">Command</label>
|
||||
<label for="appCmd" class="form-label">{{ $t('apps.cmd') }}</label>
|
||||
<input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp"
|
||||
v-model="editForm.cmd" />
|
||||
<div id="appCmdHelp" class="form-text">
|
||||
The main application to start. If blank, no application will be started.<br>
|
||||
<b>Note:</b> If the path to the command executable contains spaces, you must enclose it in quotes.
|
||||
{{ $t('apps.cmd_desc') }}<br>
|
||||
<b>{{ $t('_common.note') }}</b> {{ $t('apps.cmd_note') }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- working dir -->
|
||||
<div class="mb-3">
|
||||
<label for="appWorkingDir" class="form-label">Working Directory</label>
|
||||
<label for="appWorkingDir" class="form-label">{{ $t('apps.working_dir') }}</label>
|
||||
<input type="text" class="form-control monospace" id="appWorkingDir" aria-describedby="appWorkingDirHelp"
|
||||
v-model="editForm['working-dir']" />
|
||||
<div id="appWorkingDirHelp" class="form-text">
|
||||
The working directory that should be passed to the process. For
|
||||
example, some applications use the working directory to search for
|
||||
configuration files. If not set, Sunshine will default to the parent
|
||||
directory of the command
|
||||
</div>
|
||||
<div id="appWorkingDirHelp" class="form-text">{{ $t('apps.working_dir_desc') }}</div>
|
||||
</div>
|
||||
<!-- elevation -->
|
||||
<div class="mb-3 form-check" v-if="platform === 'windows'">
|
||||
<label for="appElevation" class="form-check-label">Run as administrator</label>
|
||||
<label for="appElevation" class="form-check-label">{{ $t('_common.run_as') }}</label>
|
||||
<input type="checkbox" class="form-check-input" id="appElevation" v-model="editForm.elevated"
|
||||
true-value="true" false-value="false" />
|
||||
<div class="form-text">
|
||||
This can be necessary for some applications that require administrator
|
||||
permissions to run properly.
|
||||
</div>
|
||||
<div class="form-text">{{ $t('apps.run_as_desc') }}</div>
|
||||
</div>
|
||||
<!-- auto-detach -->
|
||||
<div class="mb-3 form-check">
|
||||
<label for="autoDetach" class="form-check-label">Continue streaming if the application exits quickly</label>
|
||||
<label for="autoDetach" class="form-check-label">{{ $t('apps.auto_detach') }}</label>
|
||||
<input type="checkbox" class="form-check-input" id="autoDetach" v-model="editForm['auto-detach']"
|
||||
true-value="true" false-value="false" />
|
||||
<div class="form-text">
|
||||
This will attempt to automatically detect launcher-type apps that close
|
||||
quickly after launching another program or instance of themselves. When
|
||||
a launcher-type app is detected, it is treated as a detached app.
|
||||
</div>
|
||||
<div class="form-text">{{ $t('apps.auto_detach_desc') }}</div>
|
||||
</div>
|
||||
<!-- wait for all processes -->
|
||||
<div class="mb-3 form-check">
|
||||
<label for="waitAll" class="form-check-label">Continue streaming until all app processes exit</label>
|
||||
<label for="waitAll" class="form-check-label">{{ $t('apps.wait_all') }}</label>
|
||||
<input type="checkbox" class="form-check-input" id="waitAll" v-model="editForm['wait-all']"
|
||||
true-value="true" false-value="false" />
|
||||
<div class="form-text">
|
||||
This will continue streaming until all processes started by the app have terminated.
|
||||
When unchecked, streaming will stop when the initial app process exits, even if other
|
||||
app processes are still running.
|
||||
</div>
|
||||
<div class="form-text">{{ $t('apps.wait_all_desc') }}</div>
|
||||
</div>
|
||||
<!-- exit timeout -->
|
||||
<div class="mb-3">
|
||||
<label for="exitTimeout" class="form-label">Exit Timeout</label>
|
||||
<label for="exitTimeout" class="form-label">{{ $t('apps.exit_timeout') }}</label>
|
||||
<input type="text" class="form-control monospace" id="exitTimeout" aria-describedby="exitTimeoutHelp"
|
||||
v-model="editForm['exit-timeout']" />
|
||||
<div id="exitTimeoutHelp" class="form-text">
|
||||
Number of seconds to wait for all app processes to gracefully exit when requested to quit.<br>
|
||||
If unset, the default is to wait up to 5 seconds. If set to zero or a negative value,
|
||||
the app will be immediately terminated.
|
||||
</div>
|
||||
<div id="exitTimeoutHelp" class="form-text">{{ $t('apps.exit_timeout_desc') }}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="appImagePath" class="form-label">Image</label>
|
||||
<label for="appImagePath" class="form-label">{{ $t('apps.image') }}</label>
|
||||
<div class="input-group dropup">
|
||||
<input type="text" class="form-control monospace" id="appImagePath" aria-describedby="appImagePathHelp"
|
||||
v-model="editForm['image-path']" />
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="findCoverToggle"
|
||||
aria-expanded="false" @click="showCoverFinder" ref="coverFinderDropdown">
|
||||
Find Cover
|
||||
{{ $t('apps.find_cover') }}
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end w-50 cover-finder overflow-hidden"
|
||||
aria-labelledby="findCoverToggle">
|
||||
<div class="modal-header px-2">
|
||||
<h4 class="modal-title">Covers Found</h4>
|
||||
<h4 class="modal-title">{{ $t('apps.covers_found') }}</h4>
|
||||
<button type="button" class="btn-close mr-2" aria-label="Close" @click="closeCoverFinder"></button>
|
||||
</div>
|
||||
<div class="modal-body cover-results px-3 pt-3" :class="{ busy: coverFinderBusy }">
|
||||
@@ -286,7 +255,7 @@
|
||||
<div v-if="coverSearching" class="col-12 col-sm-6 col-lg-4 mb-3">
|
||||
<div class="cover-container">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden">{{ $t('apps.loading') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -303,100 +272,94 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="appImagePathHelp" class="form-text">
|
||||
Application icon/picture/image path that will be sent to client. Image
|
||||
must be a PNG file. If not set, Sunshine will send default box image.
|
||||
</div>
|
||||
<div id="appImagePathHelp" class="form-text">{{ $t('apps.image_desc') }}</div>
|
||||
</div>
|
||||
<div class="env-hint alert alert-info">
|
||||
<div class="form-text">
|
||||
<h4>About Environment Variables</h4>
|
||||
All commands get these environment variables by default:
|
||||
<h4>{{ $t('apps.env_vars_about') }}</h4>
|
||||
{{ $t('apps.env_vars_desc') }}
|
||||
</div>
|
||||
<table class="env-table">
|
||||
<tr>
|
||||
<td><b>Var Name</b></td>
|
||||
<td><b>{{ $t('apps.env_var_name') }}</b></td>
|
||||
<td><b></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_APP_ID</td>
|
||||
<td>App ID</td>
|
||||
<td>{{ $t('apps.env_app_id') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_APP_NAME</td>
|
||||
<td>App Name</td>
|
||||
<td>{{ $t('apps.env_app_name') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_WIDTH</td>
|
||||
<td>The Width requested by the client</td>
|
||||
<td>{{ $t('apps.env_client_width') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_HEIGHT</td>
|
||||
<td>The Height requested by the client</td>
|
||||
<td>{{ $t('apps.env_client_height') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_FPS</td>
|
||||
<td>The FPS requested by the client</td>
|
||||
<td>{{ $t('apps.env_client_fps') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_HDR</td>
|
||||
<td>(true/false) if HDR is enabled by the client</td>
|
||||
<td>{{ $t('apps.env_client_hdr') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_GCMAP</td>
|
||||
<td>(int) the requested gamepad mask, in a bitset/bitfield format</td>
|
||||
<td>{{ $t('apps.env_client_gcmap') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_HOST_AUDIO</td>
|
||||
<td>(true/false) if the client has requested host audio</td>
|
||||
<td>{{ $t('apps.env_client_host_audio') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_ENABLE_SOPS</td>
|
||||
<td>(true/false) if the client has requested the option to optimize the game for optimal
|
||||
streaming</td>
|
||||
<td>{{ $t('apps.env_client_enable_sops') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: monospace">SUNSHINE_CLIENT_AUDIO_CONFIGURATION</td>
|
||||
<td>The Audio Configuration requested by the client (2.0/5.1/7.1)</td>
|
||||
<td>{{ $t('apps.env_client_audio_config') }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="form-text" v-if="platform === 'windows'"><b>Example - QRes for Resolution
|
||||
Automation:</b>
|
||||
<pre>cmd /C <qres path>\QRes.exe /X:%SUNSHINE_CLIENT_WIDTH% /Y:%SUNSHINE_CLIENT_HEIGHT%</pre>
|
||||
<div class="form-text" v-if="platform === 'windows'"><b>{{ $t('apps.env_qres_example') }}</b>
|
||||
<pre>cmd /C <{{ $t('apps.env_qres_path') }}>\QRes.exe /X:%SUNSHINE_CLIENT_WIDTH% /Y:%SUNSHINE_CLIENT_HEIGHT%</pre>
|
||||
</div>
|
||||
<div class="form-text" v-else-if="platform === 'linux'"><b>Example - Xrandr for Resolution
|
||||
Automation:</b>
|
||||
<div class="form-text" v-else-if="platform === 'linux'"><b>{{ $t('apps.env_xrandr_example') }}</b>
|
||||
<pre>sh -c "xrandr --output HDMI-1 --mode \"${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}\" --rate 60"</pre>
|
||||
</div>
|
||||
<div class="form-text" v-else-if="platform === 'macos'"><b>Example - displayplacer for
|
||||
Resolution
|
||||
Automation:</b>
|
||||
<pre>sh -c "displayplacer "id:<screenId> res:${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} hz:60 scaling:on origin:(0,0) degree:0""</pre>
|
||||
<div class="form-text" v-else-if="platform === 'macos'"><b>{{ $t('apps.env_displayplacer_example') }}</b>
|
||||
<pre>sh -c "displayplacer "id:<screenId> res:${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} hz:60 scaling:on origin:(0,0) degree:0""</pre>
|
||||
</div>
|
||||
<div class="form-text"><a
|
||||
href="https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/guides/app_examples.html"
|
||||
target="_blank">See More</a></div>
|
||||
target="_blank">{{ $t('_common.see_more') }}</a></div>
|
||||
</div>
|
||||
<!-- Save buttons -->
|
||||
<div class="d-flex">
|
||||
<button @click="showEditForm = false" class="btn btn-secondary m-2">
|
||||
Cancel
|
||||
{{ $t('_common.cancel') }}
|
||||
</button>
|
||||
<button class="btn btn-primary m-2" @click="save">Save</button>
|
||||
<button class="btn btn-primary m-2" @click="save">{{ $t('_common.save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2" v-else>
|
||||
<button class="btn btn-primary" @click="newApp">
|
||||
<i class="fas fa-plus"></i> Add New
|
||||
<i class="fas fa-plus"></i> {{ $t('apps.add_new') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="module">
|
||||
import { createApp } from 'vue';
|
||||
import i18n from './locale.js'
|
||||
import Navbar from './Navbar.vue'
|
||||
import {Dropdown} from 'bootstrap'
|
||||
|
||||
const app = createApp({
|
||||
components: {
|
||||
Navbar
|
||||
@@ -598,6 +561,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
app.mount("#app")
|
||||
|
||||
//Wait for locale initialization, then render
|
||||
i18n().then(i18n => {
|
||||
app.use(i18n);
|
||||
app.mount('#app');
|
||||
});
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,16 +5,16 @@
|
||||
<%- header %>
|
||||
</head>
|
||||
|
||||
<body id="app">
|
||||
<body id="app" v-cloak>
|
||||
<Navbar></Navbar>
|
||||
<div id="content" class="container">
|
||||
<h1 class="my-4">Hello, Sunshine!</h1>
|
||||
<p>Sunshine is a self-hosted game stream host for Moonlight.</p>
|
||||
<h1 class="my-4">{{ $t('index.welcome') }}</h1>
|
||||
<p>{{ $t('index.description') }}</p>
|
||||
<div class="alert alert-danger" v-if="fancyLogs.find(x => x.level === 'Fatal')">
|
||||
<div style="line-height: 32px;">
|
||||
<i class="fas fa-circle-exclamation" style="font-size: 32px;margin-right: 0.25em;"></i>
|
||||
<b>Attention!</b> Sunshine detected these errors during startup. We <b>STRONGLY RECOMMEND</b> fixing them
|
||||
before streaming.<br>
|
||||
<p v-html="$t('index.startup_errors')"></p>
|
||||
<br>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="v in fancyLogs.filter(x => x.level === 'Fatal')">{{v.value}}</li>
|
||||
@@ -27,20 +27,22 @@
|
||||
<h2>Version {{version}}</h2>
|
||||
<br>
|
||||
<div v-if="loading">
|
||||
Loading Latest Release...
|
||||
{{ $t('index.loading_latest') }}
|
||||
</div>
|
||||
<div class="alert alert-success" v-if="buildVersionIsDirty">
|
||||
Thank you for helping to make Sunshine a better software! 🌇
|
||||
{{ $t('index.version_dirty') }} 🌇
|
||||
</div>
|
||||
<div v-else-if="!nightlyBuildAvailable && !stableBuildAvailable && !buildVersionIsDirty">
|
||||
<div class="alert alert-success">
|
||||
You're running the latest version of Sunshine
|
||||
{{ $t('index.version_latest') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="nightlyBuildAvailable">
|
||||
<div class="alert alert-warning">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="my-2">A new <b>Nightly</b> Version is Available!</div>
|
||||
<div class="my-2">
|
||||
<p v-html="$t('index.new_nightly')"></p>
|
||||
</div>
|
||||
<a class="btn btn-success m-1" href="https://github.com/LizardByte/Sunshine/releases/nightly-dev"
|
||||
target="_blank">Download</a>
|
||||
</div>
|
||||
@@ -51,8 +53,10 @@
|
||||
<div v-if="stableBuildAvailable">
|
||||
<div class="alert alert-warning">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="my-2">A new <b>Stable</b> Version is Available!</div>
|
||||
<a class="btn btn-success m-1" :href="githubVersion.html_url" target="_blank">Download</a>
|
||||
<div class="my-2">
|
||||
<p v-html="$t('index.new_stable')"></p>
|
||||
</div>
|
||||
<a class="btn btn-success m-1" :href="githubVersion.html_url" target="_blank">{{ $t('index.download') }}</a>
|
||||
</div>
|
||||
<h3>{{githubVersion.name}}</h3>
|
||||
<pre>{{githubVersion.body}}</pre>
|
||||
@@ -69,8 +73,10 @@
|
||||
|
||||
<script type="module">
|
||||
import { createApp } from 'vue'
|
||||
import i18n from './locale.js'
|
||||
import Navbar from './Navbar.vue'
|
||||
import ResourceCard from './ResourceCard.vue'
|
||||
|
||||
console.log("Hello, Sunshine!")
|
||||
let app = createApp({
|
||||
components: {
|
||||
@@ -106,10 +112,16 @@
|
||||
stableBuildAvailable() {
|
||||
// If we can't get versions, return false
|
||||
if (!this.githubVersion || !this.version) return false;
|
||||
|
||||
// Get the GitHub version tag
|
||||
let v = this.githubVersion.name;
|
||||
let v_github = this.githubVersion.name;
|
||||
// If the version starts with a v, remove it
|
||||
if (v.indexOf("v") === 0) v = v.substring(1);
|
||||
if (v_github.indexOf("v") === 0) v_github = v_github.substring(1);
|
||||
|
||||
// Get the current version
|
||||
let v_this = this.version;
|
||||
// If the version starts with a v, remove it
|
||||
if (v_this.indexOf("v") === 0) v_this = v_this.substring(1);
|
||||
|
||||
// if nightly or dirty, we do an additional check to make sure it's an actual upgrade.
|
||||
if (this.buildVersionIsNightly || this.buildVersionIsDirty) {
|
||||
@@ -118,7 +130,7 @@
|
||||
}
|
||||
|
||||
// return true if the version is different, otherwise false
|
||||
return v !== this.version;
|
||||
return v_github !== v_this;
|
||||
},
|
||||
nightlyBuildAvailable() {
|
||||
// Verify nightly data is available and the build version is not stable
|
||||
@@ -154,5 +166,10 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
app.mount('#app');
|
||||
|
||||
//Wait for locale initialization, then render
|
||||
i18n().then(i18n => {
|
||||
app.use(i18n);
|
||||
app.mount('#app');
|
||||
});
|
||||
</script>
|
||||
|
||||
27
src_assets/common/assets/web/locale.js
Normal file
27
src_assets/common/assets/web/locale.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import {createI18n} from "vue-i18n";
|
||||
|
||||
// Import only the fallback language files
|
||||
import en from './public/assets/locale/en.json'
|
||||
|
||||
export default async function() {
|
||||
let r = await (await fetch("/api/configLocale")).json();
|
||||
let locale = r.locale ?? "en";
|
||||
document.querySelector('html').setAttribute('lang', locale);
|
||||
let messages = {
|
||||
en
|
||||
};
|
||||
try {
|
||||
if (locale !== 'en') {
|
||||
let r = await (await fetch(`/assets/locale/${locale}.json`)).json();
|
||||
messages[locale] = r;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to download translations", e);
|
||||
}
|
||||
const i18n = createI18n({
|
||||
locale: locale, // set locale
|
||||
fallbackLocale: 'en', // set fallback locale
|
||||
messages: messages
|
||||
})
|
||||
return i18n;
|
||||
}
|
||||
@@ -16,42 +16,40 @@
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body id="app">
|
||||
<body id="app" v-cloak>
|
||||
<Navbar></Navbar>
|
||||
<div class="container">
|
||||
<h1 class="my-4">Password Change</h1>
|
||||
<h1 class="my-4">{{ $t('password.password_change') }}</h1>
|
||||
<form @submit.prevent="save">
|
||||
<div class="card d-flex p-4 flex-row">
|
||||
<div class="col-md-6 px-4">
|
||||
<h4>Current Credentials</h4>
|
||||
<h4>{{ $t('password.current_creds') }}</h4>
|
||||
<div class="mb-3">
|
||||
<label for="currentUsername" class="form-label">Username</label>
|
||||
<label for="currentUsername" class="form-label">{{ $t('_common.username') }}</label>
|
||||
<input required type="text" class="form-control" id="currentUsername"
|
||||
v-model="passwordData.currentUsername" />
|
||||
<div class="form-text"> </div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="currentPassword" class="form-label">Password</label>
|
||||
<label for="currentPassword" class="form-label">{{ $t('_common.password') }}</label>
|
||||
<input autocomplete="current-password" type="password" class="form-control" id="currentPassword"
|
||||
v-model="passwordData.currentPassword" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 px-4">
|
||||
<h4>New Credentials</h4>
|
||||
<h4>{{ $t('password.new_creds') }}</h4>
|
||||
<div class="mb-3">
|
||||
<label for="newUsername" class="form-label">New Username</label>
|
||||
<label for="newUsername" class="form-label">{{ $t('_common.username') }}</label>
|
||||
<input type="text" class="form-control" id="newUsername" v-model="passwordData.newUsername" />
|
||||
<div class="form-text">
|
||||
If not specified, the username will not change
|
||||
</div>
|
||||
<div class="form-text">{{ $t('password.new_username_desc') }}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="newPassword" class="form-label">Password</label>
|
||||
<label for="newPassword" class="form-label">{{ $t('_common.password') }}</label>
|
||||
<input autocomplete="new-password" required type="password" class="form-control" id="newPassword"
|
||||
v-model="passwordData.newPassword" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirmNewPassword" class="form-label">Confirm Password</label>
|
||||
<label for="confirmNewPassword" class="form-label">{{ $t('password.confirm_password') }}</label>
|
||||
<input autocomplete="new-password" required type="password" class="form-control" id="confirmNewPassword"
|
||||
v-model="passwordData.confirmNewPassword" />
|
||||
</div>
|
||||
@@ -59,17 +57,17 @@
|
||||
</div>
|
||||
<div class="alert alert-danger" v-if="error"><b>Error: </b>{{error}}</div>
|
||||
<div class="alert alert-success" v-if="success">
|
||||
<b>Success! </b>This page will reload soon, your browser will ask you for
|
||||
the new credentials
|
||||
<b>{{ $t('_common.success') }}</b> {{ $t('password.success_msg') }}
|
||||
</div>
|
||||
<div class="mb-3 buttons">
|
||||
<button class="btn btn-primary">Save</button>
|
||||
<button class="btn btn-primary">{{ $t('_common.save') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
<script type="module">
|
||||
import { createApp } from 'vue'
|
||||
import i18n from './locale.js'
|
||||
import Navbar from './Navbar.vue'
|
||||
|
||||
const app = createApp({
|
||||
@@ -115,5 +113,9 @@
|
||||
},
|
||||
});
|
||||
|
||||
app.mount("#app");
|
||||
//Wait for locale initialization, then render
|
||||
i18n().then(i18n => {
|
||||
app.use(i18n);
|
||||
app.mount('#app');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,19 +5,17 @@
|
||||
<%- header %>
|
||||
</head>
|
||||
|
||||
<body id="app">
|
||||
<body id="app" v-cloak>
|
||||
<Navbar></Navbar>
|
||||
<div id="content" class="container">
|
||||
<h1 class="my-4">PIN Pairing</h1>
|
||||
<h1 class="my-4">{{ $t('pin.pin_pairing') }}</h1>
|
||||
<form action="" class="form d-flex flex-column align-items-center" id="form">
|
||||
<div class="card flex-column d-flex p-4 mb-4">
|
||||
<input type="text" pattern="\d*" placeholder="PIN" id="pin-input" class="form-control my-4" />
|
||||
<button class="btn btn-primary">Send</button>
|
||||
<input type="text" pattern="\d*" placeholder="PIN" autofocus id="pin-input" class="form-control my-4" />
|
||||
<button class="btn btn-primary">{{ $t('pin.send') }}</button>
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<b>Warning!</b> Make sure you have access to the client you are pairing
|
||||
with.<br>
|
||||
This software can give total control to your computer, so be careful!
|
||||
<b>{{ $t('_common.warning') }}</b> {{ $t('pin.warning_msg') }}
|
||||
</div>
|
||||
<div id="status"></div>
|
||||
</form>
|
||||
@@ -25,33 +23,41 @@
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
import { createApp } from 'vue'
|
||||
import i18n from './locale.js'
|
||||
import Navbar from './Navbar.vue'
|
||||
import {createApp} from 'vue'
|
||||
|
||||
let app = createApp({
|
||||
components: {
|
||||
Navbar
|
||||
}
|
||||
});
|
||||
app.mount("#app");
|
||||
|
||||
document.querySelector("#form").addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
let pin = document.querySelector("#pin-input").value;
|
||||
document.querySelector("#status").innerHTML = "";
|
||||
let b = JSON.stringify({ pin: pin });
|
||||
fetch("/api/pin", { method: "POST", body: b })
|
||||
.then((response) => response.json())
|
||||
.then((response) => {
|
||||
if (response.status.toString().toLowerCase() === "true") {
|
||||
document.querySelector(
|
||||
"#status"
|
||||
).innerHTML = `<div class="alert alert-success" role="alert">Success! Please check Moonlight to continue</div>`;
|
||||
document.querySelector("#pin-input").value = "";
|
||||
} else {
|
||||
document.querySelector(
|
||||
"#status"
|
||||
).innerHTML = `<div class="alert alert-danger" role="alert">Pairing Failed: Check if the PIN is typed correctly</div>`;
|
||||
}
|
||||
});
|
||||
|
||||
//Wait for locale initialization, then render
|
||||
i18n().then(i18n => {
|
||||
app.use(i18n);
|
||||
app.mount('#app');
|
||||
|
||||
// this must be after mounting the app
|
||||
document.querySelector("#form").addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
let pin = document.querySelector("#pin-input").value;
|
||||
document.querySelector("#status").innerHTML = "";
|
||||
let b = JSON.stringify({ pin: pin });
|
||||
fetch("/api/pin", { method: "POST", body: b })
|
||||
.then((response) => response.json())
|
||||
.then((response) => {
|
||||
if (response.status.toString().toLowerCase() === "true") {
|
||||
document.querySelector(
|
||||
"#status"
|
||||
).innerHTML = `<div class="alert alert-success" role="alert">${i18n.global.t('pin.pair_success')}</div>`;
|
||||
document.querySelector("#pin-input").value = "";
|
||||
} else {
|
||||
document.querySelector(
|
||||
"#status"
|
||||
).innerHTML = `<div class="alert alert-danger" role="alert">${i18n.global.t('pin.pair_failure')}</div>`;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/* Hide pages while localization is loading */
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
389
src_assets/common/assets/web/public/assets/locale/de.json
Normal file
389
src_assets/common/assets/web/public/assets/locale/de.json
Normal file
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"_common": {
|
||||
"apply": "Anwenden",
|
||||
"auto": "Automatisch",
|
||||
"autodetect": "AutoDetection (empfohlen)",
|
||||
"cancel": "Abbrechen",
|
||||
"disabled": "Deaktiviert",
|
||||
"disabled_def": "Deaktiviert (Standard)",
|
||||
"do_cmd": "Befehl ausführen",
|
||||
"elevated": "Erhöhte",
|
||||
"enabled": "Aktiviert",
|
||||
"enabled_def": "Aktiviert (Standard)",
|
||||
"error": "Fehler!",
|
||||
"note": "Hinweis:",
|
||||
"password": "Passwort",
|
||||
"run_as": "Als Admin ausführen",
|
||||
"save": "Speichern",
|
||||
"see_more": "Mehr ansehen",
|
||||
"success": "Erfolgreich!",
|
||||
"undo_cmd": "Befehl rückgängig machen",
|
||||
"username": "Benutzername",
|
||||
"warning": "Warnung!"
|
||||
},
|
||||
"apps": {
|
||||
"actions": "Aktionen",
|
||||
"add_cmds": "Befehle hinzufügen",
|
||||
"add_new": "Neu",
|
||||
"app_name": "Anwendungsname",
|
||||
"app_name_desc": "Anwendungsname, wie auf Mondlicht gezeigt",
|
||||
"applications_desc": "Anwendungen werden nur beim Neustart des Clients aktualisiert",
|
||||
"applications_title": "Anwendungen",
|
||||
"auto_detach": "Streaming fortsetzen, wenn die Anwendung schnell beendet wird",
|
||||
"auto_detach_desc": "Dies wird versuchen, automatisch Apps vom Launcher-Typ zu erkennen, die sich nach dem Start eines anderen Programms oder einer Instanz von sich selbst schnell schließen. Wenn eine Anwendung vom Launcher-Typ erkannt wird, wird sie als abgetrennte App behandelt.",
|
||||
"cmd": "Befehl",
|
||||
"cmd_desc": "Die zu startende Hauptanwendung. Wenn leer wird keine Anwendung gestartet.",
|
||||
"cmd_note": "Wenn der Pfad zum ausführbaren Kommando Leerzeichen enthält, müssen Sie ihn in Anführungszeichen einfügen.",
|
||||
"cmd_prep_desc": "Eine Liste von Befehlen, die vor oder nach dieser Anwendung ausgeführt werden sollen. Wenn einer der Prep-Befehle fehlschlägt, wird das Starten der Anwendung abgebrochen.",
|
||||
"cmd_prep_name": "Befehlsvorbereitungen",
|
||||
"covers_found": "Cover gefunden",
|
||||
"delete": "Löschen",
|
||||
"detached_cmds": "Getrennte Befehle",
|
||||
"detached_cmds_add": "Separiertes Kommando hinzufügen",
|
||||
"detached_cmds_desc": "Eine Liste von Befehlen, die im Hintergrund ausgeführt werden sollen.",
|
||||
"detached_cmds_note": "Wenn der Pfad zum ausführbaren Kommando Leerzeichen enthält, müssen Sie ihn in Anführungszeichen einfügen.",
|
||||
"edit": "Bearbeiten",
|
||||
"env_app_id": "App-ID",
|
||||
"env_app_name": "App-Name",
|
||||
"env_client_audio_config": "Die vom Client angeforderte Audio-Konfiguration (2.0/5.1/7.1)",
|
||||
"env_client_enable_sops": "Der Client hat die Option angefordert, das Spiel für ein optimales Streaming zu optimieren (true/false)",
|
||||
"env_client_fps": "Das vom Client angeforderte FPS (int)",
|
||||
"env_client_gcmap": "Die angeforderte Gamepadmaske im Bitset/Bitfield Format (int)",
|
||||
"env_client_hdr": "HDR ist vom Client aktiviert (true/false)",
|
||||
"env_client_height": "Die vom Client angeforderte Höhe (int)",
|
||||
"env_client_host_audio": "Der Client hat Host-Audio angefordert (true/falsch)",
|
||||
"env_client_width": "Die vom Client angeforderte Breite (int)",
|
||||
"env_displayplacer_example": "Beispiel - displayplacer für die Automatisierung der Auflösung:",
|
||||
"env_qres_example": "Beispiel - QRes für die Automatisierung der Auflösung:",
|
||||
"env_qres_path": "qres Pfad",
|
||||
"env_var_name": "Var Name",
|
||||
"env_vars_about": "Über Umgebungsvariablen",
|
||||
"env_vars_desc": "Alle Befehle erhalten diese Umgebungsvariablen standardmäßig:",
|
||||
"env_xrandr_example": "Beispiel - Xrandr für die Auflösungsautomatisierung:",
|
||||
"exit_timeout": "Timeout beenden",
|
||||
"exit_timeout_desc": "Anzahl der Sekunden, die gewartet werden, bis alle App-Prozesse anmutig beendet werden, wenn sie beendet werden. Wenn nicht gesetzt, ist die Standardeinstellung bis zu 5 Sekunden. Wenn Null oder ein negativer Wert gesetzt wird, wird die App sofort beendet.",
|
||||
"find_cover": "Cover finden",
|
||||
"global_prep_desc": "Aktiviere/Deaktiviere die Ausführung von Global Prep Commands für diese Anwendung.",
|
||||
"global_prep_name": "Globale Vorbereitungsbefehle",
|
||||
"image": "Bild",
|
||||
"image_desc": "Symbol/Bild/Bild/Bildpfad, der an den Client gesendet wird. Das Bild muss eine PNG-Datei sein. Falls nicht gesetzt, sendet Sunshine ein Standardbild-Bild.",
|
||||
"loading": "Wird geladen...",
|
||||
"name": "Name",
|
||||
"output_desc": "Die Datei, in der die Ausgabe des Befehls gespeichert wird, wenn sie nicht angegeben ist, wird die Ausgabe ignoriert",
|
||||
"output_name": "Ausgang",
|
||||
"run_as_desc": "Dies kann für einige Anwendungen notwendig sein, die Administratorrechte benötigen, um ordnungsgemäß zu funktionieren.",
|
||||
"wait_all": "Streaming fortsetzen bis alle App-Prozesse beendet sind",
|
||||
"wait_all_desc": "Dies wird fortgesetzt, bis alle Prozesse, die von der App gestartet werden, beendet sind. Wenn diese Option deaktiviert ist, wird das Streaming gestoppt, wenn der erste App-Prozess beendet wird, auch wenn andere App-Prozesse noch laufen.",
|
||||
"working_dir": "Arbeitsverzeichnis",
|
||||
"working_dir_desc": "Das Arbeitsverzeichnis, das an den Prozess übergeben werden soll. Zum Beispiel verwenden einige Anwendungen das Arbeitsverzeichnis, um nach Konfigurationsdateien zu suchen. Falls nicht gesetzt, wird Sunshine standardmäßig das übergeordnete Verzeichnis des Befehls verwenden"
|
||||
},
|
||||
"config": {
|
||||
"adapter_name": "Adaptername",
|
||||
"adapter_name_desc_linux_1": "Geben Sie eine GPU für die Aufnahme manuell an.",
|
||||
"adapter_name_desc_linux_2": "um alle Geräte zu finden, die VAAPI nutzen können",
|
||||
"adapter_name_desc_linux_3": "Ersetze ``renderD129`` durch das Gerät von oben, um den Namen und die Fähigkeiten des Geräts aufzulisten. Um von Sunshine unterstützt zu werden, muss es zumindest über folgende Punkte verfügen:",
|
||||
"adapter_name_desc_win": "Legen Sie eine GPU für die Aufnahme manuell fest. Falls nicht festgelegt, wird die GPU automatisch ausgewählt. Wir empfehlen dringend, dieses Feld leer zu lassen, um die automatische GPU-Auswahl zu verwenden! Hinweis: Diese GPU muss ein Display angeschlossen und eingeschaltet haben. Die passenden Werte finden Sie mit dem folgenden Befehl:",
|
||||
"adapter_name_placeholder_win": "Radeon RX 580-Serie",
|
||||
"add": "Neu",
|
||||
"address_family": "Adressfamilie",
|
||||
"address_family_both": "IPv4+IPv6",
|
||||
"address_family_desc": "Adressfamilie einstellen, die von Sunshine verwendet wird",
|
||||
"address_family_ipv4": "Nur IPv4",
|
||||
"always_send_scancodes": "Scancodes immer senden",
|
||||
"always_send_scancodes_desc": "Das Senden von Scancodes verbessert die Kompatibilität mit Spielen und Apps, kann aber zu falschen Tastatureingaben von bestimmten Clients führen, die kein amerikanisches Tastaturlayout verwenden. Aktivieren, wenn die Eingabe der Tastatur in bestimmten Anwendungen überhaupt nicht funktioniert. Deaktivieren, wenn Schlüssel auf dem Client die falsche Eingabe auf dem Host generieren.",
|
||||
"amd_coder": "AMF Coder (H264)",
|
||||
"amd_coder_desc": "Erlaubt es Ihnen, die Entropy-Kodierung auszuwählen, um die Qualität oder die Kodierungsgeschwindigkeit zu priorisieren. H.264 nur.",
|
||||
"amd_enforce_hrd": "Hypothetische Referenz-Decodierer (HRD) durchsetzen",
|
||||
"amd_enforce_hrd_desc": "Steigern Sie die Einschränkungen bei der Ratensteuerung, um die Anforderungen des HRD-Modells zu erfüllen. Dies reduziert die Bitratenüberläufe erheblich, kann jedoch zu Kodierungsartefakten oder zu geringerer Qualität auf bestimmten Karten führen.",
|
||||
"amd_preanalysis": "AMF-Voranalyse",
|
||||
"amd_preanalysis_desc": "Dies ermöglicht die Vorabanalyse der Rate, wodurch die Qualität auf Kosten einer erhöhten Encoding-Latenz erhöht werden kann.",
|
||||
"amd_quality": "AMF-Qualität",
|
||||
"amd_quality_balanced": "ausgewogen -- Ausgewogen (Standard)",
|
||||
"amd_quality_desc": "Dies steuert die Balance zwischen Kodierungsgeschwindigkeit und Qualität.",
|
||||
"amd_quality_group": "AMF Qualitätseinstellungen",
|
||||
"amd_quality_quality": "Qualität -- Qualität bevorzugen",
|
||||
"amd_quality_speed": "speed -- bevorzuge Geschwindigkeit",
|
||||
"amd_rc": "AMF-Ratensteuerung",
|
||||
"amd_rc_cbr": "cbr – Konstante Bitrate",
|
||||
"amd_rc_cqp": "cqp -- Konstanter qp-Modus",
|
||||
"amd_rc_desc": "Diese steuert die Methode der Ratensteuerung, um sicherzustellen, dass wir nicht das Client-Bitrate Ziel überschreiten. 'cqp' ist nicht geeignet für Bitraten-Targeting, und andere Optionen außer 'vbr_latency' hängen von der Durchsetzung von HRD ab, um Bitraten-Überläufe einzuschränken.",
|
||||
"amd_rc_group": "AMF Rate Control Einstellungen",
|
||||
"amd_rc_vbr_latency": "vbr_latency -- latenzeingeschränkte Bitrate (Standard)",
|
||||
"amd_rc_vbr_peak": "vbr_peak – eingeschränkte Variablen-Bitrate spitzen",
|
||||
"amd_usage": "AMF-Nutzung",
|
||||
"amd_usage_desc": "Dies legt das Basiscodierungsprofil fest. Alle unten dargestellten Optionen werden eine Teilmenge des Nutzungsprofils überschreiben. Es werden jedoch zusätzliche versteckte Einstellungen angewendet, die an anderer Stelle nicht konfiguriert werden können.",
|
||||
"amd_usage_lowlatency": "niedrige Latenz - niedrige Latenz (schnell)",
|
||||
"amd_usage_lowlatency_high_quality": "lowlatency_high_quality - niedrige Latenz, hohe Qualität (schnell)",
|
||||
"amd_usage_transcoding": "transcoding -- Umkodierung (langsamste)",
|
||||
"amd_usage_ultralowlatency": "ultralowlatenz - extrem niedrige Latenz (schnellste)",
|
||||
"amd_usage_webcam": "webcam -- Webcam (langsam)",
|
||||
"amd_vbaq": "AMF-Varianz-basierte Adaptive Quantisierung (VBAQ)",
|
||||
"amd_vbaq_desc": "Das menschliche visuelle System ist in der Regel weniger empfindlich auf Artefakte in stark strukturierten Bereichen. Im VBAQ-Modus wird die Pixelvarianz verwendet, um die Komplexität der räumlichen Texturen anzuzeigen, so dass der Encoder mehr Bits für glättende Bereiche zuweisen kann. Die Aktivierung dieser Funktion führt zu Verbesserungen der subjektiven visuellen Qualität mit einigen Inhalten.",
|
||||
"apply_note": "Klicken Sie auf 'Anwenden', um Sunshine neu zu starten und Änderungen anzuwenden. Dies wird alle laufenden Sitzungen beenden.",
|
||||
"audio_sink": "Audio Sink",
|
||||
"audio_sink_desc_linux": "Der Name des Audio-Spüls, der für Audio Loopback verwendet wird. Wenn Sie diese Variable nicht angeben, wählt pulseaudio das Standard-Monitorgerät. Sie können den Namen des Audiospülers mit einem Befehl finden:",
|
||||
"audio_sink_desc_macos": "Der Name des für Audio Loopback verwendeten Audiosenks kann aufgrund von Systembeschränkungen nur auf Mikrofone auf macOS zugreifen. Zum Streamen von System-Audio mit Soundflower oder BlackHole.",
|
||||
"audio_sink_desc_win": "Geben Sie ein bestimmtes Audiogerät für die Aufnahme manuell an. Wenn nicht gesetzt, wird das Gerät automatisch ausgewählt. Wir empfehlen dringend, dieses Feld leer zu lassen, um die automatische Geräteauswahl zu verwenden! Wenn Sie mehrere Audiogeräte mit identischen Namen haben, können Sie die Geräte-ID mit dem folgenden Befehl erhalten:",
|
||||
"audio_sink_placeholder_macos": "BlackHole 2ch",
|
||||
"audio_sink_placeholder_win": "Lautsprecher (High Definition Audio Device)",
|
||||
"av1_mode": "AV1 Support",
|
||||
"av1_mode_0": "Sunshine werbt Unterstützung für AV1 basierend auf Encoder Fähigkeiten (empfohlen)",
|
||||
"av1_mode_1": "Sunshine werbt keinen Support für AV1",
|
||||
"av1_mode_2": "Sunshine werbt Unterstützung für AV1 Hauptprofil mit 8-Bit",
|
||||
"av1_mode_3": "Sunshine werbt Unterstützung für AV1 Hauptprofile mit 8-Bit und 10-Bit (HDR)",
|
||||
"av1_mode_desc": "Ermöglicht dem Client, AV1 Haupt-8-bit oder 10-bit Video-Streams anzufordern. AV1 ist CPU-intensiver zum Kodieren, daher kann die Aktivierung die Leistung bei der Verwendung von Software Codierung verringern.",
|
||||
"back_button_timeout": "Timeout für Home/Guide Button Emulation",
|
||||
"back_button_timeout_desc": "Wenn die Schaltfläche Zurück/Auswählen für die angegebene Anzahl an Millisekunden gedrückt gehalten wird, wird die Taste Home/Guide emuliert. Wenn auf einen Wert < 0 (Standard) gesetzt ist, wird die Home/Guide-Taste nicht nachgeahmt.",
|
||||
"capture": "Erzwinge eine bestimmte Aufnahmemethode",
|
||||
"capture_desc": "Im automatischen Modus wird Sunshine den ersten verwenden, der funktioniert. NvFBC benötigt gepatchte Nvidia-Treiber.",
|
||||
"cert": "Zertifikat",
|
||||
"cert_desc": "Das Zertifikat, das für das Web-UI und Moonlight Client-Paar verwendet wird. Für bestmögliche Kompatibilität sollte dieser einen RSA-2048 öffentlichen Schlüssel haben.",
|
||||
"channels": "Maximal verbundene Clients",
|
||||
"channels_desc_1": "Sunshine kann eine einzelne Streaming-Sitzung gleichzeitig mit mehreren Clients teilen.",
|
||||
"channels_desc_2": "Einige Hardware-Encoder haben möglicherweise Einschränkungen, die die Leistung bei mehreren Streams verringern.",
|
||||
"coder_cabac": "cabac -- kontextadaptive binäre arithmetische Kodierung - höhere Qualität",
|
||||
"coder_cavlc": "cavlc -- kontextadaptive Kodierung variabler Länge - schnellere Dekodierung",
|
||||
"configuration": "Konfiguration",
|
||||
"controller": "Enable Gamepad Input",
|
||||
"controller_desc": "Erlaubt Gästen das Host-System mit einem Gamepad/Controller zu steuern",
|
||||
"credentials_file": "Anmeldedaten Datei",
|
||||
"credentials_file_desc": "Speichere Benutzername/Passwort getrennt von Sunshine's Status-Datei.",
|
||||
"ds4_back_as_touchpad_click": "Zum Touchpad-Klick zurück/auswählen",
|
||||
"ds4_back_as_touchpad_click_desc": "Beim Erzwingen der DS4-Emulation zum Touchpad-Klick zurück/auswählen",
|
||||
"encoder": "Erzwinge einen bestimmten Encoder",
|
||||
"encoder_desc": "Erzwinge einen bestimmten Encoder, sonst wählt Sunshine die beste verfügbare Option. Notiz: Wenn Sie einen Hardwarekodierer unter Windows angeben, muss er mit der GPU übereinstimmen, in der das Display verbunden ist.",
|
||||
"encoder_software": "Software",
|
||||
"external_ip": "Externe IP",
|
||||
"external_ip_desc": "Wenn keine externe IP-Adresse angegeben ist, erkennt Sunshine automatisch externe IP",
|
||||
"fec_percentage": "Prozentsatz FEC",
|
||||
"fec_percentage_desc": "Prozentsatz der Fehlerkorrektur von Paketen pro Datenpaket in jedem Videobild. Höhere Werte können für mehr Netzwerk-Paketverlust korrigieren, aber auf Kosten einer erhöhten Bandbreitennutzung.",
|
||||
"ffmpeg_auto": "auto -- ffmpeg entscheiden lassen (Standard)",
|
||||
"file_apps": "App-Datei",
|
||||
"file_apps_desc": "Die Datei, in der die aktuellen Apps von Sunshine gespeichert werden.",
|
||||
"file_state": "Zustandsdatei",
|
||||
"file_state_desc": "Die Datei, in der der aktuelle Zustand von Sunshine gespeichert ist",
|
||||
"fps": "Angekündigte FPS",
|
||||
"gamepad": "Emulierter Gamepad-Typ",
|
||||
"gamepad_auto": "Automatische Auswahloptionen",
|
||||
"gamepad_desc": "Wähle welche Art von Gamepad emuliert werden soll",
|
||||
"gamepad_ds4": "DS4 (PS4)",
|
||||
"gamepad_manual": "Manuelle DS4 Optionen",
|
||||
"gamepad_x360": "X360 (Xbox 360)",
|
||||
"global_prep_cmd": "Befehlsvorbereitungen",
|
||||
"global_prep_cmd_desc": "Konfigurieren Sie eine Liste von Befehlen, die vor oder nach Ausführung einer Anwendung ausgeführt werden sollen. Wenn eines der angegebenen Vorbereitungsbefehle fehlschlägt, wird der Anwendungsstart abgebrochen.",
|
||||
"hevc_mode": "HEVC Unterstützung",
|
||||
"hevc_mode_0": "Sunshine werbt Unterstützung für HEVC basierend auf Encoderfähigkeiten (empfohlen)",
|
||||
"hevc_mode_1": "Sunshine werbt keine Unterstützung für HEVC",
|
||||
"hevc_mode_2": "Sunshine werbt Unterstützung für das HEVC Hauptprofil",
|
||||
"hevc_mode_3": "Sunshine werbt Unterstützung für HEVC Haupt- und Main10-Profile (HDR)",
|
||||
"hevc_mode_desc": "Ermöglicht dem Client, HEVC Main oder HEVC Main10 Videostreams anzufordern. HEVC ist CPU-intensiver zum Kodieren, daher kann dies die Leistung bei der Verwendung von Software-Kodierungen verringern.",
|
||||
"high_resolution_scrolling": "Unterstützung für hochauflösende Scrolling",
|
||||
"high_resolution_scrolling_desc": "Wenn aktiviert, durchläuft Sunshine hochauflösende Scroll-Ereignisse von Moonlight-Clients. Dies kann nützlich sein, um ältere Anwendungen zu deaktivieren, die bei hochauflösenden Scroll-Ereignissen zu schnell scrollen.",
|
||||
"install_steam_audio_drivers": "Steam Audio Treiber installieren",
|
||||
"install_steam_audio_drivers_desc": "Wenn Steam installiert ist, wird der Steam Streaming Speakers Treiber automatisch installiert, um 5.1/7.1 Surround-Sound zu unterstützen und Host-Audio zu mutieren.",
|
||||
"key_repeat_delay": "Schlüssel-Wiederholung Verzögerung",
|
||||
"key_repeat_delay_desc": "Legen Sie fest, wie schnell sich die Tasten wiederholen. Die anfängliche Verzögerung in Millisekunden bevor Sie die Tasten wiederholen.",
|
||||
"key_repeat_frequency": "Tastendruck-Frequenz",
|
||||
"key_repeat_frequency_desc": "Wie oft Tasten jede Sekunde wiederholen. Diese konfigurierbare Option unterstützt Dezimalstellen.",
|
||||
"key_rightalt_to_key_win": "Rechter Alt-Taste auf Windows-Taste zuweisen",
|
||||
"key_rightalt_to_key_win_desc": "Möglicherweise können Sie den Windows-Schlüssel nicht direkt von Moonlight senden. In diesen Fällen kann es nützlich sein, Sunshine glauben zu lassen, dass die rechte Alt-Taste die Windows-Taste ist",
|
||||
"keyboard": "Tastatureingabe aktivieren",
|
||||
"keyboard_desc": "Erlaubt Gästen das Host-System mit der Tastatur zu steuern",
|
||||
"lan_encryption_mode": "LAN-Verschlüsselungsmodus",
|
||||
"lan_encryption_mode_1": "Für unterstützte Clients aktiviert",
|
||||
"lan_encryption_mode_2": "Benötigt für alle Kunden",
|
||||
"lan_encryption_mode_desc": "Dies legt fest, wann die Verschlüsselung beim Streaming über Ihr lokales Netzwerk verwendet wird. Verschlüsselung kann die Streaming-Leistung senken, insbesondere auf weniger leistungsfähigen Hosts und Clients.",
|
||||
"locale": "Lokal",
|
||||
"locale_desc": "Die Locale, die für die Benutzeroberfläche von Sunshine verwendet wird.",
|
||||
"log_level": "Log-Level",
|
||||
"log_level_0": "Verbose",
|
||||
"log_level_1": "Debug",
|
||||
"log_level_2": "Info",
|
||||
"log_level_3": "Warnung",
|
||||
"log_level_4": "Fehler",
|
||||
"log_level_5": "Fatal",
|
||||
"log_level_6": "Keine",
|
||||
"log_level_desc": "Der minimale Log-Level wird auf Standard gedruckt",
|
||||
"log_path": "Logdateipfad",
|
||||
"log_path_desc": "Die Datei, in der die aktuellen Logs von Sunshine gespeichert werden.",
|
||||
"min_threads": "Minimale CPU-Thread-Anzahl",
|
||||
"min_threads_desc": "Die Erhöhung des Wertes verringert die Encoding-Effizienz, aber der Abgleich lohnt sich in der Regel, mehr CPU-Kerne für die Kodierung zu verwenden. Der ideale Wert ist der niedrigste Wert, der zuverlässig an den gewünschten Streaming-Einstellungen auf Ihrer Hardware kodieren kann.",
|
||||
"misc": "Verschiedene Optionen",
|
||||
"motion_as_ds4": "Ein DS4 Gamepad emulieren, wenn der Client Gamepad Bewegungsmelder meldet",
|
||||
"motion_as_ds4_desc": "Wenn deaktiviert, werden Bewegungssensor bei der Auswahl des Gamepad-Typs nicht berücksichtigt.",
|
||||
"mouse": "Maus-Eingabe aktivieren",
|
||||
"mouse_desc": "Erlaubt Gästen das Host-System mit der Maus zu steuern",
|
||||
"native_pen_touch": "Native Pen/Touch Unterstützung",
|
||||
"native_pen_touch_desc": "Wenn aktiviert, durchläuft Sunshine natives Pen / Berühren von Moonlight-Clients. Dies kann nützlich sein, um ältere Anwendungen ohne nativen Stift-/Berührungs-Support zu deaktivieren.",
|
||||
"nvenc_h264_cavlc": "CAVLC gegenüber CABAC in H.264 bevorzugen",
|
||||
"nvenc_h264_cavlc_desc": "Einfachere Form der Entropy-Codierung. CAVLC benötigt ca. 10% mehr Bitrate für die gleiche Qualität. Nur relevant für wirklich alte Decodierungsgeräte.",
|
||||
"nvenc_latency_over_power": "Reduzierte Encoding-Latenz gegenüber Energieeinsparungen bevorzugen",
|
||||
"nvenc_latency_over_power_desc": "Sunshine fordert die maximale GPU-Taktgeschwindigkeit beim Streaming an, um die Encoding-Latenz zu reduzieren. Deaktivieren wird nicht empfohlen, da dies zu einer signifikant erhöhten Encoding-Latenz führen kann.",
|
||||
"nvenc_opengl_vulkan_on_dxgi": "OpenGL/Vulkan auf DXGI zeigen",
|
||||
"nvenc_opengl_vulkan_on_dxgi_desc": "Sunshine kann OpenGL und Vulkan Programme nicht mit voller Bildwiederholrate erfassen, es sei denn, sie sind auf DXGI vorhanden. Dies ist eine systemweite Einstellung, die beim Beenden des Sonnenscheinprogramms rückgängig gemacht wird.",
|
||||
"nvenc_preset": "Leistungsvorgabe",
|
||||
"nvenc_preset_1": "(schnellste, Standard)",
|
||||
"nvenc_preset_7": "(langsamste)",
|
||||
"nvenc_preset_desc": "Höhere Zahlen verbessern die Komprimierung (Qualität bei der angegebenen Bitrate) auf Kosten einer erhöhten Kodierungslatenz. Wird empfohlen, nur zu ändern, wenn durch Netzwerk oder Decoder begrenzt, sonst kann ein ähnlicher Effekt durch Erhöhung der Bitrate erreicht werden.",
|
||||
"nvenc_realtime_hags": "Echtzeit-Priorität in der Hardware-beschleunigten gpu Planung verwenden",
|
||||
"nvenc_realtime_hags_desc": "Derzeit können NVIDIA-Treiber im Encoder einfrieren, wenn HAGS aktiviert ist, Echtzeit-Priorität verwendet wird und die VRAM-Auslastung fast fast erreicht ist. Die Deaktivierung dieser Option senkt die Priorität auf hoch, indem das Einfrieren auf Kosten einer reduzierten Aufnahmeleistung umgangen wird, wenn die GPU stark belastet ist.",
|
||||
"nvenc_spatial_aq": "Spatial AQ",
|
||||
"nvenc_spatial_aq_desc": "Zuweisen von höheren QP-Werten zu flachen Regionen des Videos. Wird empfohlen zu aktivieren, wenn Streaming mit niedrigeren Bitraten.",
|
||||
"nvenc_spatial_aq_disabled": "Deaktiviert (schneller, Standard)",
|
||||
"nvenc_spatial_aq_enabled": "Aktiviert (langsamer)",
|
||||
"nvenc_twopass": "Zwei-Pass-Modus",
|
||||
"nvenc_twopass_desc": "Fügt vorläufige Kodierungen hinzu. Dies erlaubt es, mehr Bewegungsvektoren zu erkennen, eine bessere Verteilung der Bitrate über den Rahmen und strengere Einhaltung der Bitratengrenzen. Die Deaktivierung ist nicht empfehlenswert, da dies gelegentlich zu Bitraten-Overshoot und anschließendem Paketverlust führen kann.",
|
||||
"nvenc_twopass_disabled": "Deaktiviert (schnellste, nicht empfohlen)",
|
||||
"nvenc_twopass_full_res": "Vollständige Auflösung (langsamer)",
|
||||
"nvenc_twopass_quarter_res": "Viertelauflösung (schneller, Standard)",
|
||||
"nvenc_vbv_increase": "Prozentsatz Erhöhung des Einzelbild-VBV/HRD",
|
||||
"nvenc_vbv_increase_desc": "Standardmäßig verwendet Sunshine Einzelbild-VBV/HRD, was bedeutet, dass jegliche kodierte Videobild-Größe nicht voraussichtlich die angeforderte Bitrate überschreiten wird, geteilt durch angeforderte Bildrate. Diese Einschränkung zu lockern, kann vorteilhaft sein und als variable Bitrate mit niedriger Latenz fungieren kann aber auch zu Paketverlusten führen, wenn das Netzwerk keinen Pufferkopf hat, um mit Bitraten-Spitzen umzugehen. Maximal zulässiger Wert ist 400, was einer 5x erhöhten Begrenzung der kodierten Videorahmen.",
|
||||
"origin_web_ui_allowed": "Ursprungsweb-UI erlaubt",
|
||||
"origin_web_ui_allowed_desc": "Der Ursprung der Remote-Endpunkt-Adresse, der der Zugriff auf das Web-Interface nicht verweigert wird",
|
||||
"origin_web_ui_allowed_lan": "Nur LAN-Nutzer können auf Web-UI zugreifen",
|
||||
"origin_web_ui_allowed_pc": "Nur localhost kann auf Web-UI zugreifen",
|
||||
"origin_web_ui_allowed_wan": "Jeder kann auf Web-UI zugreifen",
|
||||
"output_name_desc_linux": "Beim Start von Sunshine sollten Sie die Liste der erkannten Monitore sehen. Sie müssen den Wert vor dem Doppelpunkt in der Ausgabe verwenden. z.B.:",
|
||||
"output_name_desc_win": "Legen Sie eine Anzeige für die Aufnahme manuell fest. Wenn diese nicht aktiviert ist, wird die primäre Anzeige aufgenommen. Hinweis: Wenn Sie eine GPU oben angegeben haben, muss diese Anzeige mit dieser GPU verbunden sein. Die entsprechenden Werte finden Sie mit dem folgenden Befehl:",
|
||||
"output_name_linux": "Nummer überwachen",
|
||||
"output_name_win": "Ausgabename",
|
||||
"ping_timeout": "Ping-Timeout",
|
||||
"ping_timeout_desc": "Wie lange warten Sie in Millisekunden auf Daten von Mondlicht bevor Sie den Strom herunterfahren",
|
||||
"pkey": "Privater Schlüssel",
|
||||
"pkey_desc": "Der private Schlüssel, der für das Web-UI- und Moonlight-Client-Paar verwendet wird. Für bestmögliche Kompatibilität sollte dies ein privater RSA-2048 Schlüssel sein.",
|
||||
"port": "Port",
|
||||
"port_alert_1": "Sonnenschein kann keine Ports unter 1024 benutzen!",
|
||||
"port_alert_2": "Ports über 65535 sind nicht verfügbar!",
|
||||
"port_desc": "Legen Sie die Familie der von Sunshine verwendeten Ports fest",
|
||||
"port_http_port_note": "Benutzen Sie diesen Port, um sich mit Moonlight zu verbinden.",
|
||||
"port_note": "Notiz",
|
||||
"port_port": "Port",
|
||||
"port_protocol": "Protocol",
|
||||
"port_tcp": "TCP",
|
||||
"port_udp": "UDP",
|
||||
"port_warning": "Das Web-Interface dem Internet zu übergeben, ist ein Sicherheitsrisiko! Fahren Sie auf eigene Gefahr!",
|
||||
"port_web_ui": "Web UI",
|
||||
"qp": "Quantifizierungsparameter",
|
||||
"qp_desc": "Einige Geräte unterstützen möglicherweise keine Constant Bit-Rate. Für diese Geräte wird stattdessen QP verwendet. Höhere Werte bedeuten mehr Kompression, aber weniger Qualität.",
|
||||
"qsv_coder": "QuickSync Coder (H264)",
|
||||
"qsv_preset": "QuickSync Preset",
|
||||
"qsv_preset_fast": "schneller (niedrigere Qualität)",
|
||||
"qsv_preset_faster": "schnellste (niedrigste Qualität)",
|
||||
"qsv_preset_medium": "medium (Standard)",
|
||||
"qsv_preset_slow": "langsam (gute Qualität)",
|
||||
"qsv_preset_slower": "langsamer (bessere Qualität)",
|
||||
"qsv_preset_slowest": "langsamste (beste Qualität)",
|
||||
"qsv_preset_veryfast": "schnellste (niedrigste Qualität)",
|
||||
"qsv_slow_hevc": "Langsame HEVC Encodierung erlauben",
|
||||
"qsv_slow_hevc_desc": "Dies kann HEVC-Kodierung auf älteren Intel GPUs ermöglichen, auf Kosten einer höheren GPU-Nutzung und schlechteren Performance.",
|
||||
"res_fps_desc": "Die von Sunshine beworbenen Anzeigenmodi Einige Versionen von Moonlight, wie Moonlight-nx (Switch), verlassen sich auf diese Listen, um sicherzustellen, dass die angeforderten Auflösungen und fps unterstützt werden. Diese Einstellung ändert nicht die Art und Weise, wie der Bildschirm an Mondlight gesendet wird.",
|
||||
"resolutions": "Angemeldete Auflösungen",
|
||||
"restart_note": "Sonnenschein wird neu gestartet, um Änderungen anzuwenden.",
|
||||
"sunshine_name": "Sonnenscheinname",
|
||||
"sunshine_name_desc": "Der von Mononlight angezeigte Name, falls nicht angegeben, wird der Hostname des PCs verwendet",
|
||||
"sw_preset": "SW-Voreinstellungen",
|
||||
"sw_preset_desc": "Optimieren Sie den Abgleich zwischen der Kodierungsgeschwindigkeit (kodierte Frames pro Sekunde) und der Komprimierungseffizienz (Qualität pro Bit im Bitstream). Standard ist überflüssig.",
|
||||
"sw_preset_fast": "schnell",
|
||||
"sw_preset_faster": "schneller",
|
||||
"sw_preset_medium": "mittel",
|
||||
"sw_preset_slow": "langsam",
|
||||
"sw_preset_slower": "langsamer",
|
||||
"sw_preset_superfast": "superschnell (Standard)",
|
||||
"sw_preset_ultrafast": "extrem schnell",
|
||||
"sw_preset_veryfast": "veryfast",
|
||||
"sw_preset_veryslow": "veryslow",
|
||||
"sw_tune": "SW Tune",
|
||||
"sw_tune_animation": "animation -- gut für Cartoons; verwendet höhere Deblocking und mehr Referenzrahmen",
|
||||
"sw_tune_desc": "Einstellmöglichkeiten, die nach der Voreinstellung angewendet werden. Standard ist Null.",
|
||||
"sw_tune_fastdecode": "fastdecode -- ermöglicht eine schnellere Dekodierung durch Deaktivieren bestimmter Filter",
|
||||
"sw_tune_film": "film -- verwenden für qualitativ hochwertige Filminhalte; senkt Deblocking",
|
||||
"sw_tune_grain": "korn -- bewahrt die Kornstruktur im alten, körnigen Filmmaterial",
|
||||
"sw_tune_stillimage": "stillimage -- gut für slideshow-ähnliche Inhalte",
|
||||
"sw_tune_zerolatency": "Zerolatency -- gut für schnelle Kodierung und Low-Latency Streaming (Standard)",
|
||||
"touchpad_as_ds4": "Ein DS4 Gamepad emulieren, wenn der Client Gamepad meldet, dass ein Touchpad vorhanden ist",
|
||||
"touchpad_as_ds4_desc": "Wenn deaktiviert, wird das Touchpad-Vorhandensein bei der Auswahl des Gamepad-Typs nicht berücksichtigt.",
|
||||
"upnp": "UPnP",
|
||||
"upnp_desc": "Portweiterleitung für Streaming über das Internet automatisch konfigurieren",
|
||||
"virtual_sink": "Virtueller Sink",
|
||||
"virtual_sink_desc": "Legen Sie ein virtuelles Audiogerät manuell fest. Wenn nicht gesetzt, wird das Gerät automatisch ausgewählt. Wir empfehlen dringend, dieses Feld leer zu lassen, um die automatische Geräteauswahl zu verwenden!",
|
||||
"virtual_sink_placeholder": "Steam Streaming Lautsprecher",
|
||||
"vt_coder": "VideoToolbox Coder",
|
||||
"vt_realtime": "VideoToolbox Echtzeit-Codierung",
|
||||
"vt_software": "VideoToolbox Software Encoding",
|
||||
"vt_software_allowed": "Zulässig",
|
||||
"vt_software_forced": "Erzwungen",
|
||||
"wan_encryption_mode": "WAN-Verschlüsselungsmodus",
|
||||
"wan_encryption_mode_1": "Aktiviert für unterstützte Clients (Standard)",
|
||||
"wan_encryption_mode_2": "Benötigt für alle Kunden",
|
||||
"wan_encryption_mode_desc": "Dies legt fest, wann Verschlüsselung beim Streaming über das Internet verwendet wird. Verschlüsselung kann die Streaming-Leistung senken, insbesondere auf weniger leistungsfähigen Hosts und Clients."
|
||||
},
|
||||
"index": {
|
||||
"description": "Sunshine ist ein selbst gehosteter Game-Stream-Host für Moonlight.",
|
||||
"download": "Download",
|
||||
"loading_latest": "Lade neueste Version...",
|
||||
"new_nightly": "Eine neue <b>Nachts</b> Version ist verfügbar!",
|
||||
"new_stable": "Eine neue <b>Stable</b> Version ist verfügbar!",
|
||||
"startup_errors": "<b>Achtung!</b> Sunshine erkannte diese Fehler während des Starts. Wir <b>STRONGLY EMPFOHLEN</b> beheben sie vor dem Streaming.",
|
||||
"version_dirty": "Vielen Dank, dass Sie dazu beigetragen haben, Sunshine zu einer besseren Software zu machen!",
|
||||
"version_latest": "Du verwendest die neueste Version von Sunshine",
|
||||
"welcome": "Hallo, Sonnenschein!"
|
||||
},
|
||||
"navbar": {
|
||||
"applications": "Anwendungen",
|
||||
"configuration": "Konfiguration",
|
||||
"home": "Zuhause",
|
||||
"password": "Passwort ändern",
|
||||
"pin": "Pin",
|
||||
"troubleshoot": "Fehlerbehebung"
|
||||
},
|
||||
"password": {
|
||||
"confirm_password": "Passwort bestätigen",
|
||||
"current_creds": "Aktuelle Zugangsdaten",
|
||||
"new_creds": "Neue Zugangsdaten",
|
||||
"new_username_desc": "Wenn nicht angegeben, wird der Benutzername nicht geändert",
|
||||
"password_change": "Passwortänderung",
|
||||
"success_msg": "Passwort wurde erfolgreich geändert! Diese Seite wird bald neu geladen, Ihr Browser wird Sie nach den neuen Zugangsdaten fragen."
|
||||
},
|
||||
"pin": {
|
||||
"pair_failure": "Paarung fehlgeschlagen: Prüfen Sie, ob die PIN korrekt eingegeben wurde",
|
||||
"pair_success": "Erfolgreich! Bitte überprüfe Mondlicht um fortzufahren",
|
||||
"pin_pairing": "PIN Pairing",
|
||||
"send": "Senden",
|
||||
"warning_msg": "Stellen Sie sicher, dass Sie Zugriff auf den Client haben, mit dem Sie sich verbinden. Diese Software kann Ihrem Computer die totale Kontrolle geben, also seien Sie vorsichtig!"
|
||||
},
|
||||
"resource_card": {
|
||||
"github_discussions": "GitHub Discussions",
|
||||
"legal": "Rechtlich",
|
||||
"legal_desc": "Durch die Weiterverwendung dieser Software erklären Sie sich mit den Nutzungsbedingungen in den folgenden Dokumenten einverstanden.",
|
||||
"license": "Lizenz",
|
||||
"lizardbyte_website": "LizardByte Webseite",
|
||||
"resources": "Ressourcen",
|
||||
"resources_desc": "Ressourcen für Sonnenschein!",
|
||||
"third_party_notice": "Drittanbieter-Mitteilung"
|
||||
},
|
||||
"troubleshooting": {
|
||||
"force_close": "Schließen erzwingen",
|
||||
"force_close_desc": "Wenn sich Moonlight über eine aktuell laufende App beschwert, sollte das Schließen der App das Problem beheben.",
|
||||
"force_close_error": "Fehler beim Schließen der Anwendung",
|
||||
"force_close_success": "Anwendung erfolgreich geschlossen!",
|
||||
"logs": "Logs",
|
||||
"logs_desc": "Siehe die Logs hochgeladen von Sunshine",
|
||||
"logs_find": "Suchen...",
|
||||
"restart_sunshine": "Sonnenschein neu starten",
|
||||
"restart_sunshine_desc": "Wenn Sunshine nicht richtig funktioniert, können Sie versuchen, es neu zu starten. Dies wird alle laufenden Sitzungen beenden.",
|
||||
"restart_sunshine_success": "Sonnenschein wird neu gestartet",
|
||||
"troubleshooting": "Fehlerbehebung",
|
||||
"unpair_all": "Alle trennen",
|
||||
"unpair_all_desc": "Entferne alle deine gekoppelten Geräte",
|
||||
"unpair_all_error": "Fehler beim Entkoppeln",
|
||||
"unpair_all_success": "Erfolgreich getrennt!"
|
||||
},
|
||||
"welcome": {
|
||||
"confirm_password": "Passwort bestätigen",
|
||||
"create_creds": "Bevor Sie loslegen, müssen Sie einen neuen Benutzernamen und ein neues Passwort für den Zugriff auf die Web-Oberfläche erstellen.",
|
||||
"create_creds_alert": "Die unten angegebenen Anmeldedaten werden benötigt, um auf das Webinterface von Sunshine zuzugreifen. Halten Sie sie sicher, da Sie sie nie wieder sehen werden!",
|
||||
"greeting": "Willkommen bei Sunshine!",
|
||||
"login": "Anmelden",
|
||||
"welcome_success": "Diese Seite wird bald neu geladen, Ihr Browser wird Sie nach den neuen Anmeldeinformationen fragen"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user