mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-08-10 00:52:16 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
483e2259d4 | ||
|
|
972f726ff9 | ||
|
|
f169c6d116 | ||
|
|
70674325ba | ||
|
|
57a722a3fb | ||
|
|
1ab1b7920e | ||
|
|
44f1984af0 | ||
|
|
4b2f5befce | ||
|
|
c222e343b0 | ||
|
|
648df66b98 | ||
|
|
21eb4eb6dd | ||
|
|
79cf382cd9 | ||
|
|
be74b740f5 | ||
|
|
014ca7e8a1 | ||
|
|
e1fddcc99c | ||
|
|
3791edcec1 | ||
|
|
1dfe9ea0f6 | ||
|
|
e18e4f433b | ||
|
|
8c86baf627 | ||
|
|
c2fba6f651 | ||
|
|
6a914f7016 | ||
|
|
6f02274dc4 | ||
|
|
bf24d0e7a6 | ||
|
|
9b0e0565b1 | ||
|
|
a487fb31ea | ||
|
|
c6548f4271 | ||
|
|
455155a1c9 | ||
|
|
a0d7250c33 | ||
|
|
8162d5f0d0 | ||
|
|
5cdc0accad | ||
|
|
79991654d0 | ||
|
|
c820f0a670 | ||
|
|
744b54ffd4 | ||
|
|
a192c504cd | ||
|
|
e20ca7a8f0 | ||
|
|
7e9b18458d | ||
|
|
d85b234f1b | ||
|
|
afc6966f10 | ||
|
|
014d693112 | ||
|
|
237f21573b | ||
|
|
27c9c0e521 | ||
|
|
fbe5e2486f | ||
|
|
21a728ccd2 | ||
|
|
a72b77de71 | ||
|
|
80aa61b6e4 | ||
|
|
6d54356166 | ||
|
|
dfb5293224 | ||
|
|
bf4ed899d9 | ||
|
|
c29c917474 | ||
|
|
889b93da2d | ||
|
|
c3f3e1606d | ||
|
|
8f1465d950 | ||
|
|
c89fd83040 | ||
|
|
91f5d39540 | ||
|
|
8227e8f8e5 | ||
|
|
7d01b50498 | ||
|
|
f238cf5303 | ||
|
|
31885434f2 | ||
|
|
a1e6f441e4 | ||
|
|
4ac8c5c8ec | ||
|
|
845749d525 | ||
|
|
9e0c72e45d | ||
|
|
e229d80466 | ||
|
|
0dfbcfdbc4 | ||
|
|
e113ac6918 | ||
|
|
77deff12a6 | ||
|
|
e5d5256aed | ||
|
|
956341930b | ||
|
|
032680ca9d | ||
|
|
446825b73d | ||
|
|
0d100a57a6 | ||
|
|
55a225d21c | ||
|
|
ae12424279 | ||
|
|
3c223eb289 | ||
|
|
9ac1e3dcd6 | ||
|
|
a21e231cae | ||
|
|
2c4e293e21 | ||
|
|
1b45b57d07 | ||
|
|
905904960d | ||
|
|
48559a5876 | ||
|
|
d2461e1908 | ||
|
|
f8819d32e3 | ||
|
|
09dff34105 | ||
|
|
16e2789197 | ||
|
|
08ac580bc6 | ||
|
|
cdfcdf2dc7 | ||
|
|
fb7c9e22ff |
@@ -7,7 +7,7 @@
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
@@ -18,8 +18,9 @@ AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AlignTrailingComments: false
|
||||
AlwaysBreakAfterReturnType: All
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
@@ -37,32 +38,32 @@ BraceWrapping:
|
||||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 2
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: 2
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: Never
|
||||
SpaceBeforeCtorInitializerColon: false
|
||||
SpaceBeforeInheritanceColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# ignore git files
|
||||
.git*
|
||||
|
||||
# ignore hidden files
|
||||
.*
|
||||
|
||||
# do not ignore .git, needed for versioning
|
||||
!/.git
|
||||
|
||||
# ignore repo directories and files
|
||||
docs/
|
||||
scripts/
|
||||
|
||||
49
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
49
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -57,6 +57,8 @@ body:
|
||||
- macOS
|
||||
- Windows
|
||||
- other, n/a
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os-version
|
||||
attributes:
|
||||
@@ -73,11 +75,13 @@ body:
|
||||
- 64 bit
|
||||
- arm
|
||||
- other, n/a
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Sunshine commit or version
|
||||
placeholder: eg. 0.16.0
|
||||
placeholder: eg. 0.18.0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@@ -87,20 +91,28 @@ body:
|
||||
description: The package you installed
|
||||
options:
|
||||
- Linux - AppImage
|
||||
- Linux - AUR
|
||||
- Linux - 20.04-deb
|
||||
- Linux - 22.04-deb
|
||||
- Linux - AUR (Third Party)
|
||||
- Linux - deb
|
||||
- Linux - Docker
|
||||
- Linux - flatpak
|
||||
- Linux - nixpkgs (Third Party)
|
||||
- Linux - PKGBUILD
|
||||
- Linux - pkg.tar.zst
|
||||
- Linux - rpm
|
||||
- Linux - solus (Third Party)
|
||||
- macOS - dmg
|
||||
- macOS - Portfile
|
||||
- macOS - pkg
|
||||
- Windows - Chocolatey (Third Party)
|
||||
- Windows - installer
|
||||
- Windows - portable
|
||||
- Windows - Scoop (Third Party)
|
||||
- Windows - Winget (Third Party)
|
||||
- other (not listed)
|
||||
- other (self built)
|
||||
- other (fork of this repo)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: graphics_type
|
||||
attributes:
|
||||
@@ -111,6 +123,8 @@ body:
|
||||
- Intel
|
||||
- Nvidia
|
||||
- none (software encoding)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: graphics_model
|
||||
attributes:
|
||||
@@ -135,6 +149,24 @@ body:
|
||||
placeholder: e.g. PipeWire/KVM/X11/KMS
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: Config
|
||||
description: |
|
||||
Please copy and paste your config (`sunshine.conf`) file.
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: apps
|
||||
attributes:
|
||||
label: Apps
|
||||
description: |
|
||||
If this is an issue with launching a game or app, please copy and paste your `apps.json` file.
|
||||
render: json
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
@@ -142,9 +174,6 @@ body:
|
||||
description: |
|
||||
Please copy and paste any relevant log output. This will be automatically formatted into code,
|
||||
so no need for backticks.
|
||||
render: Shell
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Make sure to close your issue when it's solved! If you found the solution yourself please comment
|
||||
so that others benefit from it.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -9,5 +9,5 @@ contact_links:
|
||||
url: https://app.lizardbyte.dev/support
|
||||
about: Official LizardByte support
|
||||
- name: Feature request
|
||||
url: https://app.lizardbyte.dev/feedback
|
||||
about: Share your suggestions or ideas to help us improve
|
||||
url: https://ideas.moonlight-stream.org
|
||||
about: Share your suggestions or ideas to help Moonlight and Sunshine improve
|
||||
|
||||
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@@ -29,7 +29,7 @@ updates:
|
||||
target-branch: "nightly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "pip"
|
||||
- package-ecosystem: "nuget"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -37,10 +37,18 @@ updates:
|
||||
target-branch: "nightly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "gitsubmodule"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "10:00"
|
||||
target-branch: "nightly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "gitsubmodule"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "10:30"
|
||||
target-branch: "nightly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
140
.github/workflows/CI.yml
vendored
140
.github/workflows/CI.yml
vendored
@@ -46,6 +46,7 @@ jobs:
|
||||
last_version: ${{ steps.verify_changelog.outputs.latest_release_tag_name }}
|
||||
release_body: ${{ steps.verify_changelog.outputs.changelog_parser_description }}
|
||||
|
||||
# todo - remove this job once versioning is fully automated by cmake
|
||||
check_versions:
|
||||
name: Check Versions
|
||||
runs-on: ubuntu-latest
|
||||
@@ -147,109 +148,6 @@ jobs:
|
||||
outputs:
|
||||
matrix: ${{ steps.flatpak_matrix.outputs.matrix }}
|
||||
|
||||
build_linux_aur:
|
||||
name: Linux AUR
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup_release
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Dependencies Linux AUR
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
cmake
|
||||
|
||||
- name: Configure PKGBUILD files
|
||||
id: prepare
|
||||
run: |
|
||||
# variables for manifest
|
||||
aur_publish=false
|
||||
aur_pkg=sunshine-dev
|
||||
sub_version=""
|
||||
conflicts="'sunshine'"
|
||||
provides="'sunshine'"
|
||||
|
||||
branch=${GITHUB_HEAD_REF}
|
||||
|
||||
# check the branch variable
|
||||
if [ -z "$branch" ]; then
|
||||
echo "This is a PUSH event"
|
||||
commit=${{ github.sha }}
|
||||
clone_url=${{ github.event.repository.clone_url }}
|
||||
|
||||
if [[ ${{ github.ref == 'refs/heads/master' }} == true ]]; then
|
||||
echo "This is a main release event"
|
||||
aur_publish=true
|
||||
aur_pkg=sunshine
|
||||
conflicts=""
|
||||
provides=""
|
||||
elif [[ ${{ github.ref == 'refs/heads/nightly' }} == true ]]; then
|
||||
echo "This is a nightly release event"
|
||||
sub_version=".r${commit}"
|
||||
fi
|
||||
else
|
||||
echo "This is a PR event"
|
||||
commit=${{ github.event.pull_request.head.sha }}
|
||||
clone_url=${{ github.event.pull_request.head.repo.clone_url }}
|
||||
|
||||
sub_version=".r${commit}"
|
||||
fi
|
||||
echo "Commit: ${commit}"
|
||||
echo "Clone URL: ${clone_url}"
|
||||
|
||||
echo "aur_publish=${aur_publish}" >> $GITHUB_OUTPUT
|
||||
echo "aur_pkg=${aur_pkg}" >> $GITHUB_OUTPUT
|
||||
|
||||
mkdir -p artifacts
|
||||
mkdir -p build
|
||||
|
||||
cd build
|
||||
cmake -DSUNSHINE_CONFIGURE_AUR=ON \
|
||||
-DSUNSHINE_AUR_PKG=${aur_pkg} \
|
||||
-DSUNSHINE_SUB_VERSION=${sub_version} \
|
||||
-DSUNSHINE_AUR_CONFLICTS=${conflicts} \
|
||||
-DSUNSHINE_AUR_PROVIDES=${provides} \
|
||||
-DGITHUB_CLONE_URL=${clone_url} \
|
||||
-DGITHUB_COMMIT=${commit} \
|
||||
-DSUNSHINE_CONFIGURE_ONLY=ON \
|
||||
..
|
||||
cd ..
|
||||
|
||||
mv ./build/PKGBUILD ./artifacts/
|
||||
|
||||
- name: Validate package
|
||||
uses: LizardByte/archlinux-package-action@master
|
||||
with:
|
||||
path: artifacts
|
||||
flags: '--syncdeps --noconfirm'
|
||||
namcap: true
|
||||
srcinfo: true
|
||||
aur: true # workaround mirror problem
|
||||
|
||||
- name: Upload Artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sunshine-linux-aur
|
||||
path: artifacts/
|
||||
|
||||
- name: Publish AUR package
|
||||
if: ${{ steps.prepare.outputs.aur_publish == 'true' }}
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v2.6.0
|
||||
with:
|
||||
pkgname: ${{ steps.prepare.outputs.aur_pkg }}
|
||||
pkgbuild: ./artifacts/PKGBUILD
|
||||
assets: |
|
||||
./artifacts/*
|
||||
commit_username: ${{ secrets.AUR_USERNAME }}
|
||||
commit_email: ${{ secrets.AUR_EMAIL }}
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: Automatic update from GitHub ${{ github.repository }} per ${{ github.ref }}
|
||||
allow_empty_commits: false
|
||||
|
||||
build_linux_flatpak:
|
||||
name: Linux Flatpak
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -264,7 +162,7 @@ jobs:
|
||||
|
||||
- name: Setup Dependencies Linux Flatpak
|
||||
run: |
|
||||
PLATFORM_VERSION=21.08
|
||||
PLATFORM_VERSION=22.08
|
||||
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y \
|
||||
@@ -384,6 +282,7 @@ jobs:
|
||||
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-thread1.71-dev \
|
||||
@@ -410,6 +309,7 @@ jobs:
|
||||
sudo apt-get install -y \
|
||||
cmake \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-thread-dev \
|
||||
libboost-program-options-dev
|
||||
@@ -419,6 +319,7 @@ jobs:
|
||||
build-essential \
|
||||
gcc-10 \
|
||||
g++-10 \
|
||||
libappindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libcap-dev \
|
||||
libcurl4-openssl-dev \
|
||||
@@ -463,6 +364,10 @@ jobs:
|
||||
sudo rm /root/cuda.run
|
||||
|
||||
- name: Build Linux
|
||||
env:
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
|
||||
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
mkdir -p build
|
||||
mkdir -p artifacts
|
||||
@@ -580,12 +485,16 @@ jobs:
|
||||
- name: Setup Dependencies MacOS
|
||||
run: |
|
||||
# install dependencies using homebrew
|
||||
brew install boost cmake curl node opus
|
||||
brew install boost cmake curl node opus pkg-config
|
||||
|
||||
# fix openssl header not found
|
||||
ln -sf /usr/local/opt/openssl/include/openssl /usr/local/include/openssl
|
||||
|
||||
- name: Build MacOS
|
||||
env:
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
|
||||
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
npm install
|
||||
|
||||
@@ -691,7 +600,8 @@ jobs:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DGITHUB_COMMIT=${commit} \
|
||||
cmake \
|
||||
-DGITHUB_COMMIT=${commit} \
|
||||
-DGITHUB_CLONE_URL=${clone_url} \
|
||||
-DSUNSHINE_CONFIGURE_PORTFILE=ON \
|
||||
-DSUNSHINE_CONFIGURE_ONLY=ON \
|
||||
@@ -742,6 +652,8 @@ jobs:
|
||||
echo "subportlist=${subportlist}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run port lint for all subports
|
||||
env:
|
||||
subportlist: ${{ steps.subportlist.outputs.subportlist }}
|
||||
run: |
|
||||
set -eu
|
||||
fail=0
|
||||
@@ -763,10 +675,10 @@ jobs:
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit "$fail"
|
||||
env:
|
||||
subportlist: ${{ steps.subportlist.outputs.subportlist }}
|
||||
|
||||
- name: Build subports
|
||||
env:
|
||||
subportlist: ${{ steps.subportlist.outputs.subportlist }}
|
||||
run: |
|
||||
set -eu
|
||||
fail=0
|
||||
@@ -815,8 +727,6 @@ jobs:
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit "$fail"
|
||||
env:
|
||||
subportlist: ${{ steps.subportlist.outputs.subportlist }}
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
@@ -896,6 +806,10 @@ jobs:
|
||||
|
||||
- name: Build Windows
|
||||
shell: msys2 {0}
|
||||
env:
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
BUILD_VERSION: ${{ needs.check_changelog.outputs.next_version_bare }}
|
||||
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
@@ -916,8 +830,8 @@ jobs:
|
||||
cpack -G ZIP
|
||||
|
||||
# move
|
||||
mv ./cpack_artifacts/Sunshine.exe ../artifacts/sunshine-windows.exe
|
||||
mv ./cpack_artifacts/Sunshine.zip ../artifacts/sunshine-windows.zip
|
||||
mv ./cpack_artifacts/Sunshine.exe ../artifacts/sunshine-windows-installer.exe
|
||||
mv ./cpack_artifacts/Sunshine.zip ../artifacts/sunshine-windows-portable.zip
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -946,7 +860,7 @@ jobs:
|
||||
runs-on: windows-latest # the required action can only be run on Windows
|
||||
steps:
|
||||
- name: Release to WinGet
|
||||
uses: vedantmgoyal2009/winget-releaser@v1
|
||||
uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: LizardByte.Sunshine
|
||||
release-tag: ${{ needs.setup_release.outputs.release_tag }}
|
||||
|
||||
35
.github/workflows/ci-docker.yml
vendored
35
.github/workflows/ci-docker.yml
vendored
@@ -205,9 +205,20 @@ jobs:
|
||||
# get branch name
|
||||
BRANCH=${GITHUB_HEAD_REF}
|
||||
|
||||
RELEASE=false
|
||||
|
||||
if [ -z "$BRANCH" ]; then
|
||||
echo "This is a PUSH event"
|
||||
BRANCH=${{ github.ref_name }}
|
||||
COMMIT=${{ github.sha }}
|
||||
CLONE_URL=${{ github.event.repository.clone_url }}
|
||||
if [[ $BRANCH == "master" ]]; then
|
||||
RELEASE=true
|
||||
fi
|
||||
else
|
||||
echo "This is a PULL REQUEST event"
|
||||
COMMIT=${{ github.event.pull_request.head.sha }}
|
||||
CLONE_URL=${{ github.event.pull_request.head.repo.clone_url }}
|
||||
fi
|
||||
|
||||
# determine to push image to dockerhub and ghcr or not
|
||||
@@ -220,7 +231,6 @@ jobs:
|
||||
# setup the tags
|
||||
REPOSITORY=${{ github.repository }}
|
||||
BASE_TAG=$(echo $REPOSITORY | tr '[:upper:]' '[:lower:]')
|
||||
COMMIT=${{ github.sha }}
|
||||
|
||||
TAGS="${BASE_TAG}:${COMMIT:0:7}${{ matrix.tag }},ghcr.io/${BASE_TAG}:${COMMIT:0:7}${{ matrix.tag }}"
|
||||
|
||||
@@ -259,7 +269,10 @@ jobs:
|
||||
elif [[ $line == "# artifacts: "* && $ARTIFACTS == "" ]]; then
|
||||
# echo the line and use `sed` to remove the custom directive
|
||||
ARTIFACTS=$(echo -e "$line" | sed 's/# artifacts: //')
|
||||
elif [[ $PLATFORMS != "" && $ARTIFACTS != "" ]]; then
|
||||
elif [[ $line == "# no-cache-filters: "* && $NO_CACHE_FILTERS == "" ]]; then
|
||||
# echo the line and use `sed` to remove the custom directive
|
||||
NO_CACHE_FILTERS=$(echo -e "$line" | sed 's/# no-cache-filters: //')
|
||||
elif [[ $PLATFORMS != "" && $ARTIFACTS != "" && $NO_CACHE_FILTERS != "" ]]; then
|
||||
# break while loop once all custom directives are found
|
||||
break
|
||||
fi
|
||||
@@ -277,7 +290,10 @@ jobs:
|
||||
echo "branch=${BRANCH}" >> $GITHUB_OUTPUT
|
||||
echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "commit=${COMMIT}" >> $GITHUB_OUTPUT
|
||||
echo "clone_url=${CLONE_URL}" >> $GITHUB_OUTPUT
|
||||
echo "release=${RELEASE}" >> $GITHUB_OUTPUT
|
||||
echo "artifacts=${ARTIFACTS}" >> $GITHUB_OUTPUT
|
||||
echo "no_cache_filters=${NO_CACHE_FILTERS}" >> $GITHUB_OUTPUT
|
||||
echo "platforms=${PLATFORMS}" >> $GITHUB_OUTPUT
|
||||
echo "push=${PUSH}" >> $GITHUB_OUTPUT
|
||||
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
|
||||
@@ -315,7 +331,7 @@ jobs:
|
||||
- name: Build artifacts
|
||||
if: ${{ steps.prepare.outputs.artifacts == 'true' }}
|
||||
id: build_artifacts
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: ./
|
||||
file: ${{ matrix.dockerfile }}
|
||||
@@ -328,13 +344,16 @@ jobs:
|
||||
BUILD_DATE=${{ steps.prepare.outputs.build_date }}
|
||||
BUILD_VERSION=${{ needs.check_changelog.outputs.next_version }}
|
||||
COMMIT=${{ steps.prepare.outputs.commit }}
|
||||
CLONE_URL=${{ steps.prepare.outputs.clone_url }}
|
||||
RELEASE=${{ steps.prepare.outputs.release }}
|
||||
tags: ${{ steps.prepare.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
no-cache-filters: ${{ steps.prepare.outputs.no_cache_filters }}
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: ./
|
||||
file: ${{ matrix.dockerfile }}
|
||||
@@ -345,9 +364,12 @@ jobs:
|
||||
BUILD_DATE=${{ steps.prepare.outputs.build_date }}
|
||||
BUILD_VERSION=${{ needs.check_changelog.outputs.next_version }}
|
||||
COMMIT=${{ steps.prepare.outputs.commit }}
|
||||
CLONE_URL=${{ steps.prepare.outputs.clone_url }}
|
||||
RELEASE=${{ steps.prepare.outputs.release }}
|
||||
tags: ${{ steps.prepare.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
no-cache-filters: ${{ steps.prepare.outputs.no_cache_filters }}
|
||||
|
||||
- name: Arrange Artifacts
|
||||
if: ${{ steps.prepare.outputs.artifacts == 'true' }}
|
||||
@@ -358,11 +380,14 @@ jobs:
|
||||
# https://unix.stackexchange.com/a/52816
|
||||
find ./ -type f -exec mv -t ./ -n '{}' +
|
||||
|
||||
# remove provenance file
|
||||
rm -f ./provenance.json
|
||||
|
||||
- name: Upload Artifacts
|
||||
if: ${{ steps.prepare.outputs.artifacts == 'true' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sunshine${{ matrix.tag }}
|
||||
name: Docker${{ matrix.tag }}
|
||||
path: artifacts/
|
||||
|
||||
- name: Create/Update GitHub Release
|
||||
|
||||
288
.github/workflows/ci-qodana.yml
vendored
Normal file
288
.github/workflows/ci-qodana.yml
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
---
|
||||
# This action is centrally managed in https://github.com/<organization>/.github/
|
||||
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
|
||||
# the above-mentioned repo.
|
||||
|
||||
name: Qodana
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master, nightly]
|
||||
types: [opened, synchronize, reopened]
|
||||
push:
|
||||
branches: [master, nightly]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
qodana_initial_check:
|
||||
name: Qodana Initial Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
# check the branch variable
|
||||
if [ "${{ github.event_name }}" == "push" ]
|
||||
then
|
||||
echo "This is a PUSH event"
|
||||
# use the branch name
|
||||
destination=${{ github.ref_name }}
|
||||
target=${{ github.ref_name }}
|
||||
else
|
||||
echo "This is a PR event"
|
||||
# use the PR number
|
||||
destination=${{ github.event.pull_request.number }}
|
||||
target=${{ github.event.pull_request.base.ref }}
|
||||
fi
|
||||
|
||||
echo "checkout_repo=$checkout_repo" >> $GITHUB_OUTPUT
|
||||
echo "checkout_ref=$checkout_ref" >> $GITHUB_OUTPUT
|
||||
echo "destination=$destination" >> $GITHUB_OUTPUT
|
||||
echo "target=$target" >> $GITHUB_OUTPUT
|
||||
|
||||
# prepare urls
|
||||
base=https://${{ github.repository_owner }}.github.io
|
||||
report_url=${base}/qodana-reports/${{ github.event.repository.name }}/${destination}
|
||||
echo "report_url=$report_url" >> $GITHUB_OUTPUT
|
||||
|
||||
# build matrix
|
||||
files=$(find . -type f -iname "qodana*.yaml")
|
||||
|
||||
echo "files: ${files}"
|
||||
|
||||
# do not quote to keep this as a single line
|
||||
echo files=${files} >> $GITHUB_OUTPUT
|
||||
|
||||
MATRIX_COMBINATIONS=""
|
||||
REPORTS_MARKDOWN=""
|
||||
for FILE in ${files}; do
|
||||
# extract the language from file name after `qodana-` and before `.yaml`
|
||||
language=$(echo $FILE | sed -r -z -e 's/(\.\/)*.*\/(qodana.yaml)/default/gm')
|
||||
if [[ $language != "default" ]]; then
|
||||
language=$(echo $FILE | sed -r -z -e 's/(\.\/)*.*qodana-(.*).yaml/\2/gm')
|
||||
fi
|
||||
MATRIX_COMBINATIONS="$MATRIX_COMBINATIONS {\"file\": \"$FILE\", \"language\": \"$language\"},"
|
||||
REPORTS_MARKDOWN="$REPORTS_MARKDOWN <br> - [${language}](${report_url}/${language})"
|
||||
done
|
||||
|
||||
# removes the last character (i.e. comma)
|
||||
MATRIX_COMBINATIONS=${MATRIX_COMBINATIONS::-1}
|
||||
|
||||
# setup matrix for later jobs
|
||||
matrix=$((
|
||||
echo "{ \"include\": [$MATRIX_COMBINATIONS] }"
|
||||
) | jq -c .)
|
||||
|
||||
echo $matrix
|
||||
echo $matrix | jq .
|
||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "reports_markdown=$REPORTS_MARKDOWN" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup initial notification inputs
|
||||
id: inputs
|
||||
if: >-
|
||||
startsWith(github.event_name, 'pull_request') &&
|
||||
steps.prepare.outputs.files != ''
|
||||
run: |
|
||||
# workflow logs
|
||||
workflow_url_a=https://github.com/${{ github.repository_owner }}/${{ github.event.repository.name }}
|
||||
workflow_url=${workflow_url_a}/actions/runs/${{ github.run_id }}
|
||||
|
||||
# multiline message
|
||||
message=$(cat <<- EOF
|
||||
:warning: **Qodana is checking this PR** :warning:
|
||||
Live results available [here](${workflow_url})
|
||||
EOF
|
||||
)
|
||||
|
||||
# escape json control characters
|
||||
message=$(jq -n --arg message "$message" '$message' | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||
|
||||
secondary_inputs=$(echo '{
|
||||
"issue_message": "'"${message}"'",
|
||||
"issue_message_id": "'"qodana"'",
|
||||
"issue_number": "'"${{ github.event.number }}"'",
|
||||
"issue_repo_owner": "'"${{ github.repository_owner }}"'",
|
||||
"issue_repo_name": "'"${{ github.event.repository.name }}"'"
|
||||
}' | jq -r .)
|
||||
|
||||
#escape json control characters
|
||||
secondary_inputs=$(jq -n --arg secondary_inputs "$secondary_inputs" '$secondary_inputs' \
|
||||
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||
|
||||
echo $secondary_inputs
|
||||
|
||||
# secondary input as string, not JSON
|
||||
# todo - change dispatch_ref to master instead of nightly
|
||||
primary_inputs=$(echo '{
|
||||
"dispatch_repository": "'"${{ github.repository_owner }}/.github"'",
|
||||
"dispatch_workflow": "'"dispatch-issue-comment.yml"'",
|
||||
"dispatch_ref": "'"nightly"'",
|
||||
"dispatch_inputs": "'"${secondary_inputs}"'"
|
||||
}' | jq -c .)
|
||||
|
||||
echo $primary_inputs
|
||||
echo $primary_inputs | jq .
|
||||
echo "primary_inputs=$primary_inputs" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Workflow Dispatch
|
||||
if: >-
|
||||
startsWith(github.event_name, 'pull_request') &&
|
||||
steps.prepare.outputs.files != ''
|
||||
uses: benc-uk/workflow-dispatch@v1.2.2
|
||||
continue-on-error: true # this might error if the workflow is not found, but we still want to run the next job
|
||||
with:
|
||||
ref: ${{ github.base_ref || github.ref_name }} # base ref for PR and branch name for push
|
||||
workflow: dispatcher.yml
|
||||
inputs: ${{ steps.inputs.outputs.primary_inputs }}
|
||||
token: ${{ github.token }}
|
||||
|
||||
outputs:
|
||||
destination: ${{ steps.prepare.outputs.destination }}
|
||||
target: ${{ steps.prepare.outputs.target }}
|
||||
files: ${{ steps.prepare.outputs.files }}
|
||||
reports_markdown: ${{ steps.prepare.outputs.reports_markdown }}
|
||||
matrix: ${{ steps.prepare.outputs.matrix }}
|
||||
|
||||
qodana:
|
||||
if: ${{ needs.qodana_initial_check.outputs.files != '' }}
|
||||
needs: [qodana_initial_check]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.qodana_initial_check.outputs.matrix) }}
|
||||
name: Qodana-Scan-${{ matrix.language }}
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Get baseline
|
||||
id: baseline
|
||||
run: |
|
||||
# check if destination is not an integer
|
||||
if ! [[ "${{ needs.qodana_initial_check.outputs.destination }}" =~ ^[0-9]+$ ]]
|
||||
then
|
||||
echo "Running for a branch update"
|
||||
echo "baseline_args=" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Running for a PR"
|
||||
|
||||
sarif_file=qodana.sarif.json
|
||||
repo=${{ github.event.repository.name }}
|
||||
target=${{ needs.qodana_initial_check.outputs.target }}
|
||||
language=${{ matrix.language }}
|
||||
|
||||
baseline_file="${repo}/${target}/${language}/results/${sarif_file}"
|
||||
baseline_file_url="https://lizardbyte.github.io/qodana-reports/${baseline_file}"
|
||||
|
||||
# don't fail if file does not exist
|
||||
wget ${baseline_file_url} || true
|
||||
|
||||
# check if file exists
|
||||
if [ -f ${sarif_file} ]
|
||||
then
|
||||
echo "baseline exists"
|
||||
echo "baseline_args=--baseline,${sarif_file}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "baseline does not exist"
|
||||
echo "baseline_args=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Rename Qodana config file
|
||||
id: rename
|
||||
run: |
|
||||
# rename the file
|
||||
if [ "${{ matrix.file }}" != "./qodana.yaml" ]
|
||||
then
|
||||
mv -f ${{ matrix.file }} ./qodana.yaml
|
||||
fi
|
||||
|
||||
- name: Qodana
|
||||
id: qodana
|
||||
continue-on-error: true # ensure dispatch-qodana job is run
|
||||
uses: JetBrains/qodana-action@v2022.3.4
|
||||
with:
|
||||
additional-cache-hash: ${{ github.ref }}-${{ matrix.language }}
|
||||
artifact-name: qodana-${{ matrix.language }} # yamllint disable-line rule:line-length
|
||||
args: '--print-problems,${{ steps.baseline.outputs.baseline_args }}'
|
||||
pr-mode: false
|
||||
upload-result: true
|
||||
use-caches: true
|
||||
|
||||
- name: Set output status
|
||||
id: status
|
||||
run: |
|
||||
# check if qodana failed
|
||||
echo "qodana_status=${{ steps.qodana.outcome }}" >> $GITHUB_OUTPUT
|
||||
|
||||
outputs:
|
||||
qodana_status: ${{ steps.status.outputs.qodana_status }}
|
||||
|
||||
dispatch-qodana:
|
||||
# trigger qodana-reports to download artifacts from the matrix runs
|
||||
needs: [qodana_initial_check, qodana]
|
||||
runs-on: ubuntu-latest
|
||||
name: Dispatch Qodana
|
||||
if: ${{ needs.qodana_initial_check.outputs.files != '' }}
|
||||
steps:
|
||||
- name: Setup qodana publish inputs
|
||||
id: inputs
|
||||
run: |
|
||||
# get the artifacts
|
||||
artifacts=${{ toJson(steps.artifacts.outputs.result) }}
|
||||
artifacts=$(echo $artifacts | jq -c .)
|
||||
|
||||
# get the target branch
|
||||
target=${{ needs.qodana_initial_check.outputs.target }}
|
||||
|
||||
# get the destination branch
|
||||
destination=${{ needs.qodana_initial_check.outputs.destination }}
|
||||
|
||||
# client payload
|
||||
secondary_inputs=$(echo '{
|
||||
"destination": "'"${destination}"'",
|
||||
"ref": "'"${{ github.ref }}"'",
|
||||
"repo": "'"${{ github.repository }}"'",
|
||||
"repo_name": "'"${{ github.event.repository.name }}"'",
|
||||
"run_id": "'"${{ github.run_id }}"'",
|
||||
"reports_markdown": "'"${{ needs.qodana_initial_check.outputs.reports_markdown }}"'",
|
||||
"status": "'"${{ needs.qodana.outputs.qodana_status }}"'"
|
||||
}' | jq -r .)
|
||||
|
||||
#escape json control characters
|
||||
secondary_inputs=$(jq -n --arg secondary_inputs "$secondary_inputs" '$secondary_inputs' \
|
||||
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
|
||||
|
||||
echo $secondary_inputs
|
||||
|
||||
primary_inputs=$(echo '{
|
||||
"dispatch_repository": "'"${{ github.repository_owner }}/qodana-reports"'",
|
||||
"dispatch_workflow": "'"dispatch-qodana.yml"'",
|
||||
"dispatch_ref": "'"master"'",
|
||||
"dispatch_inputs": "'"$secondary_inputs"'"
|
||||
}' | jq -c .)
|
||||
|
||||
echo $primary_inputs
|
||||
echo $primary_inputs | jq .
|
||||
echo "primary_inputs=$primary_inputs" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Workflow Dispatch
|
||||
uses: benc-uk/workflow-dispatch@v1.2.2
|
||||
continue-on-error: true # this might error if the workflow is not found, but we don't want to fail the workflow
|
||||
with:
|
||||
ref: ${{ github.base_ref || github.ref_name }} # base ref for PR and branch name for push
|
||||
workflow: dispatcher.yml
|
||||
inputs: ${{ steps.inputs.outputs.primary_inputs }}
|
||||
token: ${{ github.token }}
|
||||
69
.github/workflows/dispatcher.yml
vendored
Normal file
69
.github/workflows/dispatcher.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
# This action is centrally managed in https://github.com/<organization>/.github/
|
||||
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
|
||||
# the above-mentioned repo.
|
||||
|
||||
# This action receives a dispatch event and passes it through to another repo. This is a workaround to avoid issues
|
||||
# where fork PRs do not have access to secrets.
|
||||
|
||||
name: Dispatcher
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dispatch_repository:
|
||||
description: 'Repository to dispatch to'
|
||||
required: true
|
||||
dispatch_workflow:
|
||||
description: 'Workflow to dispatch to'
|
||||
required: true
|
||||
dispatch_ref:
|
||||
description: 'Ref/branch to dispatch to'
|
||||
required: true
|
||||
dispatch_inputs:
|
||||
description: 'Inputs to send'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
dispatcher:
|
||||
name: Repository Dispatch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Unescape JSON control characters
|
||||
id: inputs
|
||||
run: |
|
||||
# get the inputs
|
||||
dispatch_inputs=${{ github.event.inputs.dispatch_inputs }}
|
||||
echo "$dispatch_inputs"
|
||||
|
||||
# temporarily replace newlines with a placeholder
|
||||
dispatch_inputs=$(echo ${dispatch_inputs} | sed 's/\\\\n/_!new_line!_/g')
|
||||
|
||||
# remove newline characters
|
||||
dispatch_inputs=$(echo ${dispatch_inputs} | sed 's/\\n//g')
|
||||
|
||||
# replace placeholder with newline
|
||||
dispatch_inputs=$(echo ${dispatch_inputs} | sed 's/_!new_line!_/\\n/g')
|
||||
|
||||
# replace escaped quotes with unescaped quotes
|
||||
dispatch_inputs=$(echo ${dispatch_inputs} | sed 's/\\"//g')
|
||||
|
||||
# debug echo
|
||||
echo "$dispatch_inputs"
|
||||
|
||||
# parse as JSON
|
||||
dispatch_inputs=$(echo "$dispatch_inputs" | jq -c .)
|
||||
|
||||
# debug echo
|
||||
echo "$dispatch_inputs"
|
||||
|
||||
echo "dispatch_inputs=$dispatch_inputs" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Workflow Dispatch
|
||||
uses: benc-uk/workflow-dispatch@v1.2.2
|
||||
with:
|
||||
repo: ${{ github.event.inputs.dispatch_repository }}
|
||||
ref: ${{ github.event.inputs.dispatch_ref || 'master' }} # default to master if not specified
|
||||
workflow: ${{ github.event.inputs.dispatch_workflow }}
|
||||
inputs: ${{ steps.inputs.outputs.dispatch_inputs }}
|
||||
token: ${{ secrets.GH_BOT_TOKEN || github.token }} # fallback to default token if not specified
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -46,3 +46,7 @@
|
||||
path = third-party/nanors
|
||||
url = https://github.com/sleepybishop/nanors.git
|
||||
branch = master
|
||||
[submodule "third-party/tray"]
|
||||
path = third-party/tray
|
||||
url = https://github.com/dmikushin/tray
|
||||
branch = master
|
||||
|
||||
191
CHANGELOG.md
191
CHANGELOG.md
@@ -1,17 +1,96 @@
|
||||
# Changelog
|
||||
|
||||
## [0.19.0] - 2023-03-29
|
||||
**Breaking**
|
||||
- (Linux/Flatpak) Moved Flatpak to org.freedesktop.Platform 22.08 and Cuda 12.0.0
|
||||
This will drop support for Nvidia GPUs with compute capability 3.5
|
||||
|
||||
**Added**
|
||||
- (Input) Added option to suppress input from gamepads, keyboards, or mice
|
||||
- (Input/Linux) Added unicode support for remote pasting (may not work on all DEs)
|
||||
- (Input/Linux) Added XTest input fallback
|
||||
- (UI) Added version notifications to web UI
|
||||
- (Linux/Windows) Add system tray icon
|
||||
- (Windows) Added ability to safely elevate commands that fail due to insufficient permissions when running as a service
|
||||
- (Config) Added global prep commands, and ability to exclude an app from using global prep commands
|
||||
- (Installer/Windows) Automatically install ViGEmBus if selected
|
||||
|
||||
**Changed**
|
||||
- (Logging) Changed client verified messages to debug to prevent spamming the log
|
||||
- (Config) Only save non default config values
|
||||
- (Service/Linux) Use xdg-desktop-autostart for systemd service
|
||||
- (Linux) Added config option to force capture method
|
||||
- (Windows) Execute prep command in context of current user
|
||||
- (Linux) Allow disconnected X11 outputs
|
||||
|
||||
**Fixed**
|
||||
- (Input/Windows) Fix issue where internation keys were not translated correct, and modifier keys appeared stuck
|
||||
- (Linux) Fixed startup when /dev/dri didn't exist
|
||||
- (UI) Changes software encoding settings to select menu instead of text input
|
||||
- (Initialization) Do not terminate upon failure, allowing access to the web UI
|
||||
|
||||
**Dependencies**
|
||||
- Bump third-party/moonlight-common-c from 07beb0f to c9426a6
|
||||
- Bump babel from 2.11.0 to 2.12.1
|
||||
- Bump @fortawesome/fontawesome-free from 6.2.1 to 6.4.0
|
||||
- Bump third-party/ViGEmClient from 9e842ba to 726404e
|
||||
- Bump ffmpeg
|
||||
- Bump third-party/miniupnp from 014c9df to e439318
|
||||
- Bump furo from 2022.12.7 to 2023.3.27
|
||||
- Bump third-party/nanors from 395e5ad to e9e242e
|
||||
|
||||
**Misc**
|
||||
- (GitHub) Shared feature request board with Moonlight
|
||||
- (Docs) Improved application examples
|
||||
- (Docs) Added WIP documentation for source code using Doxygen and Breathe
|
||||
- (Build) Fix linux clang build errors
|
||||
- (Build/Archlinux) Skip irrelevant submodules
|
||||
- (Build/Archlinux) Disable download timeout
|
||||
- (Build/macOS) Support compiling for earlier releases of macOS
|
||||
- (Docs) Add favicon
|
||||
- (Docs) Add missing config default values
|
||||
- (Build) Fix compiler warnings due to depreciated elements in C++17
|
||||
- (Build) Fix libcurl link errors
|
||||
- (Clang) Adjusted formatting rules
|
||||
|
||||
## [0.18.4] - 2023-02-20
|
||||
**Fixed**
|
||||
- (Linux/AUR) Drop support of AUR package
|
||||
- (Docker) General enhancements to docker images
|
||||
|
||||
## [0.18.3] - 2023-02-13
|
||||
**Added**
|
||||
- (Linux) Added PKGBUILD for Archlinux based distros to releases
|
||||
- (Linux) Added precompiled package for Archlinux based distros to releases
|
||||
- (Docker) Added archlinux docker image (x86_64 only)
|
||||
|
||||
## [0.18.2] - 2023-02-13
|
||||
**Fixed**
|
||||
- (Video/KMV/Linux) Fixed wayland capture on Nvidia for KMS
|
||||
- (Video/Linux) Implement vaSyncBuffer stuf for libva <2.9.0
|
||||
- (UI) Fix issue where mime type was not being set for node_modules when using a reverse proxy
|
||||
- (UI/macOS) Added missing audio sink config options
|
||||
- (Linux) Specify correct Boost dependency versions
|
||||
- (Video/AMF) Add missing encoder tunables
|
||||
|
||||
## [0.18.1] - 2023-01-31
|
||||
**Fixed**
|
||||
- (Linux) Fixed missing dependencies for deb and rpm packages
|
||||
- (Linux) Use dynamic boost
|
||||
|
||||
## [0.18.0] - 2023-01-29
|
||||
Attention, this release contains critical security fixes. Please update as soon as possible. Additionally, we are
|
||||
encouraging users to change your Sunshine password, especially if you expose the web UI (i.e. port 47790 by default)
|
||||
to the internet, or have ever uploaded your logs with verbose output to a public resource.
|
||||
|
||||
### Added
|
||||
**Added**
|
||||
- (Windows) Add support for Intel QuickSync
|
||||
- (Linux) Added aarch64 deb and rpm packages
|
||||
- (Windows) Add support for hybrid graphics systems, such as laptops with both integrated and discrete GPUs
|
||||
- (Linux) Add support for streaming from Steam Deck Gaming Mode
|
||||
- (Windows) Add HDR support, see https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/usage.html#hdr-support
|
||||
### Fixed
|
||||
|
||||
**Fixed**
|
||||
- (Network) Refactor code for UPnP port forwarding
|
||||
- (Video) Enforce 10 FPS encoding frame rate minimum to improve static image quality
|
||||
- (Linux) deb and rpm packages are now specific to destination distro and version
|
||||
@@ -35,18 +114,20 @@ location which is problematic when running as a service or on a multi-user syste
|
||||
as a service, games and applications were launched as SYSTEM. This could lead to issues with save files and other game
|
||||
settings. In v0.17.0, games now run under your user account without elevated privileges.
|
||||
|
||||
### Breaking
|
||||
**Breaking**
|
||||
- (Apps) Removed automatic desktop entry (Re-add by adding an empty application named "Desktop" with no commands, "desktop.png" can be added as the image.)
|
||||
- (Windows) Improved user upgrade experience (Suggest to manually uninstall existing Sunshine version before this upgrade. Do NOT select to remove everything, if prompted. Make a backup of config files before uninstall.)
|
||||
- (Windows) Move config files to specific directory (files will be migrated automatically if using Windows installer)
|
||||
- (Dependencies) Fix npm path (breaking change for package maintainers)
|
||||
### Added
|
||||
|
||||
**Added**
|
||||
- (macOS) Added initial support for arm64 on macOS through Macports portfile
|
||||
- (Input) Added support for foreign keyboard input
|
||||
- (Misc) Logs inside the WebUI and log to file
|
||||
- (UI/Windows) Added an Apply button to configuration page when running as a service
|
||||
- (Input/Windows) Enable Mouse Keys while streaming for systems with no physical mouse
|
||||
### Fixed
|
||||
|
||||
**Fixed**
|
||||
- (Video) Improved capture performance
|
||||
- (Audio) Improved audio bitrate and quality handling
|
||||
- (Apps/Windows) Fixed PATH environment variable handling
|
||||
@@ -70,7 +151,8 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- (Service/Windows) Self terminate/restart service if process hangs for 10 seconds
|
||||
- (Input/Windows) Fix Windows masked cursor blending with GPU encoders
|
||||
- (Video) Color conversion fixes and BT.2020 support
|
||||
### Dependencies
|
||||
|
||||
**Dependencies**
|
||||
- Bump ffmpeg from 4.4 to 5.1
|
||||
- ffmpeg_patches: add amfenc delay/buffering fix
|
||||
- CBS moved to ffmpeg submodules
|
||||
@@ -83,15 +165,17 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- Bump @fortawesome/fontawesome-free from 6.2.0 to 6.2.1
|
||||
|
||||
## [0.16.0] - 2022-12-13
|
||||
### Added
|
||||
**Added**
|
||||
- Add cover finder
|
||||
- (Docker) Add arm64 docker image
|
||||
- (Flatpak) Add installation helper scripts
|
||||
- (Windows) Add support for Unicode input messages
|
||||
### Fixed
|
||||
|
||||
**Fixed**
|
||||
- (Linux) Reintroduce Ubuntu 20.04 and 22.04 specific deb packages
|
||||
- (Linux) Fixed udev and systemd file locations
|
||||
### Dependencies
|
||||
|
||||
**Dependencies**
|
||||
- Bump babel from 2.10.3 to 2.11.0
|
||||
- Bump sphinx-copybutton from 0.5.0 to 0.5.1
|
||||
- Bump KSXGitHub/github-actions-deploy-aur from 2.5.0 to 2.6.0
|
||||
@@ -99,23 +183,26 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- Update moonlight-common-c
|
||||
- Use pre-built ffmpeg from LizardByte/build-deps for all sunshine builds (breaking change for third-party package maintainers)
|
||||
- Bump furo from 2022.9.29 to 2022.12.7
|
||||
### Misc
|
||||
|
||||
**Misc**
|
||||
- Misc org level workflow updates
|
||||
- Fix misc typos in docs
|
||||
- Fix winget release
|
||||
|
||||
## [0.15.0] - 2022-10-30
|
||||
### Added
|
||||
**Added**
|
||||
- (Windows) Add firewall rules scripts
|
||||
- (Windows) Automatically add and remove firewall rules at install/uninstall
|
||||
- (Windows) Automatically add and remove service at install/uninstall
|
||||
- (Docker) Official image added
|
||||
- (Linux) Add aarch64 flatpak package
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- (Windows/Linux/MacOS) - Move default config and apps file to assets directory
|
||||
- (MacOS) Bump boost to 1.80 for macport builds
|
||||
- (Linux) Remove backup and restore of config files
|
||||
### Fixed
|
||||
|
||||
**Fixed**
|
||||
- (Linux) - Create sunshine config directory if it doesn't exist
|
||||
- (Linux) Remove portable home and config directories for AppImage
|
||||
- (Windows) Include service install and uninstall scripts again
|
||||
@@ -126,27 +213,31 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- (Linux) Fix CUDA RGBA to NV12 conversion
|
||||
|
||||
## [0.14.1] - 2022-08-09
|
||||
### Added
|
||||
**Added**
|
||||
- (Linux) Flatpak package added
|
||||
- (Linux) AUR package automated updates
|
||||
- (Windows) Winget package automated updates
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- (General) Moved repo to @LizardByte GitHub org
|
||||
- (WebUI) Fixed button spacing on home page
|
||||
- (WebUI) Added Discord WidgetBot Crate
|
||||
### Fixed
|
||||
|
||||
**Fixed**
|
||||
- (Linux/Mac) Default config and app files now copied to user home directory
|
||||
- (Windows) Default config and app files now copied to working directory
|
||||
|
||||
## [0.14.0] - 2022-06-15
|
||||
### Added
|
||||
|
||||
**Added**
|
||||
- (Documentation) Added Sphinx documentation available at https://sunshinestream.readthedocs.io/en/latest/
|
||||
- (Development) Initial support for Localization
|
||||
- (Linux) Add rpm package as release asset
|
||||
- (macOS) Add Portfile as release asset
|
||||
- (Windows) Add DwmFlush() call to improve capture
|
||||
- (Windows) Add Windows installer
|
||||
### Fixed
|
||||
|
||||
**Fixed**
|
||||
- (AMD) Fixed hwdevice being destroyed before context
|
||||
- (Linux) Added missing dependencies to AppImage
|
||||
- (Linux) Fixed rumble events causing game to freeze
|
||||
@@ -157,41 +248,44 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- (Stream/Video) AVPacket fix
|
||||
|
||||
## [0.13.0] - 2022-02-27
|
||||
### Added
|
||||
**Added**
|
||||
- (macOS) Initial support for macOS (#40)
|
||||
|
||||
## [0.12.0] - 2022-02-13
|
||||
### Added
|
||||
**Added**
|
||||
- New command line argument `--version`
|
||||
- Custom png poster support
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- Correct software bitrate calculation
|
||||
- Increase vbv-bufsize to 1/10 of requested bitrate
|
||||
- Improvements to Web UI
|
||||
|
||||
## [0.11.1] - 2021-10-04
|
||||
### Changed
|
||||
**Changed**
|
||||
- (Linux) Fix search path for config file and assets
|
||||
|
||||
## [0.11.0] - 2021-10-04
|
||||
### Added
|
||||
**Added**
|
||||
- (Linux) Added support for wlroots based compositors on Wayland.
|
||||
- (Windows) Added an icon for the executable
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- Fixed a bug causing segfault when connecting multiple controllers.
|
||||
- (Linux) Improved NVENC, it now offloads converting images from RGB to NV12
|
||||
- (Linux) Fixed a bug causes stuttering
|
||||
|
||||
## [0.10.1] - 2021-08-21
|
||||
### Changed
|
||||
**Changed**
|
||||
- (Linux) Re-enabled KMS
|
||||
|
||||
## [0.10.0] - 2021-08-20
|
||||
### Added
|
||||
**Added**
|
||||
- Added support for Rumble with gamepads.
|
||||
- Added support for keyboard shortcuts <--- See the README for details.
|
||||
- (Windows) A very basic script has been added in Sunshine-Windows\tools <-- This will start Sunshine at boot with the highest privileges which is needed to display the login prompt.
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- Some cosmetic changes to the WebUI.
|
||||
- The first time the WebUI is opened, it will request the creation of a username/password pair from the user.
|
||||
- Fixed audio crackling introduced in version 0.8.0
|
||||
@@ -199,54 +293,58 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- (Windows) Installing from debian package shouldn't overwrite your configuration files anymore. <-- It's recommended that you back up `/etc/sunshine/` before testing this.
|
||||
|
||||
## [0.9.0] - 2021-07-11
|
||||
### Added
|
||||
**Added**
|
||||
- Added audio encryption
|
||||
- (Linux) Added basic NVENC support on Linux
|
||||
- (Windows) The Windows version can now capture the lock screen and the UAC prompt as long as it's run through `PsExec.exe` https://docs.microsoft.com/en-us/sysinternals/downloads/psexec
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- Sunshine will now accept expired or not-yet-valid certificates, as long as they are signed properly.
|
||||
- Fixed compatibility with iOS version of Moonlight
|
||||
- Drastically reduced chance of being forced to skip error correction due to video frame size
|
||||
- (Linux) sunshine.service will be installed automatically.
|
||||
|
||||
## [0.8.0] - 2021-06-30
|
||||
### Added
|
||||
**Added**
|
||||
- Added mDNS support: Moonlight will automatically find Sunshine.
|
||||
- Added UPnP support. It's off by default.
|
||||
|
||||
## [0.7.7] - 2021-06-24
|
||||
### Added
|
||||
**Added**
|
||||
- (Linux) Added installation package for Debian
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- Fixed incorrect scaling for absolute mouse coordinates when using multiple monitors.
|
||||
- Fixed incorrect colors when scaling for software encoder
|
||||
|
||||
## [0.7.1] - 2021-06-18
|
||||
### Changed
|
||||
**Changed**
|
||||
- (Linux) Fixed an issue where it was impossible to start sunshine on ubuntu 20.04
|
||||
|
||||
## [0.7.0] - 2021-06-16
|
||||
### Added
|
||||
**Added**
|
||||
- Added a Web Manager. Accessible through: https://localhost:47990 or https://<ip of your pc>:47990
|
||||
- (Linux) Added hardware encoding support for AMD on Linux
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- (Linux) Moved certificates and saved pairings generated during runtime to .config/sunshine on Linux
|
||||
|
||||
## [0.6.0] - 2021-05-26
|
||||
### Added
|
||||
**Added**
|
||||
- Added support for surround audio
|
||||
### Changed
|
||||
|
||||
**Changed**
|
||||
- Maintain aspect ratio when scaling video
|
||||
- Fix issue where Sunshine is forced to drop frames when they are too large
|
||||
|
||||
## [0.5.0] - 2021-05-13
|
||||
### Added
|
||||
**Added**
|
||||
- Added support for absolute mouse coordinates
|
||||
- (Linux) Added support for streaming specific monitor on Linux
|
||||
- (Windows) Added support for AMF on Windows
|
||||
|
||||
## [0.4.0] - 2020-05-03
|
||||
### Changed
|
||||
**Changed**
|
||||
- prep-cmd is now optional in apps.json
|
||||
- Fixed bug causing video artifacts
|
||||
- Fixed bug preventing Moonlight from closing app on exit
|
||||
@@ -255,25 +353,25 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
- Fixed bug causing crash when monitor has resolution 1366x768
|
||||
|
||||
## [0.3.1] - 2020-04-24
|
||||
### Changed
|
||||
**Changed**
|
||||
- Fix a memory leak.
|
||||
|
||||
## [0.3.0] - 2020-04-23
|
||||
### Changed
|
||||
**Changed**
|
||||
- Hardware acceleration on NVidia GPU's for Video encoding on Windows
|
||||
|
||||
## [0.2.0] - 2020-03-21
|
||||
### Changed
|
||||
**Changed**
|
||||
- Multicasting is now supported: You can set the maximum simultaneous connections with the configurable option: channels
|
||||
- Configuration variables can be overwritten on the command line: "name=value" --> it can be useful to set min_log_level=debug without modifying the configuration file
|
||||
- Switches to make testing the pairing mechanism more convenient has been added, see "sunshine --help" for details
|
||||
|
||||
## [0.1.1] - 2020-01-30
|
||||
### Added
|
||||
**Added**
|
||||
- (Linux) Added deb package and service for Linux
|
||||
|
||||
## [0.1.0] - 2020-01-27
|
||||
### Added
|
||||
**Added**
|
||||
- The first official release for Sunshine!
|
||||
|
||||
[0.1.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.1.0
|
||||
@@ -301,3 +399,8 @@ settings. In v0.17.0, games now run under your user account without elevated pri
|
||||
[0.16.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.16.0
|
||||
[0.17.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.17.0
|
||||
[0.18.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.18.0
|
||||
[0.18.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.18.1
|
||||
[0.18.2]: https://github.com/LizardByte/Sunshine/releases/tag/v0.18.2
|
||||
[0.18.3]: https://github.com/LizardByte/Sunshine/releases/tag/v0.18.3
|
||||
[0.18.4]: https://github.com/LizardByte/Sunshine/releases/tag/v0.18.4
|
||||
[0.19.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.19.0
|
||||
|
||||
194
CMakeLists.txt
194
CMakeLists.txt
@@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
# `CMAKE_CUDA_ARCHITECTURES` requires 3.18
|
||||
|
||||
project(Sunshine VERSION 0.18.0
|
||||
# todo - set version to 0.0.0 once confident in automated versioning
|
||||
project(Sunshine VERSION 0.19.0
|
||||
DESCRIPTION "Sunshine is a self-hosted game stream host for Moonlight."
|
||||
HOMEPAGE_URL "https://app.lizardbyte.dev")
|
||||
|
||||
@@ -10,6 +11,65 @@ and Nvidia GPUs for hardware encoding. Software encoding is also available. You
|
||||
Moonlight client on a variety of devices. A web UI is provided to allow configuration, and client pairing, from \
|
||||
your favorite web browser. Pair from the local server or any mobile device.")
|
||||
|
||||
# Check if env vars are defined before attempting to access them, variables will be defined even if blank
|
||||
if((DEFINED ENV{BRANCH}) AND (DEFINED ENV{BUILD_VERSION}) AND (DEFINED ENV{COMMIT})) # cmake-lint: disable=W0106
|
||||
if(($ENV{BRANCH} STREQUAL "master") AND (NOT $ENV{BUILD_VERSION} STREQUAL ""))
|
||||
# If BRANCH is "master" and BUILD_VERSION is not empty, then we are building a master branch
|
||||
MESSAGE("Got from CI master branch and version $ENV{BUILD_VERSION}")
|
||||
set(PROJECT_VERSION $ENV{BUILD_VERSION})
|
||||
elseif((DEFINED ENV{BRANCH}) AND (DEFINED ENV{COMMIT}))
|
||||
# If BRANCH is set but not BUILD_VERSION we are building nightly, we gather only the commit hash
|
||||
MESSAGE("Got from CI $ENV{BRANCH} branch and commit $ENV{COMMIT}")
|
||||
set(PROJECT_VERSION ${PROJECT_VERSION}.$ENV{COMMIT})
|
||||
endif()
|
||||
# Generate Sunshine Version based of the git tag
|
||||
# https://github.com/nocnokneo/cmake-git-versioning-example/blob/master/LICENSE
|
||||
else()
|
||||
find_package(Git)
|
||||
if(GIT_EXECUTABLE)
|
||||
MESSAGE("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
get_filename_component(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
|
||||
#Get current Branch
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
#WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE_BRANCH
|
||||
RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
# Gather current commit
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
#WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE_VERSION
|
||||
RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
# Check if Dirty
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} diff --quiet --exit-code
|
||||
#WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_IS_DIRTY
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT GIT_DESCRIBE_ERROR_CODE)
|
||||
MESSAGE("Sunshine Branch: ${GIT_DESCRIBE_BRANCH}")
|
||||
if(NOT GIT_DESCRIBE_BRANCH STREQUAL "master")
|
||||
set(PROJECT_VERSION ${PROJECT_VERSION}.${GIT_DESCRIBE_VERSION})
|
||||
MESSAGE("Sunshine Version: ${GIT_DESCRIBE_VERSION}")
|
||||
endif()
|
||||
if(GIT_IS_DIRTY)
|
||||
set(PROJECT_VERSION ${PROJECT_VERSION}.dirty)
|
||||
MESSAGE("Git tree is dirty!")
|
||||
endif()
|
||||
else()
|
||||
MESSAGE(ERROR ": Got git error while fetching tags: ${GIT_DESCRIBE_ERROR_CODE}")
|
||||
endif()
|
||||
else()
|
||||
MESSAGE(WARNING ": Git not found, cannot find git version")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(SUNSHINE_CONFIGURE_APPIMAGE "Configuration specific for AppImage." OFF)
|
||||
option(SUNSHINE_CONFIGURE_AUR "Configure files required for AUR." OFF)
|
||||
option(SUNSHINE_CONFIGURE_FLATPAK_MAN "Configure manifest file required for Flatpak build." OFF)
|
||||
@@ -72,23 +132,27 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(CURL REQUIRED libcurl)
|
||||
|
||||
if(NOT APPLE)
|
||||
set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103
|
||||
# workaround to prevent link errors against icudata, icui18n
|
||||
set(Boost_NO_BOOST_CMAKE ON) # cmake-lint: disable=C0103
|
||||
set(Boost_NO_BOOST_CMAKE ON) # cmake-lint: disable=C0103
|
||||
endif()
|
||||
|
||||
find_package(Boost COMPONENTS log filesystem program_options REQUIRED)
|
||||
find_package(Boost COMPONENTS locale log filesystem program_options REQUIRED)
|
||||
|
||||
list(APPEND SUNSHINE_COMPILE_OPTIONS -Wall -Wno-missing-braces -Wno-maybe-uninitialized -Wno-sign-compare)
|
||||
|
||||
# enable system tray, we will disable this later if we cannot find the required package config on linux
|
||||
set(SUNSHINE_TRAY 1)
|
||||
|
||||
if(WIN32)
|
||||
enable_language(RC)
|
||||
set(CMAKE_RC_COMPILER windres)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CURL_STATIC_LDFLAGS} ${CURL_STATIC_CFLAGS}")
|
||||
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
include_directories(${CURL_STATIC_INCLUDE_DIRS})
|
||||
link_directories(${CURL_STATIC_LIBRARY_DIRS})
|
||||
|
||||
add_compile_definitions(SUNSHINE_PLATFORM="windows")
|
||||
add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now
|
||||
@@ -110,6 +174,7 @@ if(WIN32)
|
||||
src/platform/windows/display_vram.cpp
|
||||
src/platform/windows/display_ram.cpp
|
||||
src/platform/windows/audio.cpp
|
||||
third-party/tray/tray_windows.c
|
||||
third-party/ViGEmClient/src/ViGEmClient.cpp
|
||||
third-party/ViGEmClient/include/ViGEm/Client.h
|
||||
third-party/ViGEmClient/include/ViGEm/Common.h
|
||||
@@ -149,6 +214,7 @@ elseif(APPLE)
|
||||
|
||||
FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices )
|
||||
FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation )
|
||||
FIND_LIBRARY(COCOA Cocoa REQUIRED ) # tray icon
|
||||
FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia )
|
||||
FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo )
|
||||
FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox )
|
||||
@@ -156,6 +222,7 @@ elseif(APPLE)
|
||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
|
||||
${APP_SERVICES_LIBRARY}
|
||||
${AV_FOUNDATION_LIBRARY}
|
||||
${COCOA}
|
||||
${CORE_MEDIA_LIBRARY}
|
||||
${CORE_VIDEO_LIBRARY}
|
||||
${VIDEO_TOOLBOX_LIBRARY}
|
||||
@@ -175,13 +242,14 @@ elseif(APPLE)
|
||||
src/platform/macos/display.mm
|
||||
src/platform/macos/input.cpp
|
||||
src/platform/macos/microphone.mm
|
||||
src/platform/macos/misc.cpp
|
||||
src/platform/macos/misc.mm
|
||||
src/platform/macos/misc.h
|
||||
src/platform/macos/nv12_zero_device.cpp
|
||||
src/platform/macos/nv12_zero_device.h
|
||||
src/platform/macos/publish.cpp
|
||||
third-party/TPCircularBuffer/TPCircularBuffer.c
|
||||
third-party/TPCircularBuffer/TPCircularBuffer.h
|
||||
third-party/tray/tray_darwin.m
|
||||
${APPLE_PLIST_FILE})
|
||||
else()
|
||||
add_compile_definitions(SUNSHINE_PLATFORM="linux")
|
||||
@@ -190,6 +258,7 @@ else()
|
||||
option(SUNSHINE_ENABLE_X11 "Enable X11 grab if available" ON)
|
||||
option(SUNSHINE_ENABLE_WAYLAND "Enable building wayland specific code" ON)
|
||||
option(SUNSHINE_ENABLE_CUDA "Enable cuda specific code" ON)
|
||||
option(SUNSHINE_ENABLE_TRAY "Enable tray icon" ON)
|
||||
|
||||
if(${SUNSHINE_ENABLE_X11})
|
||||
find_package(X11)
|
||||
@@ -355,10 +424,27 @@ ${CMAKE_BINARY_DIR}/generated-src/${filename}.h")
|
||||
src/platform/linux/wlgrab.cpp
|
||||
src/platform/linux/wayland.cpp)
|
||||
endif()
|
||||
if(NOT ${X11_FOUND} AND NOT (${LIBDRM_FOUND} AND ${LIBCAP_FOUND}) AND NOT ${WAYLAND_FOUND} AND NOT ${})
|
||||
if(NOT ${X11_FOUND} AND NOT (${LIBDRM_FOUND} AND ${LIBCAP_FOUND}) AND NOT ${WAYLAND_FOUND})
|
||||
message(FATAL_ERROR "Couldn't find either x11, wayland, cuda or (libdrm and libcap)")
|
||||
endif()
|
||||
|
||||
# tray icon
|
||||
if(${SUNSHINE_ENABLE_TRAY})
|
||||
pkg_check_modules(APPINDICATOR appindicator3-0.1)
|
||||
if(NOT APPINDICATOR_FOUND)
|
||||
message(WARNING "Couldn't find appindicator, disabling tray icon")
|
||||
set(SUNSHINE_TRAY 0)
|
||||
else()
|
||||
include_directories(${APPINDICATOR_INCLUDE_DIRS})
|
||||
link_directories(${APPINDICATOR_LIBRARY_DIRS})
|
||||
|
||||
list(APPEND PLATFORM_TARGET_FILES third-party/tray/tray_linux.c)
|
||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES ${APPINDICATOR_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
set(SUNSHINE_TRAY 0)
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_TARGET_FILES
|
||||
src/platform/linux/publish.cpp
|
||||
src/platform/linux/vaapi.h
|
||||
@@ -380,6 +466,7 @@ ${CMAKE_BINARY_DIR}/generated-src/${filename}.h")
|
||||
third-party/glad/include/glad/egl.h)
|
||||
|
||||
list(APPEND PLATFORM_LIBRARIES
|
||||
Boost::dynamic_linking
|
||||
dl
|
||||
evdev
|
||||
numa
|
||||
@@ -397,7 +484,7 @@ ${CMAKE_BINARY_DIR}/generated-src/${filename}.h")
|
||||
configure_file(sunshine.service.in sunshine.service @ONLY)
|
||||
endif()
|
||||
|
||||
configure_file(version.h.in version.h @ONLY)
|
||||
configure_file(src/version.h.in version.h @ONLY)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(SUNSHINE_TARGET_FILES
|
||||
@@ -407,6 +494,7 @@ set(SUNSHINE_TARGET_FILES
|
||||
third-party/moonlight-common-c/src/Rtsp.h
|
||||
third-party/moonlight-common-c/src/RtspParser.c
|
||||
third-party/moonlight-common-c/src/Video.h
|
||||
third-party/tray/tray.h
|
||||
src/upnp.cpp
|
||||
src/upnp.h
|
||||
src/cbs.cpp
|
||||
@@ -440,6 +528,8 @@ set(SUNSHINE_TARGET_FILES
|
||||
src/network.cpp
|
||||
src/network.h
|
||||
src/move_by_copy.h
|
||||
src/system_tray.cpp
|
||||
src/system_tray.h
|
||||
src/task_pool.h
|
||||
src/thread_pool.h
|
||||
src/thread_safe.h
|
||||
@@ -452,6 +542,8 @@ set_source_files_properties(src/upnp.cpp PROPERTIES COMPILE_FLAGS -Wno-pedantic)
|
||||
set_source_files_properties(third-party/nanors/rs.c
|
||||
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")
|
||||
|
||||
list(APPEND SUNSHINE_DEFINITIONS SUNSHINE_TRAY=${SUNSHINE_TRAY})
|
||||
|
||||
# Pre-compiled binaries
|
||||
if(WIN32)
|
||||
set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-windows-x86_64")
|
||||
@@ -469,6 +561,8 @@ else()
|
||||
else()
|
||||
set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-linux-x86_64")
|
||||
list(APPEND FFMPEG_PLATFORM_LIBRARIES mfx)
|
||||
set(CPACK_DEB_PLATFORM_PACKAGE_DEPENDS "libmfx1,")
|
||||
set(CPACK_RPM_PLATFORM_PACKAGE_REQUIRES "intel-mediasdk >= 22.3.0,")
|
||||
endif()
|
||||
endif()
|
||||
set(FFMPEG_INCLUDE_DIRS
|
||||
@@ -559,6 +653,8 @@ endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_options(sunshine PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${APPLE_PLIST_FILE})
|
||||
# Tell linker to dynamically load these symbols at runtime, in case they're unavailable:
|
||||
target_link_options(sunshine PRIVATE -Wl,-U,_CGPreflightScreenCaptureAccess -Wl,-U,_CGRequestScreenCaptureAccess)
|
||||
endif()
|
||||
|
||||
foreach(flag IN LISTS SUNSHINE_COMPILE_OPTIONS)
|
||||
@@ -597,6 +693,7 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
install(TARGETS dxgi-info RUNTIME DESTINATION "tools" COMPONENT dxgi)
|
||||
install(TARGETS audio-info RUNTIME DESTINATION "tools" COMPONENT audio)
|
||||
install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT sunshinesvc)
|
||||
install(TARGETS elevator RUNTIME DESTINATION "tools" COMPONENT elevator)
|
||||
|
||||
# Mandatory tools
|
||||
install(TARGETS ddprobe RUNTIME DESTINATION "tools" COMPONENT application)
|
||||
@@ -608,6 +705,9 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/"
|
||||
DESTINATION "scripts"
|
||||
COMPONENT service)
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/vigembus/"
|
||||
DESTINATION "scripts"
|
||||
COMPONENT vigembus)
|
||||
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/migration/"
|
||||
DESTINATION "scripts"
|
||||
COMPONENT assets)
|
||||
@@ -640,11 +740,7 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
nsExec::ExecToLog 'icacls \\\"$INSTDIR\\\\config\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)'
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"'
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"'
|
||||
MessageBox MB_YESNO|MB_ICONQUESTION \
|
||||
'Do you want to install ViGEmBus? This is REQUIRED for gamepad support while streaming.' \
|
||||
/SD IDNO IDNO NoController
|
||||
ExecShell 'open' 'https://github.com/ViGEm/ViGEmBus/releases/latest'; \
|
||||
skipped if no
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\install-vigembus.bat\\\"'
|
||||
NoController:
|
||||
")
|
||||
|
||||
@@ -654,6 +750,11 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"'
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"'
|
||||
MessageBox MB_YESNO|MB_ICONQUESTION \
|
||||
'Do you want to remove ViGEmBus)?' \
|
||||
/SD IDNO IDNO NoVigem
|
||||
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\uninstall-vigembus.bat\\\"'; skipped if no
|
||||
NoVigem:
|
||||
MessageBox MB_YESNO|MB_ICONQUESTION \
|
||||
'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' \
|
||||
/SD IDNO IDNO NoDelete
|
||||
@@ -708,6 +809,12 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
set(CPACK_COMPONENT_AUDIO_DESCRIPTION "CLI tool providing information about sound devices.")
|
||||
set(CPACK_COMPONENT_AUDIO_GROUP "tools")
|
||||
|
||||
# elevation tool
|
||||
set(CPACK_COMPONENT_ELEVATOR_DISPLAY_NAME "elevator")
|
||||
set(CPACK_COMPONENT_ELEVATOR_DESCRIPTION "CLI tool that assists with elevating \
|
||||
commands when permissions have been denied.")
|
||||
set(CPACK_COMPONENT_ELEVATOR_GROUP "tools")
|
||||
|
||||
# display tool
|
||||
set(CPACK_COMPONENT_DXGI_DISPLAY_NAME "dxgi-info")
|
||||
set(CPACK_COMPONENT_DXGI_DESCRIPTION "CLI tool providing information about graphics cards and displays.")
|
||||
@@ -728,6 +835,11 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h
|
||||
set(CPACK_COMPONENT_FIREWALL_DISPLAY_NAME "firewall-scripts")
|
||||
set(CPACK_COMPONENT_FIREWALL_DESCRIPTION "Scripts to enable or disable firewall rules.")
|
||||
set(CPACK_COMPONENT_FIREWALL_GROUP "scripts")
|
||||
|
||||
# vigembus scripts
|
||||
set(CPACK_COMPONENT_VIGEMBUS_DISPLAY_NAME "vigembus-scripts")
|
||||
set(CPACK_COMPONENT_VIGEMBUS_DESCRIPTION "Scripts to install and uninstall ViGEmBus for virtual gamepad support.")
|
||||
set(CPACK_COMPONENT_VIGEMBUS_GROUP "scripts")
|
||||
endif()
|
||||
if(APPLE)
|
||||
# TODO: bundle doesn't produce a valid .app use cpack -G DragNDrop
|
||||
@@ -787,36 +899,58 @@ elseif(UNIX)
|
||||
# Dependencies
|
||||
set(CPACK_DEB_COMPONENT_INSTALL ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "\
|
||||
libboost-filesystem1.67.0 | libboost-filesystem1.71.0 | libboost-filesystem1.74.0, \
|
||||
libboost-log1.67.0 | libboost-log1.71.0 | libboost-log1.74.0, \
|
||||
libboost-program-options1.67.0 | libboost-program-options1.71.0 | libboost-program-options1.74.0, \
|
||||
libboost-thread1.67.0 | libboost-thread1.71.0 | libboost-thread1.74.0, \
|
||||
${CPACK_DEB_PLATFORM_PACKAGE_DEPENDS} \
|
||||
libboost-filesystem${Boost_VERSION}, \
|
||||
libboost-locale${Boost_VERSION}, \
|
||||
libboost-log${Boost_VERSION}, \
|
||||
libboost-program-options${Boost_VERSION}, \
|
||||
libboost-thread${Boost_VERSION}, \
|
||||
libcap2, \
|
||||
libcurl4, \
|
||||
libdrm2, \
|
||||
libevdev2, \
|
||||
libnuma1, \
|
||||
libopus0, \
|
||||
libpulse0, \
|
||||
libxcb-shm0, \
|
||||
libxcb-xfixes0, \
|
||||
libxtst6, \
|
||||
openssl")
|
||||
libva2, \
|
||||
libva-drm2, \
|
||||
libvdpau1, \
|
||||
libwayland-client0, \
|
||||
libx11-6, \
|
||||
openssl | libssl3")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "\
|
||||
boost-filesystem >= 1.67.0, \
|
||||
boost-log >= 1.67.0, \
|
||||
boost-program-options >= 1.67.0, \
|
||||
boost-thread >= 1.67.0, \
|
||||
${CPACK_RPM_PLATFORM_PACKAGE_REQUIRES} \
|
||||
boost-filesystem >= ${Boost_VERSION}, \
|
||||
boost-locale >= ${Boost_VERSION}, \
|
||||
boost-log >= ${Boost_VERSION}, \
|
||||
boost-program-options >= ${Boost_VERSION}, \
|
||||
boost-thread >= ${Boost_VERSION}, \
|
||||
libcap >= 2.22, \
|
||||
libcurl >= 7.0, \
|
||||
libdrm >= 2.4.97, \
|
||||
libevdev >= 1.5.6, \
|
||||
libopusenc >= 0.2.1, \
|
||||
libxcb >= 1.13, \
|
||||
libXtst >= 1.2.3, \
|
||||
openssl >= 1.1, \
|
||||
libva >= 2.14.0, \
|
||||
libvdpau >= 1.5, \
|
||||
libwayland-client >= 1.20.0, \
|
||||
libX11 >= 1.7.3.1, \
|
||||
numactl-libs >= 2.0.14, \
|
||||
openssl >= 3.0.2, \
|
||||
pulseaudio-libs >= 10.0")
|
||||
# This should automatically figure out dependencies, doesn't work with the current config
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
|
||||
|
||||
if(${SUNSHINE_TRAY} STREQUAL 1)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/sunshine.svg"
|
||||
DESTINATION "/usr/share/icons")
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "\
|
||||
${CPACK_DEBIAN_PACKAGE_DEPENDS}, \
|
||||
libappindicator3-1")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "\
|
||||
${CPACK_RPM_PACKAGE_REQUIRES}, \
|
||||
libappindicator-gtk3 >= 12.10.0")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@ ENTRYPOINT steam && sunshine
|
||||
- commit hash
|
||||
|
||||
### SUNSHINE_OS
|
||||
Sunshine images are available, based on the following base images.
|
||||
Sunshine images are available with the following tag suffixes, based on their respective base images.
|
||||
|
||||
- `archlinux`
|
||||
- `debian-bullseye`
|
||||
- `fedora-36`
|
||||
- `fedora-37`
|
||||
@@ -126,9 +127,13 @@ If you want to change the PUID or PGID after the image has been built, it will r
|
||||
Specifying `lizardbyte/sunshine:latest-<SUNSHINE_OS>` or `ghcr.io/lizardbyte/sunshine:latest-<SUNSHINE_OS>` should
|
||||
retrieve the correct image for your architecture.
|
||||
|
||||
The architectures supported by these images are:
|
||||
The architectures supported by these images are shown in the table below.
|
||||
|
||||
| Architecture | Available |
|
||||
|:---------------:|:---------:|
|
||||
| amd64 / x86_64 | ✅ |
|
||||
| arm64 / aarch64 | ✅ |
|
||||
| tag suffix | amd64/x86_64 | arm64/aarch64 |
|
||||
|-----------------|--------------|---------------|
|
||||
| archlinux | ✅ | ❌ |
|
||||
| debian-bullseye | ✅ | ✅ |
|
||||
| fedora-36 | ✅ | ✅ |
|
||||
| fedora-37 | ✅ | ✅ |
|
||||
| ubuntu-20.04 | ✅ | ✅ |
|
||||
| ubuntu-22.04 | ✅ | ✅ |
|
||||
|
||||
@@ -123,10 +123,6 @@ Stats
|
||||
:alt: GitHub stars
|
||||
:target: https://github.com/LizardByte/Sunshine
|
||||
|
||||
.. image:: https://img.shields.io/badge/dynamic/json?color=blue&label=AUR&style=for-the-badge&query=$.results.0.NumVotes&url=https%3A%2F%2Fapp.lizardbyte.dev%2Funo%2Faur%2Fsunshine.json&logo=archlinux
|
||||
:alt: AUR votes
|
||||
:target: https://aur.archlinux.org/packages/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
|
||||
|
||||
144
docker/archlinux.dockerfile
Normal file
144
docker/archlinux.dockerfile
Normal file
@@ -0,0 +1,144 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64
|
||||
# archlinux does not have an arm64 base image
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=archlinux
|
||||
ARG TAG=base-devel
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
|
||||
# install dependencies
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
pacman -Syu --disable-download-timeout --noconfirm \
|
||||
archlinux-keyring
|
||||
_DEPS
|
||||
|
||||
# Setup builder user, arch prevents running makepkg as root
|
||||
RUN useradd -m builder && \
|
||||
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
||||
# makepkg is used in sunshine-build and uploader build stages
|
||||
|
||||
FROM sunshine-base as sunshine-build
|
||||
|
||||
ARG BRANCH
|
||||
ARG BUILD_VERSION
|
||||
ARG COMMIT
|
||||
ARG CLONE_URL
|
||||
# note: BUILD_VERSION may be blank
|
||||
|
||||
ENV BRANCH=${BRANCH}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV COMMIT=${COMMIT}
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
# cuda, libcap, and libdrm are optional dependencies for PKGBUILD
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
pacman -Syu --disable-download-timeout --noconfirm \
|
||||
base-devel \
|
||||
cmake \
|
||||
cuda \
|
||||
git \
|
||||
libcap \
|
||||
libdrm \
|
||||
namcap
|
||||
_DEPS
|
||||
|
||||
# Setup builder user
|
||||
USER builder
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY --link .. .
|
||||
|
||||
# setup build directory
|
||||
WORKDIR /build/sunshine/build
|
||||
|
||||
# configure PKGBUILD file
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
if [[ "${BUILD_VERSION}" == '' ]]; then
|
||||
sub_version=".r${COMMIT}"
|
||||
else
|
||||
sub_version=""
|
||||
fi
|
||||
cmake \
|
||||
-DSUNSHINE_CONFIGURE_AUR=ON \
|
||||
-DSUNSHINE_SUB_VERSION="${sub_version}" \
|
||||
-DGITHUB_CLONE_URL="${CLONE_URL}" \
|
||||
-DGITHUB_COMMIT="${COMMIT}" \
|
||||
-DSUNSHINE_CONFIGURE_ONLY=ON \
|
||||
/build/sunshine
|
||||
_MAKE
|
||||
|
||||
WORKDIR /build/sunshine/pkg
|
||||
RUN mv /build/sunshine/build/PKGBUILD .
|
||||
|
||||
# namcap and build PKGBUILD file
|
||||
RUN <<_PKGBUILD
|
||||
#!/bin/bash
|
||||
set -e
|
||||
namcap -i PKGBUILD
|
||||
makepkg -si --noconfirm
|
||||
ls -a
|
||||
_PKGBUILD
|
||||
|
||||
FROM scratch as artifacts
|
||||
|
||||
COPY --link --from=sunshine-build /build/sunshine/pkg/PKGBUILD /PKGBUILD
|
||||
COPY --link --from=sunshine-build /build/sunshine/pkg/sunshine*.pkg.tar.zst /sunshine.pkg.tar.zst
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy from uploader instead of artifacts or uploader stage will not run
|
||||
COPY --link --from=artifacts /sunshine.pkg.tar.zst /
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
pacman -U --disable-download-timeout --noconfirm \
|
||||
/sunshine.pkg.tar.zst
|
||||
_INSTALL_SUNSHINE
|
||||
|
||||
# network setup
|
||||
EXPOSE 47984-47990/tcp
|
||||
EXPOSE 48010
|
||||
EXPOSE 47998-48000/udp
|
||||
|
||||
# setup user
|
||||
ARG PGID=1000
|
||||
ENV PGID=${PGID}
|
||||
ARG PUID=1000
|
||||
ENV PUID=${PUID}
|
||||
ENV TZ="UTC"
|
||||
ARG UNAME=lizard
|
||||
ENV UNAME=${UNAME}
|
||||
|
||||
ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# first delete the builder user
|
||||
userdel -r builder
|
||||
|
||||
# then create the lizard user
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
ln -s ${HOME}/.config/sunshine /config
|
||||
chown -R ${UNAME} ${HOME}
|
||||
_SETUP_USER
|
||||
|
||||
USER ${UNAME}
|
||||
WORKDIR ${HOME}
|
||||
|
||||
# entrypoint
|
||||
ENTRYPOINT ["/usr/bin/sunshine"]
|
||||
@@ -2,6 +2,7 @@
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=debian
|
||||
ARG TAG=bullseye
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
@@ -13,16 +14,28 @@ FROM sunshine-base as sunshine-build
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
ARG BRANCH
|
||||
ARG BUILD_VERSION
|
||||
ARG COMMIT
|
||||
# note: BUILD_VERSION may be blank
|
||||
|
||||
ENV BRANCH=${BRANCH}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV COMMIT=${COMMIT}
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential=12.9* \
|
||||
cmake=3.18.4* \
|
||||
git=1:2.30.2* \
|
||||
libavdevice-dev=7:4.3.* \
|
||||
libboost-filesystem-dev=1.74.0* \
|
||||
libboost-locale-dev=1.74.0* \
|
||||
libboost-log-dev=1.74.0* \
|
||||
libboost-program-options-dev=1.74.0* \
|
||||
libboost-thread-dev=1.74.0* \
|
||||
@@ -63,6 +76,7 @@ ENV CUDA_BUILD="520.61.05"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CUDA
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
|
||||
cuda_suffix=""
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
@@ -78,7 +92,7 @@ _INSTALL_CUDA
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY .. .
|
||||
COPY --link .. .
|
||||
|
||||
# setup npm dependencies
|
||||
RUN npm install
|
||||
@@ -89,6 +103,7 @@ WORKDIR /build/sunshine/build
|
||||
# cmake and cpack
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cmake \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -108,16 +123,17 @@ FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
ARG TARGETARCH
|
||||
COPY --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy deb from builder
|
||||
COPY --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
COPY --link --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends /sunshine.deb
|
||||
apt-get clean
|
||||
@@ -142,6 +158,8 @@ ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
#!/bin/bash
|
||||
set -e
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=fedora
|
||||
ARG TAG=36
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
@@ -11,19 +12,30 @@ FROM sunshine-base as sunshine-build
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
ARG BRANCH
|
||||
ARG BUILD_VERSION
|
||||
ARG COMMIT
|
||||
# note: BUILD_VERSION may be blank
|
||||
|
||||
ENV BRANCH=${BRANCH}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV COMMIT=${COMMIT}
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
# hadolint ignore=DL3041
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
dnf -y update
|
||||
dnf -y group install "Development Tools"
|
||||
dnf -y install \
|
||||
boost-devel-1.76.0* \
|
||||
boost-static-1.76.0* \
|
||||
cmake-3.22.2* \
|
||||
gcc-12.0.1* \
|
||||
gcc-c++-12.0.1* \
|
||||
git-2.39.2* \
|
||||
libappindicator-gtk3-devel-12.10.0* \
|
||||
libcap-devel-2.48* \
|
||||
libcurl-devel-7.82.0* \
|
||||
libdrm-devel-2.4.110* \
|
||||
@@ -62,6 +74,7 @@ ENV CUDA_BUILD="525.60.13"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CUDA
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
|
||||
cuda_suffix=""
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
@@ -77,7 +90,7 @@ _INSTALL_CUDA
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY .. .
|
||||
COPY --link .. .
|
||||
|
||||
# setup npm dependencies
|
||||
RUN npm install
|
||||
@@ -88,6 +101,7 @@ WORKDIR /build/sunshine/build
|
||||
# cmake and cpack
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cmake \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -107,16 +121,17 @@ FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
ARG TARGETARCH
|
||||
COPY --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
|
||||
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy deb from builder
|
||||
COPY --from=artifacts /sunshine*.rpm /sunshine.rpm
|
||||
COPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
dnf -y update
|
||||
dnf -y install /sunshine.rpm
|
||||
dnf clean all
|
||||
@@ -141,6 +156,8 @@ ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
#!/bin/bash
|
||||
set -e
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=fedora
|
||||
ARG TAG=37
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
@@ -11,19 +12,30 @@ FROM sunshine-base as sunshine-build
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
ARG BRANCH
|
||||
ARG BUILD_VERSION
|
||||
ARG COMMIT
|
||||
# note: BUILD_VERSION may be blank
|
||||
|
||||
ENV BRANCH=${BRANCH}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV COMMIT=${COMMIT}
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
# hadolint ignore=DL3041
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
dnf -y update
|
||||
dnf -y group install "Development Tools"
|
||||
dnf -y install \
|
||||
boost-devel-1.78.0* \
|
||||
boost-static-1.78.0* \
|
||||
cmake-3.24.1* \
|
||||
gcc-12.2.1* \
|
||||
gcc-c++-12.2.1* \
|
||||
git-2.39.2* \
|
||||
libappindicator-gtk3-devel-12.10.1* \
|
||||
libcap-devel-2.48* \
|
||||
libcurl-devel-7.85.0* \
|
||||
libdrm-devel-2.4.112* \
|
||||
@@ -62,6 +74,7 @@ ENV CUDA_BUILD="525.60.13"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CUDA
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
|
||||
cuda_suffix=""
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
@@ -77,7 +90,7 @@ _INSTALL_CUDA
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY .. .
|
||||
COPY --link .. .
|
||||
|
||||
# setup npm dependencies
|
||||
RUN npm install
|
||||
@@ -88,6 +101,7 @@ WORKDIR /build/sunshine/build
|
||||
# cmake and cpack
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cmake \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -107,16 +121,17 @@ FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
ARG TARGETARCH
|
||||
COPY --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
|
||||
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy deb from builder
|
||||
COPY --from=artifacts /sunshine*.rpm /sunshine.rpm
|
||||
COPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
dnf -y update
|
||||
dnf -y install /sunshine.rpm
|
||||
dnf clean all
|
||||
@@ -141,6 +156,8 @@ ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
#!/bin/bash
|
||||
set -e
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms_pr: linux/amd64
|
||||
ARG BASE=ubuntu
|
||||
ARG TAG=18.04
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
FROM sunshine-base as sunshine-build
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
software-properties-common=0.96.24.32.18
|
||||
add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
apt-get install -y --no-install-recommends \
|
||||
bison=2:3.0.4* \
|
||||
build-essential=12.4* \
|
||||
gcc-10=10.3.0* \
|
||||
g++-10=10.3.0* \
|
||||
libavdevice-dev=7:3.4.* \
|
||||
libcap-dev=1:2.25* \
|
||||
libcurl-openssl1.0-dev=7.58.0* \
|
||||
libdrm-dev=2.4.101* \
|
||||
libevdev-dev=1.5.8* \
|
||||
libnuma-dev=2.0.11* \
|
||||
libopus-dev=1.1.2* \
|
||||
libpulse-dev=1:11.1* \
|
||||
libssl1.0-dev=1.0.2* \
|
||||
libva-dev=2.1.0* \
|
||||
libvdpau-dev=1.1.1* \
|
||||
libwayland-dev=1.16.0* \
|
||||
libx11-dev=2:1.6.4* \
|
||||
libxcb-shm0-dev=1.13* \
|
||||
libxcb-xfixes0-dev=1.13* \
|
||||
libxcb1-dev=1.13* \
|
||||
libxfixes-dev=1:5.0.3* \
|
||||
libxrandr-dev=2:1.5.1* \
|
||||
libxtst-dev=2:1.2.3* \
|
||||
npm=3.5.2* \
|
||||
node-gyp=3.6.2* \
|
||||
nodejs-dev=8.10.0* \
|
||||
wget=1.19.4*
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
_DEPS
|
||||
|
||||
# Update gcc alias
|
||||
# https://stackoverflow.com/a/70653945/11214013
|
||||
RUN <<_GCC_ALIAS
|
||||
#!/bin/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
|
||||
_GCC_ALIAS
|
||||
|
||||
# install boost
|
||||
# cannot install boost for aarch64 using ppa:savoury1/boost-defaults-1.71
|
||||
# otherwise add the repository and the following packages
|
||||
# libboost-filesystem1.71-dev=1.71.0* \
|
||||
# libboost-log1.71-dev=1.71.0* \
|
||||
# libboost-program-options1.71-dev=1.71.0* \
|
||||
# libboost-regex1.71-dev=1.71.0* \
|
||||
# libboost-thread1.71-dev=1.71.0* \
|
||||
WORKDIR /build/tmp
|
||||
RUN <<_INSTALL_BOOST
|
||||
url="https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.bz2"
|
||||
wget "${url}" --progress=bar:force:noscroll -q --show-progress -O ./boost.tar.bz2
|
||||
tar --bzip2 -xf boost.tar.bz2 --directory /build
|
||||
mv /build/boost_*/ /build/boost
|
||||
ls -a /build/boost
|
||||
cd /build/boost
|
||||
./bootstrap.sh --with-libraries=system,thread,log,program_options && \
|
||||
./b2 install variant=release link=static,shared runtime-link=shared -j "$(nproc)"
|
||||
_INSTALL_BOOST
|
||||
|
||||
# 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
|
||||
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
|
||||
cmake --version
|
||||
_INSTALL_CMAKE
|
||||
|
||||
# install cuda
|
||||
WORKDIR /build/cuda
|
||||
# versions: https://developer.nvidia.com/cuda-toolkit-archive
|
||||
ENV CUDA_VERSION="11.8.0"
|
||||
ENV CUDA_BUILD="520.61.05"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CUDA
|
||||
#!/bin/bash
|
||||
cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
|
||||
cuda_suffix=""
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
cuda_suffix="_sbsa"
|
||||
fi
|
||||
url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"
|
||||
echo "cuda url: ${url}"
|
||||
wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run
|
||||
chmod a+x ./cuda.run
|
||||
./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm
|
||||
rm ./cuda.run
|
||||
_INSTALL_CUDA
|
||||
|
||||
# todo - install libmfx
|
||||
# https://github.com/Intel-Media-SDK/MediaSDK
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY .. .
|
||||
|
||||
# setup npm dependencies
|
||||
RUN npm install
|
||||
|
||||
# setup build directory
|
||||
WORKDIR /build/sunshine/build
|
||||
|
||||
# cmake and cpack
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
cmake \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DSUNSHINE_ASSETS_DIR=share/sunshine \
|
||||
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
|
||||
-DSUNSHINE_ENABLE_WAYLAND=ON \
|
||||
-DSUNSHINE_ENABLE_X11=ON \
|
||||
-DSUNSHINE_ENABLE_DRM=ON \
|
||||
-DSUNSHINE_ENABLE_CUDA=ON \
|
||||
/build/sunshine
|
||||
make -j "$(nproc)"
|
||||
cpack -G DEB
|
||||
_MAKE
|
||||
|
||||
FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
ARG TARGETARCH
|
||||
COPY --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy deb from builder
|
||||
COPY --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends /sunshine.deb
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
_INSTALL_SUNSHINE
|
||||
|
||||
# network setup
|
||||
EXPOSE 47984-47990/tcp
|
||||
EXPOSE 48010
|
||||
EXPOSE 47998-48000/udp
|
||||
|
||||
# setup user
|
||||
ARG PGID=1000
|
||||
ENV PGID=${PGID}
|
||||
ARG PUID=1000
|
||||
ENV PUID=${PUID}
|
||||
ENV TZ="UTC"
|
||||
ARG UNAME=lizard
|
||||
ENV UNAME=${UNAME}
|
||||
|
||||
ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
ln -s ${HOME}/.config/sunshine /config
|
||||
chown -R ${UNAME} ${HOME}
|
||||
_SETUP_USER
|
||||
|
||||
USER ${UNAME}
|
||||
WORKDIR ${HOME}
|
||||
|
||||
# entrypoint
|
||||
ENTRYPOINT ["/usr/bin/sunshine"]
|
||||
@@ -2,6 +2,7 @@
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=ubuntu
|
||||
ARG TAG=20.04
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
@@ -13,17 +14,30 @@ FROM sunshine-base as sunshine-build
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
ARG BRANCH
|
||||
ARG BUILD_VERSION
|
||||
ARG COMMIT
|
||||
# note: BUILD_VERSION may be blank
|
||||
|
||||
ENV BRANCH=${BRANCH}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV COMMIT=${COMMIT}
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential=12.8* \
|
||||
gcc-10=10.3.0* \
|
||||
g++-10=10.3.0* \
|
||||
git=1:2.25.1* \
|
||||
libappindicator3-dev=12.10.1* \
|
||||
libavdevice-dev=7:4.2.* \
|
||||
libboost-filesystem-dev=1.71.0* \
|
||||
libboost-locale-dev=1.71.0* \
|
||||
libboost-log-dev=1.71.0* \
|
||||
libboost-program-options-dev=1.71.0* \
|
||||
libboost-thread-dev=1.71.0* \
|
||||
@@ -60,6 +74,7 @@ _DEPS
|
||||
# https://stackoverflow.com/a/70653945/11214013
|
||||
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 \
|
||||
@@ -76,6 +91,7 @@ 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"
|
||||
@@ -97,6 +113,7 @@ ENV CUDA_BUILD="520.61.05"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CUDA
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
|
||||
cuda_suffix=""
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
@@ -112,7 +129,7 @@ _INSTALL_CUDA
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY .. .
|
||||
COPY --link .. .
|
||||
|
||||
# setup npm dependencies
|
||||
RUN npm install
|
||||
@@ -123,6 +140,7 @@ WORKDIR /build/sunshine/build
|
||||
# cmake and cpack
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cmake \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -142,16 +160,17 @@ FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
ARG TARGETARCH
|
||||
COPY --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy deb from builder
|
||||
COPY --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
COPY --link --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends /sunshine.deb
|
||||
apt-get clean
|
||||
@@ -176,6 +195,8 @@ ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
#!/bin/bash
|
||||
set -e
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# artifacts: true
|
||||
# platforms: linux/amd64,linux/arm64/v8
|
||||
# platforms_pr: linux/amd64
|
||||
# no-cache-filters: sunshine-base,artifacts,sunshine
|
||||
ARG BASE=ubuntu
|
||||
ARG TAG=22.04
|
||||
FROM ${BASE}:${TAG} AS sunshine-base
|
||||
@@ -13,22 +14,35 @@ FROM sunshine-base as sunshine-build
|
||||
ARG TARGETPLATFORM
|
||||
RUN echo "target_platform: ${TARGETPLATFORM}"
|
||||
|
||||
ARG BRANCH
|
||||
ARG BUILD_VERSION
|
||||
ARG COMMIT
|
||||
# note: BUILD_VERSION may be blank
|
||||
|
||||
ENV BRANCH=${BRANCH}
|
||||
ENV BUILD_VERSION=${BUILD_VERSION}
|
||||
ENV COMMIT=${COMMIT}
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
# install dependencies
|
||||
RUN <<_DEPS
|
||||
#!/bin/bash
|
||||
set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential=12.9* \
|
||||
cmake=3.22.1* \
|
||||
git=1:2.34.1* \
|
||||
libappindicator3-dev=12.10.1* \
|
||||
libavdevice-dev=7:4.4.* \
|
||||
libboost-filesystem-dev=1.74.0* \
|
||||
libboost-locale-dev=1.74.0* \
|
||||
libboost-log-dev=1.74.0* \
|
||||
libboost-program-options-dev=1.74.0* \
|
||||
libboost-thread-dev=1.74.0* \
|
||||
libcap-dev=1:2.44* \
|
||||
libcurl4-openssl-dev=7.81.0* \
|
||||
libdrm-dev=2.4.110* \
|
||||
libdrm-dev=2.4.113* \
|
||||
libevdev-dev=1.12.1* \
|
||||
libnuma-dev=2.0.14* \
|
||||
libopus-dev=1.3.1* \
|
||||
@@ -63,6 +77,7 @@ ENV CUDA_BUILD="520.61.05"
|
||||
# hadolint ignore=SC3010
|
||||
RUN <<_INSTALL_CUDA
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"
|
||||
cuda_suffix=""
|
||||
if [[ "${TARGETPLATFORM}" == 'linux/arm64' ]]; then
|
||||
@@ -78,7 +93,7 @@ _INSTALL_CUDA
|
||||
|
||||
# copy repository
|
||||
WORKDIR /build/sunshine/
|
||||
COPY .. .
|
||||
COPY --link .. .
|
||||
|
||||
# setup npm dependencies
|
||||
RUN npm install
|
||||
@@ -89,6 +104,7 @@ WORKDIR /build/sunshine/build
|
||||
# cmake and cpack
|
||||
RUN <<_MAKE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cmake \
|
||||
-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -108,16 +124,17 @@ FROM scratch AS artifacts
|
||||
ARG BASE
|
||||
ARG TAG
|
||||
ARG TARGETARCH
|
||||
COPY --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.deb /sunshine-${BASE}-${TAG}-${TARGETARCH}.deb
|
||||
|
||||
FROM sunshine-base as sunshine
|
||||
|
||||
# copy deb from builder
|
||||
COPY --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
COPY --link --from=artifacts /sunshine*.deb /sunshine.deb
|
||||
|
||||
# install sunshine
|
||||
RUN <<_INSTALL_SUNSHINE
|
||||
#!/bin/bash
|
||||
set -e
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends /sunshine.deb
|
||||
apt-get clean
|
||||
@@ -142,6 +159,8 @@ ENV HOME=/home/$UNAME
|
||||
|
||||
# setup user
|
||||
RUN <<_SETUP_USER
|
||||
#!/bin/bash
|
||||
set -e
|
||||
groupadd -f -g "${PGID}" "${UNAME}"
|
||||
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -G input -u "${PUID}" "${UNAME}"
|
||||
mkdir -p ${HOME}/.config/sunshine
|
||||
|
||||
2778
docs/Doxyfile
Normal file
2778
docs/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
furo==2022.12.7
|
||||
m2r2==0.3.3
|
||||
breathe==4.35.0
|
||||
furo==2023.3.27
|
||||
m2r2==0.3.3.post2
|
||||
Sphinx==6.1.3
|
||||
sphinx-copybutton==0.5.1
|
||||
|
||||
@@ -7,7 +7,7 @@ Performance Tips
|
||||
|
||||
AMD
|
||||
^^^
|
||||
In Windows, enabling `Enahanced Sync` in AMD's settings may help reduce the latency by an additional frame. This
|
||||
In Windows, enabling `Enhanced Sync` in AMD's settings may help reduce the latency by an additional frame. This
|
||||
applies to `amfenc` and `libx264`.
|
||||
|
||||
Nvidia
|
||||
@@ -107,6 +107,20 @@ log_path
|
||||
|
||||
log_path = sunshine.log
|
||||
|
||||
global_prep_cmd
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
A list of commands to be run before/after all applications. If any of the prep-commands fail, starting the application is aborted.
|
||||
|
||||
**Default**
|
||||
``[]``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
global_prep_cmd = [{"do":"nircmd.exe setdisplay 1280 720 32 144","undo":"nircmd.exe setdisplay 2560 1440 32 144"}]
|
||||
|
||||
Controls
|
||||
--------
|
||||
|
||||
@@ -160,7 +174,7 @@ key_repeat_delay
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
The initial delay in milliseconds before repeating keys. Controls how fast keys will repeat themselves.
|
||||
The initial delay, in milliseconds, before repeating keys. Controls how fast keys will repeat themselves.
|
||||
|
||||
**Default**
|
||||
``500``
|
||||
@@ -179,7 +193,7 @@ key_repeat_frequency
|
||||
.. Tip:: This configurable option supports decimals.
|
||||
|
||||
**Default**
|
||||
.. Todo:: Unknown
|
||||
``24.9``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -197,7 +211,11 @@ keybindings
|
||||
.. Hint:: keybindings needs to have a multiple of two elements.
|
||||
|
||||
**Default**
|
||||
None
|
||||
.. code-block:: text
|
||||
|
||||
0x10, 0xA0,
|
||||
0x11, 0xA2,
|
||||
0x12, 0xA4
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -217,7 +235,7 @@ key_rightalt_to_key_win
|
||||
make Sunshine think the Right Alt key is the Windows key.
|
||||
|
||||
**Default**
|
||||
None
|
||||
``disabled``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -281,13 +299,18 @@ output_name
|
||||
.. Tip:: To find the name of the appropriate values follow these instructions.
|
||||
|
||||
**Linux**
|
||||
.. code-block:: bash
|
||||
During Sunshine startup, you should see the list of detected monitors:
|
||||
|
||||
xrandr --listmonitors
|
||||
.. code-block:: text
|
||||
|
||||
Example output: ``0: +HDMI-1 1920/518x1200/324+0+0 HDMI-1``
|
||||
Info: Detecting connected monitors
|
||||
Info: Detected monitor 0: DVI-D-0, connected: false
|
||||
Info: Detected monitor 1: HDMI-0, connected: true
|
||||
Info: Detected monitor 2: DP-0, connected: true
|
||||
Info: Detected monitor 3: DP-1, connected: false
|
||||
Info: Detected monitor 4: DVI-D-1, connected: false
|
||||
|
||||
You need to use the value before the colon in the output, e.g. ``0``.
|
||||
You need to use the value before the colon in the output, e.g. ``1``.
|
||||
|
||||
.. Todo:: macOS
|
||||
|
||||
@@ -322,7 +345,7 @@ fps
|
||||
fps is supported.
|
||||
|
||||
**Default**
|
||||
.. Todo:: Unknown
|
||||
``[10, 30, 60, 90, 120]``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -339,7 +362,20 @@ resolutions
|
||||
resolution is supported.
|
||||
|
||||
**Default**
|
||||
.. Todo:: Unknown
|
||||
.. code-block:: text
|
||||
|
||||
[
|
||||
352x240,
|
||||
480x360,
|
||||
858x480,
|
||||
1280x720,
|
||||
1920x1080,
|
||||
2560x1080,
|
||||
3440x1440,
|
||||
1920x1200,
|
||||
3860x2160,
|
||||
3840x1600,
|
||||
]
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -411,6 +447,8 @@ audio_sink
|
||||
|
||||
tools\audio-info.exe
|
||||
|
||||
.. Tip:: If you want to mute the host speakers, use `virtual_sink`_ instead.
|
||||
|
||||
**Default**
|
||||
Sunshine will select the default audio device.
|
||||
|
||||
@@ -439,8 +477,13 @@ virtual_sink
|
||||
|
||||
.. Tip:: See `audio_sink`_!
|
||||
|
||||
**Default**
|
||||
.. Todo:: Unknown
|
||||
.. Tip:: These are some options for virtual sound devices.
|
||||
|
||||
- Stream Streaming Speakers (Linux, macOS, Windows)
|
||||
|
||||
- To use this option, you must have Steam installed and have used Stream remote play at least once.
|
||||
|
||||
- `Virtual Audio Cable <https://vb-audio.com/Cable/>`_ (macOS, Windows)
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -486,7 +529,7 @@ port
|
||||
Mic (unused) 48002 UDP +13
|
||||
================ ============ ===========================
|
||||
|
||||
.. Attention:: Custom ports are only allowed on select Moonlight clients.
|
||||
.. Attention:: Custom ports may not be supported by all Moonlight clients.
|
||||
|
||||
**Default**
|
||||
``47989``
|
||||
@@ -503,7 +546,7 @@ pkey
|
||||
The private key. This must be 2048 bits.
|
||||
|
||||
**Default**
|
||||
.. Todo:: Unknown
|
||||
``credentials/cakey.pem``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -517,7 +560,7 @@ cert
|
||||
The certificate. Must be signed with a 2048 bit key.
|
||||
|
||||
**Default**
|
||||
.. Todo:: Unknown
|
||||
``credentials/cacert.pem``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -597,7 +640,7 @@ upnp
|
||||
===== ===========
|
||||
|
||||
**Default**
|
||||
``off``
|
||||
``disabled``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
@@ -608,7 +651,7 @@ ping_timeout
|
||||
^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
How long to wait in milliseconds for data from Moonlight before shutting down the stream.
|
||||
How long to wait, in milliseconds, for data from Moonlight before shutting down the stream.
|
||||
|
||||
**Default**
|
||||
``10000``
|
||||
@@ -728,6 +771,40 @@ hevc_mode
|
||||
|
||||
hevc_mode = 2
|
||||
|
||||
capture
|
||||
^^^^^^^
|
||||
|
||||
**Description**
|
||||
Force specific screen capture method.
|
||||
|
||||
.. Caution:: Applies to Linux only.
|
||||
|
||||
**Choices**
|
||||
|
||||
.. table::
|
||||
:widths: auto
|
||||
|
||||
========= ===========
|
||||
Value Description
|
||||
========= ===========
|
||||
nvfbc Use NVIDIA Frame Buffer Capture to capture direct to GPU memory. This is usually the fastest method for
|
||||
NVIDIA cards. For GeForce cards it will only work with drivers patched with
|
||||
`nvidia-patch <https://github.com/keylase/nvidia-patch/>`_
|
||||
or `nvlax <https://github.com/keylase/nvidia-patch/>`_.
|
||||
wlr Capture for wlroots based Wayland compositors via DMA-BUF.
|
||||
kms DRM/KMS screen capture from the kernel. This requires that sunshine has cap_sys_admin capability.
|
||||
See :ref:`Linux Setup <about/usage:setup>`.
|
||||
x11 Uses XCB. This is the slowest and most CPU intensive so should be avoided if possible.
|
||||
========= ===========
|
||||
|
||||
**Default**
|
||||
Automatic. Sunshine will use the first capture method available in the order of the table above.
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
capture = kms
|
||||
|
||||
encoder
|
||||
^^^^^^^
|
||||
|
||||
@@ -784,12 +861,10 @@ sw_preset
|
||||
ultrafast fastest
|
||||
superfast
|
||||
veryfast
|
||||
superfast
|
||||
faster
|
||||
fast
|
||||
medium
|
||||
slow
|
||||
slow
|
||||
slower
|
||||
veryslow slowest
|
||||
========= ===========
|
||||
@@ -1081,6 +1156,68 @@ amd_rc
|
||||
|
||||
amd_rc = vbr_latency
|
||||
|
||||
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
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
**Description**
|
||||
Preanalysis can increase encoding quality at the cost of latency.
|
||||
|
||||
.. Note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
**Default**
|
||||
``disabled``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_preanalysis = disabled
|
||||
|
||||
amd_vbaq
|
||||
^^^^^^^^
|
||||
|
||||
**Description**
|
||||
Variance Based Adaptive Quantization (VBAQ) can increase subjective visual quality.
|
||||
|
||||
.. Note:: This option only applies when using amdvce `encoder`_.
|
||||
|
||||
**Default**
|
||||
``enabled``
|
||||
|
||||
**Example**
|
||||
.. code-block:: text
|
||||
|
||||
amd_vbaq = enabled
|
||||
|
||||
amd_coder
|
||||
^^^^^^^^^
|
||||
|
||||
|
||||
189
docs/source/about/app_examples.rst
Normal file
189
docs/source/about/app_examples.rst
Normal file
@@ -0,0 +1,189 @@
|
||||
App Examples
|
||||
============
|
||||
Since not all applications behave the same, we decided to create some examples to help you get started adding games
|
||||
and applications to Sunshine.
|
||||
|
||||
.. Attention:: Throughout these examples, any fields not shown are left blank. You can enhance your experience by
|
||||
adding an image or a log file (via the ``Output`` field).
|
||||
|
||||
Common Examples
|
||||
---------------
|
||||
|
||||
Desktop
|
||||
^^^^^^^
|
||||
|
||||
+----------------------+-----------------+
|
||||
| **Field** | **Value** |
|
||||
+----------------------+-----------------+
|
||||
| Application Name | ``Desktop`` |
|
||||
+----------------------+-----------------+
|
||||
| Image | ``desktop.png`` |
|
||||
+----------------------+-----------------+
|
||||
|
||||
Steam Big Picture
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. Note:: Steam is launched as a detached command because Steam starts with a process that self updates itself and the original
|
||||
process is killed. Since the original process ends it will not work as a regular command.
|
||||
|
||||
+----------------------+------------------------------------------+----------------------------------+-----------------------------------+
|
||||
| **Field** | **Linux** | **macOS** | **Windows** |
|
||||
+----------------------+------------------------------------------+----------------------------------+-----------------------------------+
|
||||
| Application Name | ``Steam Big Picture`` |
|
||||
+----------------------+------------------------------------------+----------------------------------+-----------------------------------+
|
||||
| Detached Commands | ``setsid steam steam://open/bigpicture`` | ``open steam://open/bigpicture`` | ``steam steam://open/bigpicture`` |
|
||||
+----------------------+------------------------------------------+----------------------------------+-----------------------------------+
|
||||
| Image | ``steam.png`` |
|
||||
+----------------------+------------------------------------------+----------------------------------+-----------------------------------+
|
||||
|
||||
Epic Game Store game
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. Note:: Using URI method will be the most consistent between various games, but does not allow a game to be launched
|
||||
using the "Command" and therefore the stream will not end when the game ends.
|
||||
|
||||
URI (Epic)
|
||||
""""""""""
|
||||
|
||||
+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| **Field** | **Windows** |
|
||||
+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Application Name | ``Surviving Mars`` |
|
||||
+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Detached Commands | ``cmd /C "start com.epicgames.launcher://apps/d759128018124dcabb1fbee9bb28e178%3A20729b9176c241f0b617c5723e70ec2d%3AOvenbird?action=launch&silent=true"`` |
|
||||
+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
Binary (Epic w/ working directory)
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
+----------------------+-----------------------------------------------+
|
||||
| **Field** | **Windows** |
|
||||
+----------------------+-----------------------------------------------+
|
||||
| Application Name | ``Surviving Mars`` |
|
||||
+----------------------+-----------------------------------------------+
|
||||
| Command | ``cmd /c "MarsEpic.exe"`` |
|
||||
+----------------------+-----------------------------------------------+
|
||||
| Working Directory | ``C:\Program Files\Epic Games\SurvivingMars`` |
|
||||
+----------------------+-----------------------------------------------+
|
||||
|
||||
Binary (Epic w/o working directory)
|
||||
"""""""""""""""""""""""""""""""""""
|
||||
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| **Field** | **Windows** |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| Application Name | ``Surviving Mars`` |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| Command | ``"C:\Program Files\Epic Games\SurvivingMars\MarsEpic.exe"`` |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
|
||||
|
||||
Steam game
|
||||
^^^^^^^^^^
|
||||
|
||||
.. Note:: Using URI method will be the most consistent between various games, but does not allow a game to be launched
|
||||
using the "Command" and therefore the stream will not end when the game ends.
|
||||
|
||||
URI (Steam)
|
||||
"""""""""""
|
||||
|
||||
+----------------------+-------------------------------------------+-----------------------------------+---------------------------------------------+
|
||||
| **Field** | **Linux** | **macOS** | **Windows** |
|
||||
+----------------------+-------------------------------------------+-----------------------------------+---------------------------------------------+
|
||||
| Application Name | ``Surviving Mars`` |
|
||||
+----------------------+-------------------------------------------+-----------------------------------+---------------------------------------------+
|
||||
| Detached Commands | ``setsid steam steam://rungameid/464920`` | ``open steam://rungameid/464920`` | ``cmd /C "start steam://rungameid/464920"`` |
|
||||
+----------------------+-------------------------------------------+-----------------------------------+---------------------------------------------+
|
||||
|
||||
Binary (Steam w/ working directory)
|
||||
"""""""""""""""""""""""""""""""""""
|
||||
|
||||
+----------------------+-------------------------+-------------------------+------------------------------------------------------------------+
|
||||
| **Field** | **Linux** | **macOS** | **Windows** |
|
||||
+----------------------+-------------------------+-------------------------+------------------------------------------------------------------+
|
||||
| Application Name | ``Surviving Mars`` |
|
||||
+----------------------+-------------------------+-------------------------+------------------------------------------------------------------+
|
||||
| Command | ``MarsSteam`` | ``cmd /c "MarsSteam.exe"`` |
|
||||
+----------------------+-------------------------+-------------------------+------------------------------------------------------------------+
|
||||
| Working Directory | ``~/.steam/steam/SteamApps/common/Survivng Mars`` | ``C:\Program Files (x86)\Steam\steamapps\common\Surviving Mars`` |
|
||||
+----------------------+-------------------------+-------------------------+------------------------------------------------------------------+
|
||||
|
||||
Binary (Steam w/o working directory)
|
||||
""""""""""""""""""""""""""""""""""""
|
||||
|
||||
+----------------------+------------------------------+------------------------------+----------------------------------------------------------------------------------+
|
||||
| **Field** | **Linux** | **macOS** | **Windows** |
|
||||
+----------------------+------------------------------+------------------------------+----------------------------------------------------------------------------------+
|
||||
| Application Name | ``Surviving Mars`` |
|
||||
+----------------------+------------------------------+------------------------------+----------------------------------------------------------------------------------+
|
||||
| Command | ``~/.steam/steam/SteamApps/common/Survivng Mars/MarsSteam`` | ``"C:\Program Files (x86)\Steam\steamapps\common\Surviving Mars\MarsSteam.exe"`` |
|
||||
+----------------------+------------------------------+------------------------------+----------------------------------------------------------------------------------+
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
Changing Resolution and Refresh Rate (Linux - X11)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| **Field** | **Value** |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
| Command Preparations | Do: ``xrandr --output HDMI-1 --mode 1920x1080 --rate 60`` |
|
||||
| +--------------------------------------------------------------+
|
||||
| | Undo: ``xrandr --output HDMI-1 --mode 3840×2160 --rate 120`` |
|
||||
+----------------------+--------------------------------------------------------------+
|
||||
|
||||
Changing Resolution and Refresh Rate (Linux - Wayland)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
+----------------------+-------------------------------------------------------------+
|
||||
| **Field** | **Value** |
|
||||
+----------------------+-------------------------------------------------------------+
|
||||
| Command Preparations | Do: ``wlr-xrandr --output HDMI-1 --mode 1920x1080@60Hz`` |
|
||||
| +-------------------------------------------------------------+
|
||||
| | Undo: ``wlr-xrandr --output HDMI-1 --mode 3840×2160@120Hz`` |
|
||||
+----------------------+-------------------------------------------------------------+
|
||||
|
||||
Flatpak
|
||||
^^^^^^^
|
||||
|
||||
.. Attention:: Because Flatpak packages run in a sandboxed environment and do not normally have access to the host,
|
||||
the Flatpak of Sunshine requires commands to be prefixed with ``flatpak-spawn --host``.
|
||||
|
||||
macOS
|
||||
-----
|
||||
|
||||
Changing Resolution and Refresh Rate (macOS)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. Note:: This example uses the `displayplacer` tool to change the resolution.
|
||||
This tool can be installed following instructions in their
|
||||
`GitHub repository <https://github.com/jakehilborn/displayplacer>`_.
|
||||
|
||||
+----------------------+-----------------------------------------------------------------------------------------------+
|
||||
| **Field** | **Value** |
|
||||
+----------------------+-----------------------------------------------------------------------------------------------+
|
||||
| Command Preparations | Do: ``displayplacer "id:<screenId> res:1920x1080 hz:60 scaling:on origin:(0,0) degree:0"`` |
|
||||
| +-----------------------------------------------------------------------------------------------+
|
||||
| | Undo: ``displayplacer "id:<screenId> res:3840x2160 hz:120 scaling:on origin:(0,0) degree:0"`` |
|
||||
+----------------------+-----------------------------------------------------------------------------------------------+
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
Changing Resolution and Refresh Rate (Windows)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. Note:: This example uses the `QRes` tool to change the resolution and refresh rate.
|
||||
This tool can be downloaded from their `SourceForge repository <https://sourceforge.net/projects/qres/>`_.
|
||||
|
||||
+----------------------+----------------------------------------------------+
|
||||
| **Field** | **Value** |
|
||||
+----------------------+----------------------------------------------------+
|
||||
| Command Preparations | Do: ``FullPath\qres.exe /x:1920 /y:1080 /r:60`` |
|
||||
| +----------------------------------------------------+
|
||||
| | Undo: ``FullPath\qres.exe /x:3840 /y:2160 /r:120`` |
|
||||
+----------------------+----------------------------------------------------+
|
||||
|
||||
.. Tip:: You can change your host resolution to match the client resolution automatically using the
|
||||
`Nonary/ResolutionAutomation <https://github.com/Nonary/ResolutionAutomation/>`_ project.
|
||||
1
docs/source/about/changelog.rst
Normal file
1
docs/source/about/changelog.rst
Normal file
@@ -0,0 +1 @@
|
||||
.. mdinclude:: ../../../CHANGELOG.md
|
||||
@@ -1,3 +1 @@
|
||||
:github_url: https://github.com/LizardByte/Sunshine/tree/nightly/docs/DOCKER_README.md
|
||||
|
||||
.. mdinclude:: ../../../DOCKER_README.md
|
||||
|
||||
@@ -35,9 +35,10 @@ CUDA is used for NVFBC capture.
|
||||
=========================================== ============== ============== ================================
|
||||
Package CUDA Version Min Driver CUDA Compute Capabilities
|
||||
=========================================== ============== ============== ================================
|
||||
https://aur.archlinux.org/packages/sunshine User dependent User dependent User dependent
|
||||
PKGBUILD User dependent User dependent User dependent
|
||||
sunshine.AppImage 11.8.0 450.80.02 50;52;60;61;62;70;75;80;86;90;35
|
||||
sunshine_{arch}.flatpak 11.8.0 450.80.02 50;52;60;61;62;70;75;80;86;90;35
|
||||
sunshine.pkg.tar.zst 11.8.0 450.80.02 50;52;60;61;62;70;75;80;86;90;35
|
||||
sunshine_{arch}.flatpak 12.0.0 525.60.13 50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-debian-bullseye-{arch}.deb 11.8.0 450.80.02 50;52;60;61;62;70;75;80;86;90;35
|
||||
sunshine-fedora-36-{arch}.rpm 12.0.0 525.60.13 50;52;60;61;62;70;75;80;86;90
|
||||
sunshine-fedora-37-{arch}.rpm 12.0.0 525.60.13 50;52;60;61;62;70;75;80;86;90
|
||||
@@ -78,14 +79,13 @@ Uninstall:
|
||||
|
||||
./sunshine.AppImage --remove
|
||||
|
||||
AUR Package
|
||||
^^^^^^^^^^^
|
||||
Archlinux PKGBUILD
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
#. Open terminal and run the following code.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://aur.archlinux.org/sunshine.git
|
||||
cd sunshine
|
||||
wget https://github.com/LizardByte/Sunshine/releases/latest/download/PKGBUILD
|
||||
makepkg -fi
|
||||
|
||||
Uninstall:
|
||||
@@ -93,6 +93,20 @@ Uninstall:
|
||||
|
||||
pacman -R sunshine
|
||||
|
||||
Archlinux pkg
|
||||
^^^^^^^^^^^^^
|
||||
#. Open terminal and run the following code.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine.pkg.tar.zst
|
||||
pacman -U --noconfirm sunshine.pkg.tar.zst
|
||||
|
||||
Uninstall:
|
||||
.. code-block:: bash
|
||||
|
||||
pacman -R sunshine
|
||||
|
||||
Debian Package
|
||||
^^^^^^^^^^^^^^
|
||||
#. Download ``sunshine-{ubuntu-version}.deb`` and run the following code.
|
||||
@@ -224,7 +238,7 @@ Windows
|
||||
|
||||
Installer
|
||||
^^^^^^^^^
|
||||
#. Download and install ``sunshine-windows.exe``
|
||||
#. Download and install ``sunshine-windows-installer.exe``
|
||||
|
||||
.. Attention:: You should carefully select or unselect the options you want to install. Do not blindly install or enable
|
||||
features.
|
||||
@@ -234,7 +248,7 @@ menu. Different versions of Windows may provide slightly different steps for uni
|
||||
|
||||
Standalone
|
||||
^^^^^^^^^^
|
||||
#. Download and extract ``sunshine-windows.zip``
|
||||
#. Download and extract ``sunshine-windows-portable.zip``
|
||||
|
||||
To uninstall, delete the extracted directory which contains the ``sunshine.exe`` file.
|
||||
|
||||
|
||||
@@ -3,10 +3,17 @@ Third Party Packages
|
||||
|
||||
.. Danger:: These packages are not maintained by LizardByte. Use at your own risk.
|
||||
|
||||
AUR
|
||||
---
|
||||
|
||||
.. image:: https://img.shields.io/badge/dynamic/json?color=blue&label=AUR&style=for-the-badge&query=$.results.0.NumVotes&url=https%3A%2F%2Fapp.lizardbyte.dev%2Funo%2Faur%2Fsunshine.json&logo=archlinux
|
||||
:alt: AUR votes
|
||||
:target: https://aur.archlinux.org/packages/sunshine
|
||||
|
||||
Chocolatey
|
||||
----------
|
||||
|
||||
.. image:: https://img.shields.io/chocolatey/v/Sunshine?style=for-the-badge&logo=chocolatey
|
||||
.. image:: https://img.shields.io/chocolatey/v/sunshine?style=for-the-badge&logo=chocolatey
|
||||
:alt: Chocolatey Version
|
||||
:target: https://community.chocolatey.org/packages/sunshine
|
||||
|
||||
|
||||
@@ -146,8 +146,7 @@ macOS
|
||||
^^^^^
|
||||
Sunshine can only access microphones on macOS due to system limitations. To stream system audio use
|
||||
`Soundflower <https://github.com/mattingalls/Soundflower>`_ or
|
||||
`BlackHole <https://github.com/ExistentialAudio/BlackHole>`_ and
|
||||
select their sink as audio device in `sunshine.conf`.
|
||||
`BlackHole <https://github.com/ExistentialAudio/BlackHole>`_.
|
||||
|
||||
.. Note:: Command Keys are not forwarded by Moonlight. Right Option-Key is mapped to CMD-Key.
|
||||
|
||||
@@ -207,7 +206,7 @@ Application List
|
||||
- ``"Variable name":"Variable value"``
|
||||
- ``apps`` - The list of applications
|
||||
- Advanced users may want to edit the application list manually. The format is ``json``.
|
||||
- Example application:
|
||||
- Example ``json`` application:
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
@@ -249,6 +248,8 @@ Application List
|
||||
|
||||
- ``working-dir`` - The working directory to use. If not specified, Sunshine will use the application directory.
|
||||
|
||||
- For more examples see :ref:`app examples <about/app_examples:app examples>`.
|
||||
|
||||
Considerations
|
||||
--------------
|
||||
- When an application is started, if there is an application already running, it will be terminated.
|
||||
@@ -257,8 +258,9 @@ Considerations
|
||||
- For example, if you attempt to run ``steam`` as a ``cmd`` instead of ``detached`` the stream will immediately fail.
|
||||
This is due to the method in which the steam process is executed. Other applications may behave similarly.
|
||||
|
||||
- In addition to the apps listed, one app "Desktop" is hardcoded into Sunshine. It does not start an application,
|
||||
instead it simply starts a stream.
|
||||
- The "Desktop" app works the same as any other application except it has no commands. It does not start an application,
|
||||
instead it simply starts a stream. If you removed it and would like to get it back, just add a new application with
|
||||
the name "Desktop" and "desktop.png" as the image path.
|
||||
- For the Linux flatpak you must prepend commands with ``flatpak-spawn --host``.
|
||||
|
||||
HDR Support
|
||||
|
||||
@@ -16,6 +16,7 @@ Install Requirements
|
||||
cmake \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-program-options-dev \
|
||||
libboost-thread-dev \
|
||||
@@ -54,10 +55,11 @@ Install Requirements
|
||||
sudo dnf group install "Development Tools" && \
|
||||
sudo dnf install \
|
||||
boost-devel \
|
||||
boost-static \
|
||||
cmake \
|
||||
gcc \
|
||||
gcc-c++ \
|
||||
intel-mediasdk-devel \ # x86_64 only
|
||||
libappindicator-gtk3-devel \
|
||||
libcap-devel \
|
||||
libcurl-devel \
|
||||
libdrm-devel \
|
||||
@@ -80,9 +82,7 @@ Install Requirements
|
||||
pulseaudio-libs-devel \
|
||||
rpm-build \ # if you want to build an RPM binary package
|
||||
wget \ # necessary for cuda install with `run` file
|
||||
which \ # necessary for cuda install with `run` file
|
||||
# libmfx-devel is not listed for fedora, this is for x86_64 only
|
||||
https://kojipkgs.fedoraproject.org//packages/libmfx/1.25/4.el8/x86_64/libmfx-devel-1.25-4.el8.x86_64.rpm
|
||||
which # necessary for cuda install with `run` file
|
||||
|
||||
Ubuntu 20.04
|
||||
^^^^^^^^^^^^
|
||||
@@ -95,8 +95,10 @@ Install Requirements
|
||||
build-essential \
|
||||
cmake \
|
||||
g++-10 \
|
||||
libappindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-thread-dev \
|
||||
libboost-program-options-dev \
|
||||
@@ -142,8 +144,10 @@ Install Requirements
|
||||
sudo apt update && sudo apt install \
|
||||
build-essential \
|
||||
cmake \
|
||||
libappindicator3-dev \
|
||||
libavdevice-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-locale-dev \
|
||||
libboost-log-dev \
|
||||
libboost-thread-dev \
|
||||
libboost-program-options-dev \
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
@@ -43,9 +44,11 @@ To use cmake method for obtaining version instead of regex,
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'breathe', # c++ support for sphinx with doxygen
|
||||
'm2r2', # enable markdown files
|
||||
'sphinx.ext.autosectionlabel',
|
||||
'sphinx.ext.todo', # enable to-do sections
|
||||
'sphinx.ext.graphviz', # enable graphs for breathe
|
||||
'sphinx.ext.viewcode', # add links to view source code
|
||||
'sphinx_copybutton', # add a copy button to code blocks
|
||||
]
|
||||
@@ -64,13 +67,15 @@ source_suffix = ['.rst', '.md']
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# images
|
||||
html_favicon = os.path.join(root_dir, 'src_assets', 'common', 'assets', 'web', 'images', 'favicon.ico')
|
||||
html_logo = os.path.join(root_dir, 'sunshine.png')
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# html_static_path = ['_static']
|
||||
|
||||
html_logo = os.path.join(root_dir, 'sunshine.png')
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'furo'
|
||||
@@ -82,4 +87,16 @@ html_theme_options = {
|
||||
|
||||
# extension config options
|
||||
autosectionlabel_prefix_document = True # Make sure the target is unique
|
||||
breathe_default_project = 'src'
|
||||
breathe_implementation_filename_extensions = ['.c', '.cc', '.cpp', '.mm']
|
||||
breathe_order_parameters_first = False
|
||||
breathe_projects = dict(
|
||||
src="../build/doxyxml"
|
||||
)
|
||||
todo_include_todos = True
|
||||
|
||||
subprocess.run('doxygen', shell=True, cwd=source_dir)
|
||||
|
||||
# disable epub mimetype warnings
|
||||
# https://github.com/readthedocs/readthedocs.org/blob/eadf6ac6dc6abc760a91e1cb147cc3c5f37d1ea8/docs/conf.py#L235-L236
|
||||
suppress_warnings = ["epub.unknown_project_files"]
|
||||
|
||||
@@ -14,11 +14,16 @@ Test clang-format locally.
|
||||
Sphinx
|
||||
------
|
||||
Sunshine uses `Sphinx <https://www.sphinx-doc.org/en/master/>`_ for documentation building. Sphinx, along with other
|
||||
required documentation depencies are included in the `./docs/requirements.txt` file. Python is required to build
|
||||
required python dependencies are included in the `./docs/requirements.txt` file. Python is required to build
|
||||
sphinx docs. Installation and setup of python will not be covered here.
|
||||
|
||||
Doxygen is used to generate the XML files required by Sphinx. Doxygen can be obtained from
|
||||
`Doxygen downloads <https://www.doxygen.nl/download.html>`_. Ensure that the `doxygen` executable is in your path.
|
||||
|
||||
The config file for Sphinx is `docs/source/conf.py`. This is already included in the repo and should not be modified.
|
||||
|
||||
The config file for Doxygen is `docs/Doxyfile`. This is already included in the repo and should not be modified.
|
||||
|
||||
Test with Sphinx
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
:github_url: https://github.com/LizardByte/Sunshine/tree/nightly/docs/source/index.rst
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
.. include:: toc.rst
|
||||
|
||||
86
docs/source/source/src.rst
Normal file
86
docs/source/source/src.rst
Normal file
@@ -0,0 +1,86 @@
|
||||
src
|
||||
===
|
||||
We are in process of improving the source code documentation. Code should be documented using Doxygen syntax.
|
||||
Some examples exist in `main.h` and `main.cpp`. In order for documentation within the code to appear in the
|
||||
rendered docs, the definition of the object must be in a header file, although the documentation itself can (and
|
||||
should) be in the source file.
|
||||
|
||||
Example Documentation Blocks
|
||||
----------------------------
|
||||
|
||||
**file.h**
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
// functions
|
||||
int main(int argc, char *argv[]);
|
||||
|
||||
**file.cpp** (with markdown)
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
/**
|
||||
* @brief Main application entry point.
|
||||
* @param argc The number of arguments.
|
||||
* @param argv The arguments.
|
||||
*
|
||||
* EXAMPLES:
|
||||
* ```cpp
|
||||
* main(1, const char* args[] = {"hello", "markdown", nullptr});
|
||||
* ```
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
// do stuff
|
||||
}
|
||||
|
||||
**file.cpp** (with ReStructuredText)
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
/**
|
||||
* @brief Main application entry point.
|
||||
* @param argc The number of arguments.
|
||||
* @param argv The arguments.
|
||||
* @rst
|
||||
* EXAMPLES:
|
||||
*
|
||||
* .. code-block:: cpp
|
||||
* main(1, const char* args[] = {"hello", "rst", nullptr});
|
||||
* @endrst
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
// do stuff
|
||||
}
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: src
|
||||
|
||||
src/main
|
||||
src/audio
|
||||
src/cbs
|
||||
src/config
|
||||
src/confighttp
|
||||
src/crypto
|
||||
src/httpcommon
|
||||
src/input
|
||||
src/move_by_copy
|
||||
src/network
|
||||
src/nvhttp
|
||||
src/process
|
||||
src/round_robin
|
||||
src/rtsp
|
||||
src/stream
|
||||
src/sync
|
||||
src/system_tray
|
||||
src/task_pool
|
||||
src/thread_pool
|
||||
src/thread_safe
|
||||
src/upnp
|
||||
src/utility
|
||||
src/uuid
|
||||
src/video
|
||||
src/platform
|
||||
4
docs/source/source/src/audio.rst
Normal file
4
docs/source/source/src/audio.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
audio
|
||||
=====
|
||||
|
||||
.. doxygenfile:: audio.h
|
||||
4
docs/source/source/src/cbs.rst
Normal file
4
docs/source/source/src/cbs.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
cbs
|
||||
===
|
||||
|
||||
.. doxygenfile:: cbs.h
|
||||
4
docs/source/source/src/config.rst
Normal file
4
docs/source/source/src/config.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
config
|
||||
======
|
||||
|
||||
.. doxygenfile:: config.h
|
||||
4
docs/source/source/src/confighttp.rst
Normal file
4
docs/source/source/src/confighttp.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
confighttp
|
||||
==========
|
||||
|
||||
.. doxygenfile:: confighttp.h
|
||||
4
docs/source/source/src/crypto.rst
Normal file
4
docs/source/source/src/crypto.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
crypto
|
||||
======
|
||||
|
||||
.. doxygenfile:: crypto.h
|
||||
4
docs/source/source/src/httpcommon.rst
Normal file
4
docs/source/source/src/httpcommon.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
httpcommon
|
||||
==========
|
||||
|
||||
.. doxygenfile:: httpcommon.h
|
||||
4
docs/source/source/src/input.rst
Normal file
4
docs/source/source/src/input.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
input
|
||||
=====
|
||||
|
||||
.. doxygenfile:: input.h
|
||||
4
docs/source/source/src/main.rst
Normal file
4
docs/source/source/src/main.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
main
|
||||
====
|
||||
|
||||
.. doxygenfile:: main.h
|
||||
4
docs/source/source/src/move_by_copy.rst
Normal file
4
docs/source/source/src/move_by_copy.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
move_by_copy
|
||||
============
|
||||
|
||||
.. doxygenfile:: move_by_copy.h
|
||||
4
docs/source/source/src/network.rst
Normal file
4
docs/source/source/src/network.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
network
|
||||
=======
|
||||
|
||||
.. doxygenfile:: network.h
|
||||
4
docs/source/source/src/nvhttp.rst
Normal file
4
docs/source/source/src/nvhttp.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
nvhttp
|
||||
======
|
||||
|
||||
.. doxygenfile:: nvhttp.h
|
||||
10
docs/source/source/src/platform.rst
Normal file
10
docs/source/source/src/platform.rst
Normal file
@@ -0,0 +1,10 @@
|
||||
platform
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
platform/common
|
||||
platform/linux
|
||||
platform/macos
|
||||
platform/windows
|
||||
4
docs/source/source/src/platform/common.rst
Normal file
4
docs/source/source/src/platform/common.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
common
|
||||
======
|
||||
|
||||
.. Todo:: Add common.h
|
||||
12
docs/source/source/src/platform/linux.rst
Normal file
12
docs/source/source/src/platform/linux.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
linux
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
linux/cuda
|
||||
linux/graphics
|
||||
linux/misc
|
||||
linux/vaapi
|
||||
linux/wayland
|
||||
linux/x11grab
|
||||
4
docs/source/source/src/platform/linux/cuda.rst
Normal file
4
docs/source/source/src/platform/linux/cuda.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
cuda
|
||||
====
|
||||
|
||||
.. doxygenfile:: platform/linux/cuda.h
|
||||
4
docs/source/source/src/platform/linux/graphics.rst
Normal file
4
docs/source/source/src/platform/linux/graphics.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
graphics
|
||||
========
|
||||
|
||||
.. Todo:: Add graphics.h
|
||||
4
docs/source/source/src/platform/linux/misc.rst
Normal file
4
docs/source/source/src/platform/linux/misc.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
misc
|
||||
====
|
||||
|
||||
.. Todo:: Add misc.h
|
||||
4
docs/source/source/src/platform/linux/vaapi.rst
Normal file
4
docs/source/source/src/platform/linux/vaapi.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
vaapi
|
||||
=====
|
||||
|
||||
.. doxygenfile:: platform/linux/vaapi.h
|
||||
4
docs/source/source/src/platform/linux/wayland.rst
Normal file
4
docs/source/source/src/platform/linux/wayland.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
wayland
|
||||
=======
|
||||
|
||||
.. doxygenfile:: platform/linux/wayland.h
|
||||
4
docs/source/source/src/platform/linux/x11grab.rst
Normal file
4
docs/source/source/src/platform/linux/x11grab.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
x11grab
|
||||
=======
|
||||
|
||||
.. Todo:: Add x11grab.h
|
||||
11
docs/source/source/src/platform/macos.rst
Normal file
11
docs/source/source/src/platform/macos.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
macos
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
macos/av_audio
|
||||
macos/av_img_t
|
||||
macos/av_video
|
||||
macos/misc
|
||||
macos/nv12_zero_device
|
||||
4
docs/source/source/src/platform/macos/av_audio.rst
Normal file
4
docs/source/source/src/platform/macos/av_audio.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
av_audio
|
||||
========
|
||||
|
||||
.. Todo:: Add av_audio.h
|
||||
4
docs/source/source/src/platform/macos/av_img_t.rst
Normal file
4
docs/source/source/src/platform/macos/av_img_t.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
av_img_t
|
||||
========
|
||||
|
||||
.. Todo:: Add av_img_t.h
|
||||
4
docs/source/source/src/platform/macos/av_video.rst
Normal file
4
docs/source/source/src/platform/macos/av_video.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
av_video
|
||||
========
|
||||
|
||||
.. Todo:: Add av_video.h
|
||||
4
docs/source/source/src/platform/macos/misc.rst
Normal file
4
docs/source/source/src/platform/macos/misc.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
misc
|
||||
====
|
||||
|
||||
.. doxygenfile:: platform/macos/misc.h
|
||||
@@ -0,0 +1,4 @@
|
||||
nv12_zero_device
|
||||
================
|
||||
|
||||
.. Todo:: Add nv12_zero_device.h
|
||||
9
docs/source/source/src/platform/windows.rst
Normal file
9
docs/source/source/src/platform/windows.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
windows
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
windows/display
|
||||
windows/misc
|
||||
windows/PolicyConfig
|
||||
4
docs/source/source/src/platform/windows/PolicyConfig.rst
Normal file
4
docs/source/source/src/platform/windows/PolicyConfig.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
PolicyConfig
|
||||
============
|
||||
|
||||
.. Todo:: Add PolicyConfig.h
|
||||
4
docs/source/source/src/platform/windows/display.rst
Normal file
4
docs/source/source/src/platform/windows/display.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
display
|
||||
=======
|
||||
|
||||
.. Todo:: Add display.h
|
||||
4
docs/source/source/src/platform/windows/misc.rst
Normal file
4
docs/source/source/src/platform/windows/misc.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
misc
|
||||
====
|
||||
|
||||
.. doxygenfile:: platform/windows/misc.h
|
||||
4
docs/source/source/src/process.rst
Normal file
4
docs/source/source/src/process.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
process
|
||||
=======
|
||||
|
||||
.. doxygenfile:: process.h
|
||||
4
docs/source/source/src/round_robin.rst
Normal file
4
docs/source/source/src/round_robin.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
round_robin
|
||||
===========
|
||||
|
||||
.. doxygenfile:: round_robin.h
|
||||
4
docs/source/source/src/rtsp.rst
Normal file
4
docs/source/source/src/rtsp.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
rtsp
|
||||
====
|
||||
|
||||
.. doxygenfile:: rtsp.h
|
||||
4
docs/source/source/src/stream.rst
Normal file
4
docs/source/source/src/stream.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
stream
|
||||
======
|
||||
|
||||
.. doxygenfile:: stream.h
|
||||
4
docs/source/source/src/sync.rst
Normal file
4
docs/source/source/src/sync.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
sync
|
||||
====
|
||||
|
||||
.. doxygenfile:: sync.h
|
||||
4
docs/source/source/src/system_tray.rst
Normal file
4
docs/source/source/src/system_tray.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
system_tray
|
||||
===========
|
||||
|
||||
.. doxygenfile:: system_tray.h
|
||||
4
docs/source/source/src/task_pool.rst
Normal file
4
docs/source/source/src/task_pool.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
tasl_pool
|
||||
=========
|
||||
|
||||
.. doxygenfile:: task_pool.h
|
||||
4
docs/source/source/src/thread_pool.rst
Normal file
4
docs/source/source/src/thread_pool.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
thread_pool
|
||||
===========
|
||||
|
||||
.. doxygenfile:: thread_pool.h
|
||||
4
docs/source/source/src/thread_safe.rst
Normal file
4
docs/source/source/src/thread_safe.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
thread_safe
|
||||
===========
|
||||
|
||||
.. doxygenfile:: thread_safe.h
|
||||
4
docs/source/source/src/upnp.rst
Normal file
4
docs/source/source/src/upnp.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
upnp
|
||||
====
|
||||
|
||||
.. doxygenfile:: upnp.h
|
||||
4
docs/source/source/src/utility.rst
Normal file
4
docs/source/source/src/utility.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
utility
|
||||
=======
|
||||
|
||||
.. Todo:: Add utility.h
|
||||
4
docs/source/source/src/uuid.rst
Normal file
4
docs/source/source/src/uuid.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
uuid
|
||||
====
|
||||
|
||||
.. doxygenfile:: uuid.h
|
||||
4
docs/source/source/src/video.rst
Normal file
4
docs/source/source/src/video.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
video
|
||||
=====
|
||||
|
||||
.. doxygenfile:: video.h
|
||||
@@ -7,7 +7,9 @@
|
||||
about/docker
|
||||
about/third_party_packages
|
||||
about/usage
|
||||
about/app_examples
|
||||
about/advanced_usage
|
||||
about/changelog
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
@@ -46,3 +48,9 @@
|
||||
:caption: Legal
|
||||
|
||||
legal/legal
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: source
|
||||
|
||||
source/src
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.2.1",
|
||||
"@fortawesome/fontawesome-free": "6.4.0",
|
||||
"bootstrap": "5.2.3",
|
||||
"vue": "2.6.12"
|
||||
}
|
||||
|
||||
@@ -1,29 +1,49 @@
|
||||
# Edit on github: https://github.com/LizardByte/Sunshine/tree/nightly/packaging/linux/aur/PKGBUILD
|
||||
# Reference: https://wiki.archlinux.org/title/PKGBUILD
|
||||
|
||||
pkgname=@SUNSHINE_AUR_PKG@
|
||||
pkgname='sunshine'
|
||||
pkgver=@PROJECT_VERSION@@SUNSHINE_SUB_VERSION@
|
||||
pkgrel=1
|
||||
pkgdesc="@PROJECT_DESCRIPTION@"
|
||||
arch=('x86_64' 'i686')
|
||||
arch=('x86_64' 'aarch64')
|
||||
url=@PROJECT_HOMEPAGE_URL@
|
||||
license=('GPL3')
|
||||
|
||||
depends=('avahi' 'boost-libs' 'curl' 'libevdev' 'libmfx' 'libpulse' 'libva' 'libvdpau' 'libx11' 'libxcb' 'libxfixes' 'libxrandr' 'libxtst' 'numactl' 'openssl' 'opus' 'udev')
|
||||
depends=('avahi' 'boost-libs' 'curl' 'libappindicator-gtk3' 'libevdev' 'libmfx' 'libpulse' 'libva' 'libvdpau' 'libx11' 'libxcb' 'libxfixes' 'libxrandr' 'libxtst' 'numactl' 'openssl' 'opus' 'udev')
|
||||
makedepends=('boost' 'cmake' 'git' 'make' 'nodejs' 'npm')
|
||||
optdepends=('cuda: NvFBC capture support'
|
||||
'libcap'
|
||||
'libdrm')
|
||||
|
||||
provides=(@SUNSHINE_AUR_PROVIDES@)
|
||||
conflicts=(@SUNSHINE_AUR_CONFLICTS@)
|
||||
provides=('sunshine')
|
||||
|
||||
source=("$pkgname::git+@GITHUB_CLONE_URL@#commit=@GITHUB_COMMIT@")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
prepare() {
|
||||
cd "$pkgname"
|
||||
git submodule update --recursive --init
|
||||
# Skip submodules that we don't want
|
||||
if [[ $CARCH == "x86_64" ]]; then
|
||||
git -c submodule."ffmpeg-macos-x86_64".update=none \
|
||||
-c submodule."ffmpeg-windows-x86_64".update=none \
|
||||
-c submodule."ffmpeg-linux-aarch64".update=none \
|
||||
-c submodule."ffmpeg-macos-aarch64".update=none \
|
||||
submodule update --recursive --init
|
||||
elif [[ $CARCH == "aarch64" ]]; then
|
||||
git -c submodule."ffmpeg-macos-x86_64".update=none \
|
||||
-c submodule."ffmpeg-windows-x86_64".update=none \
|
||||
-c submodule."ffmpeg-linux-x86_64".update=none \
|
||||
-c submodule."ffmpeg-macos-aarch64".update=none \
|
||||
submodule update --recursive --init
|
||||
|
||||
# It's unlikely that someone could get this far on a system with an incorrect arch, but we should handle it anyway
|
||||
# Pull linux ffmpeg submodules
|
||||
else
|
||||
git -c submodule."ffmpeg-macos-x86_64".update=none \
|
||||
-c submodule."ffmpeg-windows-x86_64".update=none \
|
||||
-c submodule."ffmpeg-macos-aarch64".update=none \
|
||||
submodule update --recursive --init
|
||||
fi
|
||||
}
|
||||
|
||||
build() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
app-id: dev.lizardbyte.sunshine
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: "21.08"
|
||||
runtime-version: "22.08"
|
||||
sdk: org.freedesktop.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.node18
|
||||
@@ -33,8 +33,8 @@ modules:
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- cd tools/build && bison -y -d -o src/engine/jamgram.cpp src/engine/jamgram.y
|
||||
- ./bootstrap.sh --prefix=$FLATPAK_DEST --with-libraries=system,thread,log,program_options || cat bootstrap.log
|
||||
- ./b2 install variant=release link=static,shared runtime-link=shared cxxflags="$CXXFLAGS" linkflags="$LDFLAGS"
|
||||
- ./bootstrap.sh --prefix=$FLATPAK_DEST --with-libraries=locale,log,program_options,system,thread
|
||||
- ./b2 install variant=release link=shared runtime-link=shared cxxflags="$CXXFLAGS" linkflags="$LDFLAGS"
|
||||
-j $FLATPAK_BUILDER_N_JOBS
|
||||
sources:
|
||||
- type: archive
|
||||
@@ -187,14 +187,14 @@ modules:
|
||||
- type: file
|
||||
only-arches:
|
||||
- x86_64
|
||||
url: https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run
|
||||
sha256: 9223c4af3aebe4a7bbed9abd9b163b03a1b34b855fbc2b4a0d1b706ac09a5a16
|
||||
url: https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda_12.0.0_525.60.13_linux.run
|
||||
sha256: 905e9b9516900839fb76064719db752439f38b8cb730b49335d8bd53ddfad392
|
||||
dest-filename: cuda.run
|
||||
- type: file
|
||||
only-arches:
|
||||
- aarch64
|
||||
url: https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux_sbsa.run # yamllint disable-line rule:line-length
|
||||
sha256: e6e9a8d31163c9776b5e313fd7590877c5684e1ecddee741154f95704d4ed27c
|
||||
url: https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda_12.0.0_525.60.13_linux_sbsa.run # yamllint disable-line rule:line-length
|
||||
sha256: cd13e9c65d4c8f895a968706f46064d536be09f9706bce081cc864b7e4fa4544
|
||||
dest-filename: cuda.run
|
||||
|
||||
- name: sunshine
|
||||
|
||||
21
qodana-js.yaml
Normal file
21
qodana-js.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
version: "1.0"
|
||||
linter: jetbrains/qodana-js:2023.1-eap
|
||||
|
||||
bootstrap: |
|
||||
# install npm dependencies
|
||||
npm install
|
||||
|
||||
exclude:
|
||||
- name: All
|
||||
paths:
|
||||
- gh-pages
|
||||
- third-party
|
||||
|
||||
failThreshold: 100
|
||||
|
||||
include:
|
||||
- name: CheckDependencyLicenses
|
||||
|
||||
profile:
|
||||
name: qodana.recommended
|
||||
29
qodana-python.yaml
Normal file
29
qodana-python.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
version: "1.0"
|
||||
linter: jetbrains/qodana-python:2023.1-eap
|
||||
|
||||
bootstrap: |
|
||||
# setup python
|
||||
|
||||
python3 -m venv /data/cache/venv
|
||||
source /data/cache/venv/bin/activate
|
||||
python3 -m pip install -r /data/project/docs/requirements.txt
|
||||
python3 -m pip install -r /data/project/scripts/requirements.txt
|
||||
|
||||
# remove idea directory (No Python interpreter configured for the project)
|
||||
# https://github.com/JetBrains/Qodana/discussions/134#discussioncomment-4329981
|
||||
rm -rf .idea
|
||||
|
||||
exclude:
|
||||
- name: All
|
||||
paths:
|
||||
- gh-pages
|
||||
- third-party
|
||||
|
||||
failThreshold: 100
|
||||
|
||||
include:
|
||||
- name: CheckDependencyLicenses
|
||||
|
||||
profile:
|
||||
name: qodana.recommended
|
||||
@@ -1 +1 @@
|
||||
Babel==2.11.0
|
||||
Babel==2.12.1
|
||||
|
||||
40
scripts/update_clang_format.py
Normal file
40
scripts/update_clang_format.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# standard imports
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# variables
|
||||
directories = [
|
||||
'src',
|
||||
'tools',
|
||||
os.path.join('third-party', 'glad'),
|
||||
os.path.join('third-party', 'nvfbc'),
|
||||
os.path.join('third-party', 'wayland-protocols')
|
||||
]
|
||||
file_types = [
|
||||
'cpp',
|
||||
'h',
|
||||
'm',
|
||||
'mm'
|
||||
]
|
||||
|
||||
|
||||
def clang_format(file: str):
|
||||
print(f'Formatting {file} ...')
|
||||
subprocess.run(['clang-format', '-i', file])
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point.
|
||||
"""
|
||||
# walk the directories
|
||||
for directory in directories:
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
if os.path.isfile(file_path) and file.rsplit('.')[-1] in file_types:
|
||||
clang_format(file=file_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
482
src/audio.cpp
482
src/audio.cpp
@@ -11,271 +11,279 @@
|
||||
#include "utility.h"
|
||||
|
||||
namespace audio {
|
||||
using namespace std::literals;
|
||||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>;
|
||||
using namespace std::literals;
|
||||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>;
|
||||
|
||||
struct audio_ctx_t {
|
||||
// We want to change the sink for the first stream only
|
||||
std::unique_ptr<std::atomic_bool> sink_flag;
|
||||
struct audio_ctx_t {
|
||||
// We want to change the sink for the first stream only
|
||||
std::unique_ptr<std::atomic_bool> sink_flag;
|
||||
|
||||
std::unique_ptr<platf::audio_control_t> control;
|
||||
std::unique_ptr<platf::audio_control_t> control;
|
||||
|
||||
bool restore_sink;
|
||||
platf::sink_t sink;
|
||||
};
|
||||
bool restore_sink;
|
||||
platf::sink_t sink;
|
||||
};
|
||||
|
||||
static int start_audio_control(audio_ctx_t &ctx);
|
||||
static void stop_audio_control(audio_ctx_t &);
|
||||
static int
|
||||
start_audio_control(audio_ctx_t &ctx);
|
||||
static void
|
||||
stop_audio_control(audio_ctx_t &);
|
||||
|
||||
int map_stream(int channels, bool quality);
|
||||
int
|
||||
map_stream(int channels, bool quality);
|
||||
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
|
||||
opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] {
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
platf::speaker::map_stereo,
|
||||
96000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
platf::speaker::map_stereo,
|
||||
512000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
4,
|
||||
2,
|
||||
platf::speaker::map_surround51,
|
||||
256000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
6,
|
||||
0,
|
||||
platf::speaker::map_surround51,
|
||||
1536000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
8,
|
||||
5,
|
||||
3,
|
||||
platf::speaker::map_surround71,
|
||||
450000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
8,
|
||||
8,
|
||||
0,
|
||||
platf::speaker::map_surround71,
|
||||
2048000,
|
||||
},
|
||||
};
|
||||
opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] {
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
platf::speaker::map_stereo,
|
||||
96000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
platf::speaker::map_stereo,
|
||||
512000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
4,
|
||||
2,
|
||||
platf::speaker::map_surround51,
|
||||
256000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
6,
|
||||
0,
|
||||
platf::speaker::map_surround51,
|
||||
1536000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
8,
|
||||
5,
|
||||
3,
|
||||
platf::speaker::map_surround71,
|
||||
450000,
|
||||
},
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
8,
|
||||
8,
|
||||
0,
|
||||
platf::speaker::map_surround71,
|
||||
2048000,
|
||||
},
|
||||
};
|
||||
|
||||
auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control);
|
||||
auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control);
|
||||
|
||||
void encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
|
||||
auto packets = mail::man->queue<packet_t>(mail::audio_packets);
|
||||
auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||
void
|
||||
encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
|
||||
auto packets = mail::man->queue<packet_t>(mail::audio_packets);
|
||||
auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||
|
||||
// Encoding takes place on this thread
|
||||
platf::adjust_thread_priority(platf::thread_priority_e::high);
|
||||
// Encoding takes place on this thread
|
||||
platf::adjust_thread_priority(platf::thread_priority_e::high);
|
||||
|
||||
opus_t opus { opus_multistream_encoder_create(
|
||||
stream->sampleRate,
|
||||
stream->channelCount,
|
||||
stream->streams,
|
||||
stream->coupledStreams,
|
||||
stream->mapping,
|
||||
OPUS_APPLICATION_RESTRICTED_LOWDELAY,
|
||||
nullptr) };
|
||||
opus_t opus { opus_multistream_encoder_create(
|
||||
stream->sampleRate,
|
||||
stream->channelCount,
|
||||
stream->streams,
|
||||
stream->coupledStreams,
|
||||
stream->mapping,
|
||||
OPUS_APPLICATION_RESTRICTED_LOWDELAY,
|
||||
nullptr) };
|
||||
|
||||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream->bitrate));
|
||||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0));
|
||||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream->bitrate));
|
||||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0));
|
||||
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
while(auto sample = samples->pop()) {
|
||||
buffer_t packet { 1400 };
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
while (auto sample = samples->pop()) {
|
||||
buffer_t packet { 1400 };
|
||||
|
||||
int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size());
|
||||
if(bytes < 0) {
|
||||
BOOST_LOG(error) << "Couldn't encode audio: "sv << opus_strerror(bytes);
|
||||
packets->stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
packet.fake_resize(bytes);
|
||||
packets->raise(channel_data, std::move(packet));
|
||||
}
|
||||
}
|
||||
|
||||
void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
||||
auto shutdown_event = mail->event<bool>(mail::shutdown);
|
||||
auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||
|
||||
auto ref = control_shared.ref();
|
||||
if(!ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &control = ref->control;
|
||||
if(!control) {
|
||||
shutdown_event->view();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Order of priority:
|
||||
// 1. Config
|
||||
// 2. Virtual if available
|
||||
// 3. Host
|
||||
std::string *sink = &ref->sink.host;
|
||||
if(!config::audio.sink.empty()) {
|
||||
sink = &config::audio.sink;
|
||||
}
|
||||
else if(ref->sink.null) {
|
||||
auto &null = *ref->sink.null;
|
||||
switch(stream->channelCount) {
|
||||
case 2:
|
||||
sink = &null.stereo;
|
||||
break;
|
||||
case 6:
|
||||
sink = &null.surround51;
|
||||
break;
|
||||
case 8:
|
||||
sink = &null.surround71;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only the first to start a session may change the default sink
|
||||
if(!ref->sink_flag->exchange(true, std::memory_order_acquire)) {
|
||||
ref->restore_sink = !config.flags[config_t::HOST_AUDIO];
|
||||
|
||||
// If the sink is empty (Host has no sink!), definately switch to the virtual.
|
||||
if(ref->sink.host.empty()) {
|
||||
if(control->set_sink(*sink)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If the client requests audio on the host, don't change the default sink
|
||||
else if(!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Capture takes place on this thread
|
||||
platf::adjust_thread_priority(platf::thread_priority_e::critical);
|
||||
|
||||
auto samples = std::make_shared<sample_queue_t::element_type>(30);
|
||||
std::thread thread { encodeThread, samples, config, channel_data };
|
||||
|
||||
auto fg = util::fail_guard([&]() {
|
||||
samples->stop();
|
||||
thread.join();
|
||||
|
||||
shutdown_event->view();
|
||||
});
|
||||
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
int samples_per_frame = frame_size * stream->channelCount;
|
||||
|
||||
auto mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
BOOST_LOG(error) << "Couldn't create audio input"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while(!shutdown_event->peek()) {
|
||||
std::vector<std::int16_t> sample_buffer;
|
||||
sample_buffer.resize(samples_per_frame);
|
||||
|
||||
auto status = mic->sample(sample_buffer);
|
||||
switch(status) {
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
case platf::capture_e::timeout:
|
||||
continue;
|
||||
case platf::capture_e::reinit:
|
||||
mic.reset();
|
||||
mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
BOOST_LOG(error) << "Couldn't re-initialize audio input"sv;
|
||||
int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size());
|
||||
if (bytes < 0) {
|
||||
BOOST_LOG(error) << "Couldn't encode audio: "sv << opus_strerror(bytes);
|
||||
packets->stop();
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
|
||||
packet.fake_resize(bytes);
|
||||
packets->raise(channel_data, std::move(packet));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
capture(safe::mail_t mail, config_t config, void *channel_data) {
|
||||
auto shutdown_event = mail->event<bool>(mail::shutdown);
|
||||
auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||
|
||||
auto ref = control_shared.ref();
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
samples->raise(std::move(sample_buffer));
|
||||
auto &control = ref->control;
|
||||
if (!control) {
|
||||
shutdown_event->view();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Order of priority:
|
||||
// 1. Config
|
||||
// 2. Virtual if available
|
||||
// 3. Host
|
||||
std::string *sink = &ref->sink.host;
|
||||
if (!config::audio.sink.empty()) {
|
||||
sink = &config::audio.sink;
|
||||
}
|
||||
else if (ref->sink.null) {
|
||||
auto &null = *ref->sink.null;
|
||||
switch (stream->channelCount) {
|
||||
case 2:
|
||||
sink = &null.stereo;
|
||||
break;
|
||||
case 6:
|
||||
sink = &null.surround51;
|
||||
break;
|
||||
case 8:
|
||||
sink = &null.surround71;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only the first to start a session may change the default sink
|
||||
if (!ref->sink_flag->exchange(true, std::memory_order_acquire)) {
|
||||
ref->restore_sink = !config.flags[config_t::HOST_AUDIO];
|
||||
|
||||
// If the sink is empty (Host has no sink!), definately switch to the virtual.
|
||||
if (ref->sink.host.empty()) {
|
||||
if (control->set_sink(*sink)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If the client requests audio on the host, don't change the default sink
|
||||
else if (!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Capture takes place on this thread
|
||||
platf::adjust_thread_priority(platf::thread_priority_e::critical);
|
||||
|
||||
auto samples = std::make_shared<sample_queue_t::element_type>(30);
|
||||
std::thread thread { encodeThread, samples, config, channel_data };
|
||||
|
||||
auto fg = util::fail_guard([&]() {
|
||||
samples->stop();
|
||||
thread.join();
|
||||
|
||||
shutdown_event->view();
|
||||
});
|
||||
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
int samples_per_frame = frame_size * stream->channelCount;
|
||||
|
||||
auto mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size);
|
||||
if (!mic) {
|
||||
BOOST_LOG(error) << "Couldn't create audio input"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (!shutdown_event->peek()) {
|
||||
std::vector<std::int16_t> sample_buffer;
|
||||
sample_buffer.resize(samples_per_frame);
|
||||
|
||||
auto status = mic->sample(sample_buffer);
|
||||
switch (status) {
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
case platf::capture_e::timeout:
|
||||
continue;
|
||||
case platf::capture_e::reinit:
|
||||
mic.reset();
|
||||
mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size);
|
||||
if (!mic) {
|
||||
BOOST_LOG(error) << "Couldn't re-initialize audio input"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
samples->raise(std::move(sample_buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int map_stream(int channels, bool quality) {
|
||||
int shift = quality ? 1 : 0;
|
||||
switch(channels) {
|
||||
case 2:
|
||||
return STEREO + shift;
|
||||
case 6:
|
||||
return SURROUND51 + shift;
|
||||
case 8:
|
||||
return SURROUND71 + shift;
|
||||
int
|
||||
map_stream(int channels, bool quality) {
|
||||
int shift = quality ? 1 : 0;
|
||||
switch (channels) {
|
||||
case 2:
|
||||
return STEREO + shift;
|
||||
case 6:
|
||||
return SURROUND51 + shift;
|
||||
case 8:
|
||||
return SURROUND71 + shift;
|
||||
}
|
||||
return STEREO;
|
||||
}
|
||||
return STEREO;
|
||||
}
|
||||
|
||||
int start_audio_control(audio_ctx_t &ctx) {
|
||||
auto fg = util::fail_guard([]() {
|
||||
BOOST_LOG(warning) << "There will be no audio"sv;
|
||||
});
|
||||
int
|
||||
start_audio_control(audio_ctx_t &ctx) {
|
||||
auto fg = util::fail_guard([]() {
|
||||
BOOST_LOG(warning) << "There will be no audio"sv;
|
||||
});
|
||||
|
||||
ctx.sink_flag = std::make_unique<std::atomic_bool>(false);
|
||||
ctx.sink_flag = std::make_unique<std::atomic_bool>(false);
|
||||
|
||||
// The default sink has not been replaced yet.
|
||||
ctx.restore_sink = false;
|
||||
// The default sink has not been replaced yet.
|
||||
ctx.restore_sink = false;
|
||||
|
||||
if(!(ctx.control = platf::audio_control())) {
|
||||
if (!(ctx.control = platf::audio_control())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto sink = ctx.control->sink_info();
|
||||
if (!sink) {
|
||||
// Let the calling code know it failed
|
||||
ctx.control.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx.sink = std::move(*sink);
|
||||
|
||||
fg.disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto sink = ctx.control->sink_info();
|
||||
if(!sink) {
|
||||
// Let the calling code know it failed
|
||||
ctx.control.reset();
|
||||
return 0;
|
||||
void
|
||||
stop_audio_control(audio_ctx_t &ctx) {
|
||||
// restore audio-sink if applicable
|
||||
if (!ctx.restore_sink) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink;
|
||||
if (!sink.empty()) {
|
||||
// Best effort, it's allowed to fail
|
||||
ctx.control->set_sink(sink);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.sink = std::move(*sink);
|
||||
|
||||
fg.disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stop_audio_control(audio_ctx_t &ctx) {
|
||||
// restore audio-sink if applicable
|
||||
if(!ctx.restore_sink) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink;
|
||||
if(!sink.empty()) {
|
||||
// Best effort, it's allowed to fail
|
||||
ctx.control->set_sink(sink);
|
||||
}
|
||||
}
|
||||
} // namespace audio
|
||||
} // namespace audio
|
||||
|
||||
71
src/audio.h
71
src/audio.h
@@ -4,44 +4,45 @@
|
||||
#include "thread_safe.h"
|
||||
#include "utility.h"
|
||||
namespace audio {
|
||||
enum stream_config_e : int {
|
||||
STEREO,
|
||||
HIGH_STEREO,
|
||||
SURROUND51,
|
||||
HIGH_SURROUND51,
|
||||
SURROUND71,
|
||||
HIGH_SURROUND71,
|
||||
MAX_STREAM_CONFIG
|
||||
};
|
||||
|
||||
struct opus_stream_config_t {
|
||||
std::int32_t sampleRate;
|
||||
int channelCount;
|
||||
int streams;
|
||||
int coupledStreams;
|
||||
const std::uint8_t *mapping;
|
||||
int bitrate;
|
||||
};
|
||||
|
||||
extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG];
|
||||
|
||||
struct config_t {
|
||||
enum flags_e : int {
|
||||
HIGH_QUALITY,
|
||||
HOST_AUDIO,
|
||||
MAX_FLAGS
|
||||
enum stream_config_e : int {
|
||||
STEREO,
|
||||
HIGH_STEREO,
|
||||
SURROUND51,
|
||||
HIGH_SURROUND51,
|
||||
SURROUND71,
|
||||
HIGH_SURROUND71,
|
||||
MAX_STREAM_CONFIG
|
||||
};
|
||||
|
||||
int packetDuration;
|
||||
int channels;
|
||||
int mask;
|
||||
struct opus_stream_config_t {
|
||||
std::int32_t sampleRate;
|
||||
int channelCount;
|
||||
int streams;
|
||||
int coupledStreams;
|
||||
const std::uint8_t *mapping;
|
||||
int bitrate;
|
||||
};
|
||||
|
||||
std::bitset<MAX_FLAGS> flags;
|
||||
};
|
||||
extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG];
|
||||
|
||||
using buffer_t = util::buffer_t<std::uint8_t>;
|
||||
using packet_t = std::pair<void *, buffer_t>;
|
||||
void capture(safe::mail_t mail, config_t config, void *channel_data);
|
||||
} // namespace audio
|
||||
struct config_t {
|
||||
enum flags_e : int {
|
||||
HIGH_QUALITY,
|
||||
HOST_AUDIO,
|
||||
MAX_FLAGS
|
||||
};
|
||||
|
||||
int packetDuration;
|
||||
int channels;
|
||||
int mask;
|
||||
|
||||
std::bitset<MAX_FLAGS> flags;
|
||||
};
|
||||
|
||||
using buffer_t = util::buffer_t<std::uint8_t>;
|
||||
using packet_t = std::pair<void *, buffer_t>;
|
||||
void
|
||||
capture(safe::mail_t mail, config_t config, void *channel_data);
|
||||
} // namespace audio
|
||||
|
||||
#endif
|
||||
|
||||
490
src/cbs.cpp
490
src/cbs.cpp
@@ -12,289 +12,293 @@ extern "C" {
|
||||
|
||||
using namespace std::literals;
|
||||
namespace cbs {
|
||||
void close(CodedBitstreamContext *c) {
|
||||
ff_cbs_close(&c);
|
||||
}
|
||||
|
||||
using ctx_t = util::safe_ptr<CodedBitstreamContext, close>;
|
||||
|
||||
class frag_t : public CodedBitstreamFragment {
|
||||
public:
|
||||
frag_t(frag_t &&o) {
|
||||
std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), (std::uint8_t *)this);
|
||||
|
||||
o.data = nullptr;
|
||||
o.units = nullptr;
|
||||
};
|
||||
|
||||
frag_t() {
|
||||
std::fill_n((std::uint8_t *)this, sizeof(*this), 0);
|
||||
void
|
||||
close(CodedBitstreamContext *c) {
|
||||
ff_cbs_close(&c);
|
||||
}
|
||||
|
||||
frag_t &operator=(frag_t &&o) {
|
||||
std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), (std::uint8_t *)this);
|
||||
using ctx_t = util::safe_ptr<CodedBitstreamContext, close>;
|
||||
|
||||
o.data = nullptr;
|
||||
o.units = nullptr;
|
||||
class frag_t: public CodedBitstreamFragment {
|
||||
public:
|
||||
frag_t(frag_t &&o) {
|
||||
std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this);
|
||||
|
||||
return *this;
|
||||
};
|
||||
o.data = nullptr;
|
||||
o.units = nullptr;
|
||||
};
|
||||
|
||||
|
||||
~frag_t() {
|
||||
if(data || units) {
|
||||
ff_cbs_fragment_free(this);
|
||||
frag_t() {
|
||||
std::fill_n((std::uint8_t *) this, sizeof(*this), 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
util::buffer_t<std::uint8_t> write(const cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
cbs::frag_t frag;
|
||||
auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr);
|
||||
if(err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
frag_t &
|
||||
operator=(frag_t &&o) {
|
||||
std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this);
|
||||
|
||||
return {};
|
||||
}
|
||||
o.data = nullptr;
|
||||
o.units = nullptr;
|
||||
|
||||
err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag);
|
||||
if(err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
return *this;
|
||||
};
|
||||
|
||||
return {};
|
||||
}
|
||||
~frag_t() {
|
||||
if (data || units) {
|
||||
ff_cbs_fragment_free(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// frag.data_size * 8 - frag.data_bit_padding == bits in fragment
|
||||
util::buffer_t<std::uint8_t> data { frag.data_size };
|
||||
std::copy_n(frag.data, frag.data_size, std::begin(data));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t> write(std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
cbs::ctx_t cbs_ctx;
|
||||
ff_cbs_init(&cbs_ctx, codec_id, nullptr);
|
||||
|
||||
return write(cbs_ctx, nal, uh, codec_id);
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
||||
H264RawSPS sps {};
|
||||
|
||||
/* b_per_p == ctx->max_b_frames for h264 */
|
||||
/* desired_b_depth == avoption("b_depth") == 1 */
|
||||
/* max_b_depth == std::min(av_log2(ctx->b_per_p) + 1, desired_b_depth) ==> 1 */
|
||||
auto max_b_depth = 1;
|
||||
auto dpb_frame = ctx->gop_size == 1 ? 0 : 1 + max_b_depth;
|
||||
auto mb_width = (FFALIGN(ctx->width, 16) / 16) * 16;
|
||||
auto mb_height = (FFALIGN(ctx->height, 16) / 16) * 16;
|
||||
|
||||
|
||||
sps.nal_unit_header.nal_ref_idc = 3;
|
||||
sps.nal_unit_header.nal_unit_type = H264_NAL_SPS;
|
||||
|
||||
sps.profile_idc = FF_PROFILE_H264_HIGH & 0xFF;
|
||||
|
||||
sps.constraint_set1_flag = 1;
|
||||
|
||||
if(ctx->level != FF_LEVEL_UNKNOWN) {
|
||||
sps.level_idc = ctx->level;
|
||||
}
|
||||
else {
|
||||
auto framerate = ctx->framerate;
|
||||
|
||||
auto level = ff_h264_guess_level(
|
||||
sps.profile_idc,
|
||||
ctx->bit_rate,
|
||||
framerate.num / framerate.den,
|
||||
mb_width,
|
||||
mb_height,
|
||||
dpb_frame);
|
||||
|
||||
if(!level) {
|
||||
BOOST_LOG(error) << "Could not guess h264 level"sv;
|
||||
util::buffer_t<std::uint8_t>
|
||||
write(const cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
cbs::frag_t frag;
|
||||
auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
sps.level_idc = level->level_idc;
|
||||
|
||||
err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// frag.data_size * 8 - frag.data_bit_padding == bits in fragment
|
||||
util::buffer_t<std::uint8_t> data { frag.data_size };
|
||||
std::copy_n(frag.data, frag.data_size, std::begin(data));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
sps.seq_parameter_set_id = 0;
|
||||
sps.chroma_format_idc = 1;
|
||||
util::buffer_t<std::uint8_t>
|
||||
write(std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
cbs::ctx_t cbs_ctx;
|
||||
ff_cbs_init(&cbs_ctx, codec_id, nullptr);
|
||||
|
||||
sps.log2_max_frame_num_minus4 = 3; // 4;
|
||||
sps.pic_order_cnt_type = 0;
|
||||
sps.log2_max_pic_order_cnt_lsb_minus4 = 0; // 4;
|
||||
|
||||
sps.max_num_ref_frames = dpb_frame;
|
||||
|
||||
sps.pic_width_in_mbs_minus1 = mb_width / 16 - 1;
|
||||
sps.pic_height_in_map_units_minus1 = mb_height / 16 - 1;
|
||||
|
||||
sps.frame_mbs_only_flag = 1;
|
||||
sps.direct_8x8_inference_flag = 1;
|
||||
|
||||
if(ctx->width != mb_width || ctx->height != mb_height) {
|
||||
sps.frame_cropping_flag = 1;
|
||||
sps.frame_crop_left_offset = 0;
|
||||
sps.frame_crop_top_offset = 0;
|
||||
sps.frame_crop_right_offset = (mb_width - ctx->width) / 2;
|
||||
sps.frame_crop_bottom_offset = (mb_height - ctx->height) / 2;
|
||||
return write(cbs_ctx, nal, uh, codec_id);
|
||||
}
|
||||
|
||||
sps.vui_parameters_present_flag = 1;
|
||||
util::buffer_t<std::uint8_t>
|
||||
make_sps_h264(const AVCodecContext *ctx) {
|
||||
H264RawSPS sps {};
|
||||
|
||||
auto &vui = sps.vui;
|
||||
/* b_per_p == ctx->max_b_frames for h264 */
|
||||
/* desired_b_depth == avoption("b_depth") == 1 */
|
||||
/* max_b_depth == std::min(av_log2(ctx->b_per_p) + 1, desired_b_depth) ==> 1 */
|
||||
auto max_b_depth = 1;
|
||||
auto dpb_frame = ctx->gop_size == 1 ? 0 : 1 + max_b_depth;
|
||||
auto mb_width = (FFALIGN(ctx->width, 16) / 16) * 16;
|
||||
auto mb_height = (FFALIGN(ctx->height, 16) / 16) * 16;
|
||||
|
||||
vui.video_format = 5;
|
||||
vui.colour_description_present_flag = 1;
|
||||
vui.video_signal_type_present_flag = 1;
|
||||
vui.video_full_range_flag = ctx->color_range == AVCOL_RANGE_JPEG;
|
||||
vui.colour_primaries = ctx->color_primaries;
|
||||
vui.transfer_characteristics = ctx->color_trc;
|
||||
vui.matrix_coefficients = ctx->colorspace;
|
||||
sps.nal_unit_header.nal_ref_idc = 3;
|
||||
sps.nal_unit_header.nal_unit_type = H264_NAL_SPS;
|
||||
|
||||
vui.low_delay_hrd_flag = 1 - vui.fixed_frame_rate_flag;
|
||||
sps.profile_idc = FF_PROFILE_H264_HIGH & 0xFF;
|
||||
|
||||
vui.bitstream_restriction_flag = 1;
|
||||
vui.motion_vectors_over_pic_boundaries_flag = 1;
|
||||
vui.log2_max_mv_length_horizontal = 15;
|
||||
vui.log2_max_mv_length_vertical = 15;
|
||||
vui.max_num_reorder_frames = max_b_depth;
|
||||
vui.max_dec_frame_buffering = max_b_depth + 1;
|
||||
sps.constraint_set1_flag = 1;
|
||||
|
||||
return write(sps.nal_unit_header.nal_unit_type, (void *)&sps.nal_unit_header, AV_CODEC_ID_H264);
|
||||
}
|
||||
if (ctx->level != FF_LEVEL_UNKNOWN) {
|
||||
sps.level_idc = ctx->level;
|
||||
}
|
||||
else {
|
||||
auto framerate = ctx->framerate;
|
||||
|
||||
hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if(ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) {
|
||||
return {};
|
||||
auto level = ff_h264_guess_level(
|
||||
sps.profile_idc,
|
||||
ctx->bit_rate,
|
||||
framerate.num / framerate.den,
|
||||
mb_width,
|
||||
mb_height,
|
||||
dpb_frame);
|
||||
|
||||
if (!level) {
|
||||
BOOST_LOG(error) << "Could not guess h264 level"sv;
|
||||
|
||||
return {};
|
||||
}
|
||||
sps.level_idc = level->level_idc;
|
||||
}
|
||||
|
||||
sps.seq_parameter_set_id = 0;
|
||||
sps.chroma_format_idc = 1;
|
||||
|
||||
sps.log2_max_frame_num_minus4 = 3; // 4;
|
||||
sps.pic_order_cnt_type = 0;
|
||||
sps.log2_max_pic_order_cnt_lsb_minus4 = 0; // 4;
|
||||
|
||||
sps.max_num_ref_frames = dpb_frame;
|
||||
|
||||
sps.pic_width_in_mbs_minus1 = mb_width / 16 - 1;
|
||||
sps.pic_height_in_map_units_minus1 = mb_height / 16 - 1;
|
||||
|
||||
sps.frame_mbs_only_flag = 1;
|
||||
sps.direct_8x8_inference_flag = 1;
|
||||
|
||||
if (ctx->width != mb_width || ctx->height != mb_height) {
|
||||
sps.frame_cropping_flag = 1;
|
||||
sps.frame_crop_left_offset = 0;
|
||||
sps.frame_crop_top_offset = 0;
|
||||
sps.frame_crop_right_offset = (mb_width - ctx->width) / 2;
|
||||
sps.frame_crop_bottom_offset = (mb_height - ctx->height) / 2;
|
||||
}
|
||||
|
||||
sps.vui_parameters_present_flag = 1;
|
||||
|
||||
auto &vui = sps.vui;
|
||||
|
||||
vui.video_format = 5;
|
||||
vui.colour_description_present_flag = 1;
|
||||
vui.video_signal_type_present_flag = 1;
|
||||
vui.video_full_range_flag = ctx->color_range == AVCOL_RANGE_JPEG;
|
||||
vui.colour_primaries = ctx->color_primaries;
|
||||
vui.transfer_characteristics = ctx->color_trc;
|
||||
vui.matrix_coefficients = ctx->colorspace;
|
||||
|
||||
vui.low_delay_hrd_flag = 1 - vui.fixed_frame_rate_flag;
|
||||
|
||||
vui.bitstream_restriction_flag = 1;
|
||||
vui.motion_vectors_over_pic_boundaries_flag = 1;
|
||||
vui.log2_max_mv_length_horizontal = 15;
|
||||
vui.log2_max_mv_length_vertical = 15;
|
||||
vui.max_num_reorder_frames = max_b_depth;
|
||||
vui.max_dec_frame_buffering = max_b_depth + 1;
|
||||
|
||||
return write(sps.nal_unit_header.nal_unit_type, (void *) &sps.nal_unit_header, AV_CODEC_ID_H264);
|
||||
}
|
||||
|
||||
cbs::frag_t frag;
|
||||
hevc_t
|
||||
make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if (ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if(err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
cbs::frag_t frag;
|
||||
|
||||
return {};
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto vps_p = ((CodedBitstreamH265Context *) ctx->priv_data)->active_vps;
|
||||
auto sps_p = ((CodedBitstreamH265Context *) ctx->priv_data)->active_sps;
|
||||
|
||||
H265RawSPS sps { *sps_p };
|
||||
H265RawVPS vps { *vps_p };
|
||||
|
||||
vps.profile_tier_level.general_profile_compatibility_flag[4] = 1;
|
||||
sps.profile_tier_level.general_profile_compatibility_flag[4] = 1;
|
||||
|
||||
auto &vui = sps.vui;
|
||||
std::memset(&vui, 0, sizeof(vui));
|
||||
|
||||
sps.vui_parameters_present_flag = 1;
|
||||
|
||||
// skip sample aspect ratio
|
||||
|
||||
vui.video_format = 5;
|
||||
vui.colour_description_present_flag = 1;
|
||||
vui.video_signal_type_present_flag = 1;
|
||||
vui.video_full_range_flag = avctx->color_range == AVCOL_RANGE_JPEG;
|
||||
vui.colour_primaries = avctx->color_primaries;
|
||||
vui.transfer_characteristics = avctx->color_trc;
|
||||
vui.matrix_coefficients = avctx->colorspace;
|
||||
|
||||
vui.vui_timing_info_present_flag = vps.vps_timing_info_present_flag;
|
||||
vui.vui_num_units_in_tick = vps.vps_num_units_in_tick;
|
||||
vui.vui_time_scale = vps.vps_time_scale;
|
||||
vui.vui_poc_proportional_to_timing_flag = vps.vps_poc_proportional_to_timing_flag;
|
||||
vui.vui_num_ticks_poc_diff_one_minus1 = vps.vps_num_ticks_poc_diff_one_minus1;
|
||||
vui.vui_hrd_parameters_present_flag = 0;
|
||||
|
||||
vui.bitstream_restriction_flag = 1;
|
||||
vui.motion_vectors_over_pic_boundaries_flag = 1;
|
||||
vui.restricted_ref_pic_lists_flag = 1;
|
||||
vui.max_bytes_per_pic_denom = 0;
|
||||
vui.max_bits_per_min_cu_denom = 0;
|
||||
vui.log2_max_mv_length_horizontal = 15;
|
||||
vui.log2_max_mv_length_vertical = 15;
|
||||
|
||||
cbs::ctx_t write_ctx;
|
||||
ff_cbs_init(&write_ctx, AV_CODEC_ID_H265, nullptr);
|
||||
|
||||
return hevc_t {
|
||||
nal_t {
|
||||
write(write_ctx, vps.nal_unit_header.nal_unit_type, (void *) &vps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, vps_p->nal_unit_header.nal_unit_type, (void *) &vps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
},
|
||||
|
||||
nal_t {
|
||||
write(write_ctx, sps.nal_unit_header.nal_unit_type, (void *) &sps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *) &sps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t>
|
||||
read_sps_h264(const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if (ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto vps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_vps;
|
||||
auto sps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps;
|
||||
cbs::frag_t frag;
|
||||
|
||||
H265RawSPS sps { *sps_p };
|
||||
H265RawVPS vps { *vps_p };
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, &*packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
vps.profile_tier_level.general_profile_compatibility_flag[4] = 1;
|
||||
sps.profile_tier_level.general_profile_compatibility_flag[4] = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto &vui = sps.vui;
|
||||
std::memset(&vui, 0, sizeof(vui));
|
||||
|
||||
sps.vui_parameters_present_flag = 1;
|
||||
|
||||
// skip sample aspect ratio
|
||||
|
||||
vui.video_format = 5;
|
||||
vui.colour_description_present_flag = 1;
|
||||
vui.video_signal_type_present_flag = 1;
|
||||
vui.video_full_range_flag = avctx->color_range == AVCOL_RANGE_JPEG;
|
||||
vui.colour_primaries = avctx->color_primaries;
|
||||
vui.transfer_characteristics = avctx->color_trc;
|
||||
vui.matrix_coefficients = avctx->colorspace;
|
||||
|
||||
|
||||
vui.vui_timing_info_present_flag = vps.vps_timing_info_present_flag;
|
||||
vui.vui_num_units_in_tick = vps.vps_num_units_in_tick;
|
||||
vui.vui_time_scale = vps.vps_time_scale;
|
||||
vui.vui_poc_proportional_to_timing_flag = vps.vps_poc_proportional_to_timing_flag;
|
||||
vui.vui_num_ticks_poc_diff_one_minus1 = vps.vps_num_ticks_poc_diff_one_minus1;
|
||||
vui.vui_hrd_parameters_present_flag = 0;
|
||||
|
||||
vui.bitstream_restriction_flag = 1;
|
||||
vui.motion_vectors_over_pic_boundaries_flag = 1;
|
||||
vui.restricted_ref_pic_lists_flag = 1;
|
||||
vui.max_bytes_per_pic_denom = 0;
|
||||
vui.max_bits_per_min_cu_denom = 0;
|
||||
vui.log2_max_mv_length_horizontal = 15;
|
||||
vui.log2_max_mv_length_vertical = 15;
|
||||
|
||||
cbs::ctx_t write_ctx;
|
||||
ff_cbs_init(&write_ctx, AV_CODEC_ID_H265, nullptr);
|
||||
|
||||
|
||||
return hevc_t {
|
||||
nal_t {
|
||||
write(write_ctx, vps.nal_unit_header.nal_unit_type, (void *)&vps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, vps_p->nal_unit_header.nal_unit_type, (void *)&vps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
},
|
||||
|
||||
nal_t {
|
||||
write(write_ctx, sps.nal_unit_header.nal_unit_type, (void *)&sps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *)&sps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t> read_sps_h264(const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if(ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) {
|
||||
return {};
|
||||
auto h264 = (H264RawNALUnitHeader *) ((CodedBitstreamH264Context *) ctx->priv_data)->active_sps;
|
||||
return write(h264->nal_unit_type, (void *) h264, AV_CODEC_ID_H264);
|
||||
}
|
||||
|
||||
cbs::frag_t frag;
|
||||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, &*packet);
|
||||
if(err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
h264_t
|
||||
make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet) {
|
||||
return h264_t {
|
||||
make_sps_h264(ctx),
|
||||
read_sps_h264(packet),
|
||||
};
|
||||
}
|
||||
|
||||
auto h264 = (H264RawNALUnitHeader *)((CodedBitstreamH264Context *)ctx->priv_data)->active_sps;
|
||||
return write(h264->nal_unit_type, (void *)h264, AV_CODEC_ID_H264);
|
||||
}
|
||||
|
||||
h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet) {
|
||||
return h264_t {
|
||||
make_sps_h264(ctx),
|
||||
read_sps_h264(packet),
|
||||
};
|
||||
}
|
||||
|
||||
bool validate_sps(const AVPacket *packet, int codec_id) {
|
||||
cbs::ctx_t ctx;
|
||||
if(ff_cbs_init(&ctx, (AVCodecID)codec_id, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cbs::frag_t frag;
|
||||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if(err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(codec_id == AV_CODEC_ID_H264) {
|
||||
auto h264 = (CodedBitstreamH264Context *)ctx->priv_data;
|
||||
|
||||
if(!h264->active_sps->vui_parameters_present_flag) {
|
||||
bool
|
||||
validate_sps(const AVPacket *packet, int codec_id) {
|
||||
cbs::ctx_t ctx;
|
||||
if (ff_cbs_init(&ctx, (AVCodecID) codec_id, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
cbs::frag_t frag;
|
||||
|
||||
return ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps->vui_parameters_present_flag;
|
||||
}
|
||||
} // namespace cbs
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (codec_id == AV_CODEC_ID_H264) {
|
||||
auto h264 = (CodedBitstreamH264Context *) ctx->priv_data;
|
||||
|
||||
if (!h264->active_sps->vui_parameters_present_flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((CodedBitstreamH265Context *) ctx->priv_data)->active_sps->vui_parameters_present_flag;
|
||||
}
|
||||
} // namespace cbs
|
||||
|
||||
35
src/cbs.h
35
src/cbs.h
@@ -8,27 +8,30 @@ struct AVCodecContext;
|
||||
|
||||
namespace cbs {
|
||||
|
||||
struct nal_t {
|
||||
util::buffer_t<std::uint8_t> _new;
|
||||
util::buffer_t<std::uint8_t> old;
|
||||
};
|
||||
struct nal_t {
|
||||
util::buffer_t<std::uint8_t> _new;
|
||||
util::buffer_t<std::uint8_t> old;
|
||||
};
|
||||
|
||||
struct hevc_t {
|
||||
nal_t vps;
|
||||
nal_t sps;
|
||||
};
|
||||
struct hevc_t {
|
||||
nal_t vps;
|
||||
nal_t sps;
|
||||
};
|
||||
|
||||
struct h264_t {
|
||||
nal_t sps;
|
||||
};
|
||||
struct h264_t {
|
||||
nal_t sps;
|
||||
};
|
||||
|
||||
hevc_t make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
hevc_t
|
||||
make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
h264_t
|
||||
make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Check if SPS->VUI is present
|
||||
*/
|
||||
bool validate_sps(const AVPacket *packet, int codec_id);
|
||||
} // namespace cbs
|
||||
bool
|
||||
validate_sps(const AVPacket *packet, int codec_id);
|
||||
} // namespace cbs
|
||||
|
||||
#endif
|
||||
|
||||
1988
src/config.cpp
1988
src/config.cpp
File diff suppressed because it is too large
Load Diff
228
src/config.h
228
src/config.h
@@ -9,135 +9,157 @@
|
||||
#include <vector>
|
||||
|
||||
namespace config {
|
||||
struct video_t {
|
||||
// ffmpeg params
|
||||
int qp; // higher == more compression and less quality
|
||||
struct video_t {
|
||||
// ffmpeg params
|
||||
int qp; // higher == more compression and less quality
|
||||
|
||||
int hevc_mode;
|
||||
int hevc_mode;
|
||||
|
||||
int min_threads; // Minimum number of threads/slices for CPU encoding
|
||||
struct {
|
||||
std::string preset;
|
||||
std::string tune;
|
||||
} sw;
|
||||
int min_threads; // Minimum number of threads/slices for CPU encoding
|
||||
struct {
|
||||
std::string sw_preset;
|
||||
std::string sw_tune;
|
||||
} sw;
|
||||
|
||||
struct {
|
||||
std::optional<int> preset;
|
||||
std::optional<int> tune;
|
||||
std::optional<int> rc;
|
||||
int coder;
|
||||
} nv;
|
||||
struct {
|
||||
std::optional<int> nv_preset;
|
||||
std::optional<int> nv_tune;
|
||||
std::optional<int> nv_rc;
|
||||
int nv_coder;
|
||||
} nv;
|
||||
|
||||
struct {
|
||||
std::optional<int> preset;
|
||||
std::optional<int> cavlc;
|
||||
} qsv;
|
||||
struct {
|
||||
std::optional<int> qsv_preset;
|
||||
std::optional<int> qsv_cavlc;
|
||||
} qsv;
|
||||
|
||||
struct {
|
||||
std::optional<int> quality_h264;
|
||||
std::optional<int> quality_hevc;
|
||||
std::optional<int> rc_h264;
|
||||
std::optional<int> rc_hevc;
|
||||
int coder;
|
||||
} amd;
|
||||
struct {
|
||||
std::optional<int> amd_quality_h264;
|
||||
std::optional<int> amd_quality_hevc;
|
||||
std::optional<int> amd_rc_h264;
|
||||
std::optional<int> amd_rc_hevc;
|
||||
std::optional<int> amd_usage_h264;
|
||||
std::optional<int> amd_usage_hevc;
|
||||
std::optional<int> amd_preanalysis;
|
||||
std::optional<int> amd_vbaq;
|
||||
int amd_coder;
|
||||
} amd;
|
||||
|
||||
struct {
|
||||
int allow_sw;
|
||||
int require_sw;
|
||||
int realtime;
|
||||
int coder;
|
||||
} vt;
|
||||
struct {
|
||||
int vt_allow_sw;
|
||||
int vt_require_sw;
|
||||
int vt_realtime;
|
||||
int vt_coder;
|
||||
} vt;
|
||||
|
||||
std::string encoder;
|
||||
std::string adapter_name;
|
||||
std::string output_name;
|
||||
bool dwmflush;
|
||||
};
|
||||
std::string capture;
|
||||
std::string encoder;
|
||||
std::string adapter_name;
|
||||
std::string output_name;
|
||||
bool dwmflush;
|
||||
};
|
||||
|
||||
struct audio_t {
|
||||
std::string sink;
|
||||
std::string virtual_sink;
|
||||
};
|
||||
struct audio_t {
|
||||
std::string sink;
|
||||
std::string virtual_sink;
|
||||
};
|
||||
|
||||
struct stream_t {
|
||||
std::chrono::milliseconds ping_timeout;
|
||||
struct stream_t {
|
||||
std::chrono::milliseconds ping_timeout;
|
||||
|
||||
std::string file_apps;
|
||||
std::string file_apps;
|
||||
|
||||
int fec_percentage;
|
||||
int fec_percentage;
|
||||
|
||||
// max unique instances of video and audio streams
|
||||
int channels;
|
||||
};
|
||||
// max unique instances of video and audio streams
|
||||
int channels;
|
||||
};
|
||||
|
||||
struct nvhttp_t {
|
||||
// Could be any of the following values:
|
||||
// pc|lan|wan
|
||||
std::string origin_pin_allowed;
|
||||
std::string origin_web_ui_allowed;
|
||||
struct nvhttp_t {
|
||||
// Could be any of the following values:
|
||||
// pc|lan|wan
|
||||
std::string origin_pin_allowed;
|
||||
std::string origin_web_ui_allowed;
|
||||
|
||||
std::string pkey; // must be 2048 bits
|
||||
std::string cert; // must be signed with a key of 2048 bits
|
||||
std::string pkey; // must be 2048 bits
|
||||
std::string cert; // must be signed with a key of 2048 bits
|
||||
|
||||
std::string sunshine_name;
|
||||
std::string sunshine_name;
|
||||
|
||||
std::string file_state;
|
||||
std::string file_state;
|
||||
|
||||
std::string external_ip;
|
||||
std::vector<std::string> resolutions;
|
||||
std::vector<int> fps;
|
||||
};
|
||||
std::string external_ip;
|
||||
std::vector<std::string> resolutions;
|
||||
std::vector<int> fps;
|
||||
};
|
||||
|
||||
struct input_t {
|
||||
std::unordered_map<int, int> keybindings;
|
||||
struct input_t {
|
||||
std::unordered_map<int, int> keybindings;
|
||||
|
||||
std::chrono::milliseconds back_button_timeout;
|
||||
std::chrono::milliseconds key_repeat_delay;
|
||||
std::chrono::duration<double> key_repeat_period;
|
||||
std::chrono::milliseconds back_button_timeout;
|
||||
std::chrono::milliseconds key_repeat_delay;
|
||||
std::chrono::duration<double> key_repeat_period;
|
||||
|
||||
std::string gamepad;
|
||||
};
|
||||
std::string gamepad;
|
||||
|
||||
namespace flag {
|
||||
enum flag_e : std::size_t {
|
||||
PIN_STDIN = 0, // Read PIN from stdin instead of http
|
||||
FRESH_STATE, // Do not load or save state
|
||||
FORCE_VIDEO_HEADER_REPLACE, // force replacing headers inside video data
|
||||
UPNP, // Try Universal Plug 'n Play
|
||||
CONST_PIN, // Use "universal" pin
|
||||
FLAG_SIZE
|
||||
};
|
||||
}
|
||||
bool keyboard;
|
||||
bool mouse;
|
||||
bool controller;
|
||||
};
|
||||
|
||||
struct sunshine_t {
|
||||
int min_log_level;
|
||||
std::bitset<flag::FLAG_SIZE> flags;
|
||||
std::string credentials_file;
|
||||
namespace flag {
|
||||
enum flag_e : std::size_t {
|
||||
PIN_STDIN = 0, // Read PIN from stdin instead of http
|
||||
FRESH_STATE, // Do not load or save state
|
||||
FORCE_VIDEO_HEADER_REPLACE, // force replacing headers inside video data
|
||||
UPNP, // Try Universal Plug 'n Play
|
||||
CONST_PIN, // Use "universal" pin
|
||||
FLAG_SIZE
|
||||
};
|
||||
}
|
||||
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string salt;
|
||||
struct prep_cmd_t {
|
||||
prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd):
|
||||
do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)) {}
|
||||
explicit prep_cmd_t(std::string &&do_cmd):
|
||||
do_cmd(std::move(do_cmd)) {}
|
||||
std::string do_cmd;
|
||||
std::string undo_cmd;
|
||||
};
|
||||
|
||||
std::string config_file;
|
||||
struct sunshine_t {
|
||||
int min_log_level;
|
||||
std::bitset<flag::FLAG_SIZE> flags;
|
||||
std::string credentials_file;
|
||||
|
||||
struct cmd_t {
|
||||
std::string name;
|
||||
int argc;
|
||||
char **argv;
|
||||
} cmd;
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string salt;
|
||||
|
||||
std::uint16_t port;
|
||||
std::string log_file;
|
||||
};
|
||||
std::string config_file;
|
||||
|
||||
extern video_t video;
|
||||
extern audio_t audio;
|
||||
extern stream_t stream;
|
||||
extern nvhttp_t nvhttp;
|
||||
extern input_t input;
|
||||
extern sunshine_t sunshine;
|
||||
struct cmd_t {
|
||||
std::string name;
|
||||
int argc;
|
||||
char **argv;
|
||||
} cmd;
|
||||
|
||||
int parse(int argc, char *argv[]);
|
||||
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content);
|
||||
} // namespace config
|
||||
std::uint16_t port;
|
||||
std::string log_file;
|
||||
|
||||
std::vector<prep_cmd_t> prep_cmds;
|
||||
};
|
||||
|
||||
extern video_t video;
|
||||
extern audio_t audio;
|
||||
extern stream_t stream;
|
||||
extern nvhttp_t nvhttp;
|
||||
extern input_t input;
|
||||
extern sunshine_t sunshine;
|
||||
|
||||
int
|
||||
parse(int argc, char *argv[]);
|
||||
std::unordered_map<std::string, std::string>
|
||||
parse_config(const std::string_view &file_content);
|
||||
} // namespace config
|
||||
#endif
|
||||
|
||||
1362
src/confighttp.cpp
1362
src/confighttp.cpp
File diff suppressed because it is too large
Load Diff
@@ -10,10 +10,29 @@
|
||||
|
||||
#define WEB_DIR SUNSHINE_ASSETS_DIR "/web/"
|
||||
|
||||
|
||||
namespace confighttp {
|
||||
constexpr auto PORT_HTTPS = 1;
|
||||
void start();
|
||||
} // namespace confighttp
|
||||
constexpr auto PORT_HTTPS = 1;
|
||||
void
|
||||
start();
|
||||
} // namespace confighttp
|
||||
|
||||
#endif // SUNSHINE_CONFIGHTTP_H
|
||||
// mime types map
|
||||
const std::map<std::string, std::string> mime_types = {
|
||||
{ "css", "text/css" },
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "ico", "image/x-icon" },
|
||||
{ "jpeg", "image/jpeg" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "js", "application/javascript" },
|
||||
{ "json", "application/json" },
|
||||
{ "png", "image/png" },
|
||||
{ "svg", "image/svg+xml" },
|
||||
{ "ttf", "font/ttf" },
|
||||
{ "txt", "text/plain" },
|
||||
{ "woff2", "font/woff2" },
|
||||
{ "xml", "text/xml" },
|
||||
};
|
||||
|
||||
#endif // SUNSHINE_CONFIGHTTP_H
|
||||
|
||||
865
src/crypto.cpp
865
src/crypto.cpp
@@ -4,486 +4,511 @@
|
||||
#include <openssl/pem.h>
|
||||
|
||||
namespace crypto {
|
||||
using asn1_string_t = util::safe_ptr<ASN1_STRING, ASN1_STRING_free>;
|
||||
using asn1_string_t = util::safe_ptr<ASN1_STRING, ASN1_STRING_free>;
|
||||
|
||||
cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx { X509_STORE_CTX_new() } {}
|
||||
void cert_chain_t::add(x509_t &&cert) {
|
||||
x509_store_t x509_store { X509_STORE_new() };
|
||||
cert_chain_t::cert_chain_t():
|
||||
_certs {}, _cert_ctx { X509_STORE_CTX_new() } {}
|
||||
void
|
||||
cert_chain_t::add(x509_t &&cert) {
|
||||
x509_store_t x509_store { X509_STORE_new() };
|
||||
|
||||
X509_STORE_add_cert(x509_store.get(), cert.get());
|
||||
_certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
|
||||
}
|
||||
|
||||
static int openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
|
||||
int err_code = X509_STORE_CTX_get_error(ctx);
|
||||
|
||||
switch(err_code) {
|
||||
// FIXME: Checking for X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY is a temporary workaround to get mmonlight-embedded to work on the raspberry pi
|
||||
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
|
||||
return 1;
|
||||
|
||||
// Expired or not-yet-valid certificates are fine. Sometimes Moonlight is running on embedded devices
|
||||
// that don't have accurate clocks (or haven't yet synchronized by the time Moonlight first runs).
|
||||
// This behavior also matches what GeForce Experience does.
|
||||
case X509_V_ERR_CERT_NOT_YET_VALID:
|
||||
case X509_V_ERR_CERT_HAS_EXPIRED:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return ok;
|
||||
X509_STORE_add_cert(x509_store.get(), cert.get());
|
||||
_certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static int
|
||||
openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
|
||||
int err_code = X509_STORE_CTX_get_error(ctx);
|
||||
|
||||
switch (err_code) {
|
||||
// Expired or not-yet-valid certificates are fine. Sometimes Moonlight is running on embedded devices
|
||||
// that don't have accurate clocks (or haven't yet synchronized by the time Moonlight first runs).
|
||||
// This behavior also matches what GeForce Experience does.
|
||||
// FIXME: Checking for X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY is a temporary workaround to get moonlight-embedded to work on the raspberry pi
|
||||
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
|
||||
case X509_V_ERR_CERT_NOT_YET_VALID:
|
||||
case X509_V_ERR_CERT_HAS_EXPIRED:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When certificates from two or more instances of Moonlight have been added to x509_store_t,
|
||||
* only one of them will be verified by X509_verify_cert, resulting in only a single instance of
|
||||
* Moonlight to be able to use Sunshine
|
||||
*
|
||||
* To circumvent this, x509_store_t instance will be created for each instance of the certificates.
|
||||
*/
|
||||
const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
int err_code = 0;
|
||||
for(auto &[_, x509_store] : _certs) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
X509_STORE_CTX_cleanup(_cert_ctx.get());
|
||||
});
|
||||
const char *
|
||||
cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
int err_code = 0;
|
||||
for (auto &[_, x509_store] : _certs) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
X509_STORE_CTX_cleanup(_cert_ctx.get());
|
||||
});
|
||||
|
||||
X509_STORE_CTX_init(_cert_ctx.get(), x509_store.get(), cert, nullptr);
|
||||
X509_STORE_CTX_set_verify_cb(_cert_ctx.get(), openssl_verify_cb);
|
||||
X509_STORE_CTX_init(_cert_ctx.get(), x509_store.get(), cert, nullptr);
|
||||
X509_STORE_CTX_set_verify_cb(_cert_ctx.get(), openssl_verify_cb);
|
||||
|
||||
// We don't care to validate the entire chain for the purposes of client auth.
|
||||
// Some versions of clients forked from Moonlight Embedded produce client certs
|
||||
// that OpenSSL doesn't detect as self-signed due to some X509v3 extensions.
|
||||
X509_STORE_CTX_set_flags(_cert_ctx.get(), X509_V_FLAG_PARTIAL_CHAIN);
|
||||
// We don't care to validate the entire chain for the purposes of client auth.
|
||||
// Some versions of clients forked from Moonlight Embedded produce client certs
|
||||
// that OpenSSL doesn't detect as self-signed due to some X509v3 extensions.
|
||||
X509_STORE_CTX_set_flags(_cert_ctx.get(), X509_V_FLAG_PARTIAL_CHAIN);
|
||||
|
||||
auto err = X509_verify_cert(_cert_ctx.get());
|
||||
auto err = X509_verify_cert(_cert_ctx.get());
|
||||
|
||||
if(err == 1) {
|
||||
return nullptr;
|
||||
if (err == 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
err_code = X509_STORE_CTX_get_error(_cert_ctx.get());
|
||||
|
||||
if (err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) {
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
err_code = X509_STORE_CTX_get_error(_cert_ctx.get());
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
}
|
||||
|
||||
if(err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) {
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
namespace cipher {
|
||||
|
||||
static int
|
||||
init_decrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
if (!ctx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_encrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_encrypt_cbc(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv) {
|
||||
if (!decrypt_ctx && init_decrypt_gcm(decrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if (EVP_DecryptInit_ex(decrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cipher = tagged_cipher.substr(tag_size);
|
||||
auto tag = tagged_cipher.substr(0, tag_size);
|
||||
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
|
||||
int size;
|
||||
if (EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *) cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(decrypt_ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char *>(tag.data())) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int len = size;
|
||||
if (EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
plaintext.resize(size + len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) {
|
||||
if (!encrypt_ctx && init_encrypt_gcm(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if (EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto tag = tagged_cipher;
|
||||
auto cipher = tag + tag_size;
|
||||
|
||||
int len;
|
||||
int size = round_to_pkcs7_padded(plaintext.size());
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if (EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *) plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// GCM encryption won't ever fill ciphertext here but we have to call it anyway
|
||||
if (EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(encrypt_ctx.get(), EVP_CTRL_GCM_GET_TAG, tag_size, tag) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len + size;
|
||||
}
|
||||
|
||||
int
|
||||
ecb_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext) {
|
||||
int len;
|
||||
|
||||
auto fg = util::fail_guard([this]() {
|
||||
EVP_CIPHER_CTX_reset(decrypt_ctx.get());
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if (EVP_DecryptInit_ex(decrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(decrypt_ctx.get(), padding);
|
||||
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
auto size = (int) plaintext.size();
|
||||
// Decrypt into the caller's buffer, leaving room for the auth tag to be prepended
|
||||
if (EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *) cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data(), &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
plaintext.resize(len + size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ecb_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
EVP_CIPHER_CTX_reset(encrypt_ctx.get());
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if (EVP_EncryptInit_ex(encrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(encrypt_ctx.get(), padding);
|
||||
|
||||
int len;
|
||||
|
||||
cipher.resize((plaintext.size() + 15) / 16 * 16);
|
||||
auto size = (int) cipher.size();
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if (EVP_EncryptUpdate(encrypt_ctx.get(), cipher.data(), &size, (const std::uint8_t *) plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cipher.resize(len + size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv) {
|
||||
if (!encrypt_ctx && init_encrypt_cbc(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if (EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int len;
|
||||
|
||||
int size = plaintext.size(); // round_to_pkcs7_padded(plaintext.size());
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if (EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *) plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size + len;
|
||||
}
|
||||
|
||||
ecb_t::ecb_t(const aes_t &key, bool padding):
|
||||
cipher_t { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding } {}
|
||||
|
||||
cbc_t::cbc_t(const aes_t &key, bool padding):
|
||||
cipher_t { nullptr, nullptr, key, padding } {}
|
||||
|
||||
gcm_t::gcm_t(const crypto::aes_t &key, bool padding):
|
||||
cipher_t { nullptr, nullptr, key, padding } {}
|
||||
|
||||
} // namespace cipher
|
||||
|
||||
aes_t
|
||||
gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin) {
|
||||
aes_t key;
|
||||
|
||||
std::string salt_pin;
|
||||
salt_pin.reserve(salt.size() + pin.size());
|
||||
|
||||
salt_pin.insert(std::end(salt_pin), std::begin(salt), std::end(salt));
|
||||
salt_pin.insert(std::end(salt_pin), std::begin(pin), std::end(pin));
|
||||
|
||||
auto hsh = hash(salt_pin);
|
||||
|
||||
std::copy(std::begin(hsh), std::begin(hsh) + key.size(), std::begin(key));
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
}
|
||||
|
||||
namespace cipher {
|
||||
|
||||
static int init_decrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
if(!ctx) {
|
||||
return -1;
|
||||
sha256_t
|
||||
hash(const std::string_view &plaintext) {
|
||||
sha256_t hsh;
|
||||
EVP_Digest(plaintext.data(), plaintext.size(), hsh.data(), nullptr, EVP_sha256(), nullptr);
|
||||
return hsh;
|
||||
}
|
||||
|
||||
if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
x509_t
|
||||
x509(const std::string_view &x) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
|
||||
BIO_write(io.get(), x.data(), x.size());
|
||||
|
||||
x509_t p;
|
||||
PEM_read_bio_X509(io.get(), &p, nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
return -1;
|
||||
pkey_t
|
||||
pkey(const std::string_view &k) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
|
||||
BIO_write(io.get(), k.data(), k.size());
|
||||
|
||||
pkey_t p = nullptr;
|
||||
PEM_read_bio_PrivateKey(io.get(), &p, nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
if(EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
std::string
|
||||
pem(x509_t &x509) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
|
||||
return 0;
|
||||
}
|
||||
PEM_write_bio_X509(bio.get(), x509.get());
|
||||
BUF_MEM *mem_ptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
static int init_encrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if(EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
return -1;
|
||||
std::string
|
||||
pem(pkey_t &pkey) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
|
||||
PEM_write_bio_PrivateKey(bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
|
||||
BUF_MEM *mem_ptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
}
|
||||
|
||||
if(EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
std::string_view
|
||||
signature(const x509_t &x) {
|
||||
// X509_ALGOR *_ = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
const ASN1_BIT_STRING *asn1 = nullptr;
|
||||
X509_get0_signature(&asn1, nullptr, x.get());
|
||||
|
||||
static int init_encrypt_cbc(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if(EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
return { (const char *) asn1->data, (std::size_t) asn1->length };
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
std::string
|
||||
rand(std::size_t bytes) {
|
||||
std::string r;
|
||||
r.resize(bytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
RAND_bytes((uint8_t *) r.data(), r.size());
|
||||
|
||||
int gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv) {
|
||||
if(!decrypt_ctx && init_decrypt_gcm(decrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if(EVP_DecryptInit_ex(decrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return false;
|
||||
std::vector<uint8_t>
|
||||
sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) {
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
|
||||
if (EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, pkey.get()) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (EVP_DigestSignUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t slen = digest_size;
|
||||
|
||||
std::vector<uint8_t> digest;
|
||||
digest.resize(slen);
|
||||
|
||||
if (EVP_DigestSignFinal(ctx.get(), digest.data(), &slen) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
auto cipher = tagged_cipher.substr(tag_size);
|
||||
auto tag = tagged_cipher.substr(0, tag_size);
|
||||
creds_t
|
||||
gen_creds(const std::string_view &cn, std::uint32_t key_bits) {
|
||||
x509_t x509 { X509_new() };
|
||||
pkey_ctx_t ctx { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) };
|
||||
pkey_t pkey;
|
||||
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
EVP_PKEY_keygen_init(ctx.get());
|
||||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), key_bits);
|
||||
EVP_PKEY_keygen(ctx.get(), &pkey);
|
||||
|
||||
int size;
|
||||
if(EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
X509_set_version(x509.get(), 2);
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(decrypt_ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char *>(tag.data())) != 1) {
|
||||
return -1;
|
||||
}
|
||||
// Generate a real serial number to avoid SEC_ERROR_REUSED_ISSUER_AND_SERIAL with Firefox
|
||||
bignum_t serial { BN_new() };
|
||||
BN_rand(serial.get(), 159, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); // 159 bits to fit in 20 bytes in DER format
|
||||
BN_set_negative(serial.get(), 0); // Serial numbers must be positive
|
||||
BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get()));
|
||||
|
||||
int len = size;
|
||||
if(EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
plaintext.resize(size + len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) {
|
||||
if(!encrypt_ctx && init_encrypt_gcm(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if(EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto tag = tagged_cipher;
|
||||
auto cipher = tag + tag_size;
|
||||
|
||||
int len;
|
||||
int size = round_to_pkcs7_padded(plaintext.size());
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if(EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// GCM encryption won't ever fill ciphertext here but we have to call it anyway
|
||||
if(EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(encrypt_ctx.get(), EVP_CTRL_GCM_GET_TAG, tag_size, tag) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len + size;
|
||||
}
|
||||
|
||||
int ecb_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext) {
|
||||
int len;
|
||||
|
||||
auto fg = util::fail_guard([this]() {
|
||||
EVP_CIPHER_CTX_reset(decrypt_ctx.get());
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if(EVP_DecryptInit_ex(decrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(decrypt_ctx.get(), padding);
|
||||
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
auto size = (int)plaintext.size();
|
||||
// Decrypt into the caller's buffer, leaving room for the auth tag to be prepended
|
||||
if(EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data(), &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
plaintext.resize(len + size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ecb_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
EVP_CIPHER_CTX_reset(encrypt_ctx.get());
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if(EVP_EncryptInit_ex(encrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(encrypt_ctx.get(), padding);
|
||||
|
||||
int len;
|
||||
|
||||
cipher.resize((plaintext.size() + 15) / 16 * 16);
|
||||
auto size = (int)cipher.size();
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if(EVP_EncryptUpdate(encrypt_ctx.get(), cipher.data(), &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cipher.resize(len + size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv) {
|
||||
if(!encrypt_ctx && init_encrypt_cbc(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if(EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int len;
|
||||
|
||||
int size = plaintext.size(); // round_to_pkcs7_padded(plaintext.size());
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if(EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size + len;
|
||||
}
|
||||
|
||||
ecb_t::ecb_t(const aes_t &key, bool padding)
|
||||
: cipher_t { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding } {}
|
||||
|
||||
cbc_t::cbc_t(const aes_t &key, bool padding)
|
||||
: cipher_t { nullptr, nullptr, key, padding } {}
|
||||
|
||||
gcm_t::gcm_t(const crypto::aes_t &key, bool padding)
|
||||
: cipher_t { nullptr, nullptr, key, padding } {}
|
||||
|
||||
} // namespace cipher
|
||||
|
||||
aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin) {
|
||||
aes_t key;
|
||||
|
||||
std::string salt_pin;
|
||||
salt_pin.reserve(salt.size() + pin.size());
|
||||
|
||||
salt_pin.insert(std::end(salt_pin), std::begin(salt), std::end(salt));
|
||||
salt_pin.insert(std::end(salt_pin), std::begin(pin), std::end(pin));
|
||||
|
||||
auto hsh = hash(salt_pin);
|
||||
|
||||
std::copy(std::begin(hsh), std::begin(hsh) + key.size(), std::begin(key));
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
sha256_t hash(const std::string_view &plaintext) {
|
||||
sha256_t hsh;
|
||||
EVP_Digest(plaintext.data(), plaintext.size(), hsh.data(), nullptr, EVP_sha256(), nullptr);
|
||||
return hsh;
|
||||
}
|
||||
|
||||
x509_t x509(const std::string_view &x) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
|
||||
BIO_write(io.get(), x.data(), x.size());
|
||||
|
||||
x509_t p;
|
||||
PEM_read_bio_X509(io.get(), &p, nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
pkey_t pkey(const std::string_view &k) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
|
||||
BIO_write(io.get(), k.data(), k.size());
|
||||
|
||||
pkey_t p = nullptr;
|
||||
PEM_read_bio_PrivateKey(io.get(), &p, nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
std::string pem(x509_t &x509) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
|
||||
PEM_write_bio_X509(bio.get(), x509.get());
|
||||
BUF_MEM *mem_ptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
}
|
||||
|
||||
std::string pem(pkey_t &pkey) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
|
||||
PEM_write_bio_PrivateKey(bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
|
||||
BUF_MEM *mem_ptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
}
|
||||
|
||||
std::string_view signature(const x509_t &x) {
|
||||
// X509_ALGOR *_ = nullptr;
|
||||
|
||||
const ASN1_BIT_STRING *asn1 = nullptr;
|
||||
X509_get0_signature(&asn1, nullptr, x.get());
|
||||
|
||||
return { (const char *)asn1->data, (std::size_t)asn1->length };
|
||||
}
|
||||
|
||||
std::string rand(std::size_t bytes) {
|
||||
std::string r;
|
||||
r.resize(bytes);
|
||||
|
||||
RAND_bytes((uint8_t *)r.data(), r.size());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) {
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
|
||||
if(EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, pkey.get()) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if(EVP_DigestSignUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t slen = digest_size;
|
||||
|
||||
std::vector<uint8_t> digest;
|
||||
digest.resize(slen);
|
||||
|
||||
if(EVP_DigestSignFinal(ctx.get(), digest.data(), &slen) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) {
|
||||
x509_t x509 { X509_new() };
|
||||
pkey_ctx_t ctx { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) };
|
||||
pkey_t pkey;
|
||||
|
||||
EVP_PKEY_keygen_init(ctx.get());
|
||||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), key_bits);
|
||||
EVP_PKEY_keygen(ctx.get(), &pkey);
|
||||
|
||||
X509_set_version(x509.get(), 2);
|
||||
|
||||
// Generate a real serial number to avoid SEC_ERROR_REUSED_ISSUER_AND_SERIAL with Firefox
|
||||
bignum_t serial { BN_new() };
|
||||
BN_rand(serial.get(), 159, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); // 159 bits to fit in 20 bytes in DER format
|
||||
BN_set_negative(serial.get(), 0); // Serial numbers must be positive
|
||||
BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get()));
|
||||
|
||||
constexpr auto year = 60 * 60 * 24 * 365;
|
||||
constexpr auto year = 60 * 60 * 24 * 365;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
|
||||
X509_gmtime_adj(X509_get_notAfter(x509.get()), 20 * year);
|
||||
X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
|
||||
X509_gmtime_adj(X509_get_notAfter(x509.get()), 20 * year);
|
||||
#else
|
||||
asn1_string_t not_before { ASN1_STRING_dup(X509_get0_notBefore(x509.get())) };
|
||||
asn1_string_t not_after { ASN1_STRING_dup(X509_get0_notAfter(x509.get())) };
|
||||
asn1_string_t not_before { ASN1_STRING_dup(X509_get0_notBefore(x509.get())) };
|
||||
asn1_string_t not_after { ASN1_STRING_dup(X509_get0_notAfter(x509.get())) };
|
||||
|
||||
X509_gmtime_adj(not_before.get(), 0);
|
||||
X509_gmtime_adj(not_after.get(), 20 * year);
|
||||
X509_gmtime_adj(not_before.get(), 0);
|
||||
X509_gmtime_adj(not_after.get(), 20 * year);
|
||||
|
||||
X509_set1_notBefore(x509.get(), not_before.get());
|
||||
X509_set1_notAfter(x509.get(), not_after.get());
|
||||
X509_set1_notBefore(x509.get(), not_before.get());
|
||||
X509_set1_notAfter(x509.get(), not_after.get());
|
||||
#endif
|
||||
|
||||
X509_set_pubkey(x509.get(), pkey.get());
|
||||
X509_set_pubkey(x509.get(), pkey.get());
|
||||
|
||||
auto name = X509_get_subject_name(x509.get());
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
||||
(const std::uint8_t *)cn.data(), cn.size(),
|
||||
-1, 0);
|
||||
auto name = X509_get_subject_name(x509.get());
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
||||
(const std::uint8_t *) cn.data(), cn.size(),
|
||||
-1, 0);
|
||||
|
||||
X509_set_issuer_name(x509.get(), name);
|
||||
X509_sign(x509.get(), pkey.get(), EVP_sha256());
|
||||
X509_set_issuer_name(x509.get(), name);
|
||||
X509_sign(x509.get(), pkey.get(), EVP_sha256());
|
||||
|
||||
return { pem(x509), pem(pkey) };
|
||||
}
|
||||
|
||||
std::vector<uint8_t> sign256(const pkey_t &pkey, const std::string_view &data) {
|
||||
return sign(pkey, data, EVP_sha256());
|
||||
}
|
||||
|
||||
bool verify(const x509_t &x509, const std::string_view &data, const std::string_view &signature, const EVP_MD *md) {
|
||||
auto pkey = X509_get_pubkey(x509.get());
|
||||
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
|
||||
if(EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pkey) != 1) {
|
||||
return false;
|
||||
return { pem(x509), pem(pkey) };
|
||||
}
|
||||
|
||||
if(EVP_DigestVerifyUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
return false;
|
||||
std::vector<uint8_t>
|
||||
sign256(const pkey_t &pkey, const std::string_view &data) {
|
||||
return sign(pkey, data, EVP_sha256());
|
||||
}
|
||||
|
||||
if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *)signature.data(), signature.size()) != 1) {
|
||||
return false;
|
||||
bool
|
||||
verify(const x509_t &x509, const std::string_view &data, const std::string_view &signature, const EVP_MD *md) {
|
||||
auto pkey = X509_get_pubkey(x509.get());
|
||||
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
|
||||
if (EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pkey) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *) signature.data(), signature.size()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature) {
|
||||
return verify(x509, data, signature, EVP_sha256());
|
||||
}
|
||||
|
||||
void md_ctx_destroy(EVP_MD_CTX *ctx) {
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
}
|
||||
|
||||
std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet) {
|
||||
auto value = rand(bytes);
|
||||
|
||||
for(std::size_t i = 0; i != value.size(); ++i) {
|
||||
value[i] = alphabet[value[i] % alphabet.length()];
|
||||
bool
|
||||
verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature) {
|
||||
return verify(x509, data, signature, EVP_sha256());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
void
|
||||
md_ctx_destroy(EVP_MD_CTX *ctx) {
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
}
|
||||
|
||||
std::string
|
||||
rand_alphabet(std::size_t bytes, const std::string_view &alphabet) {
|
||||
auto value = rand(bytes);
|
||||
|
||||
for (std::size_t i = 0; i != value.size(); ++i) {
|
||||
value[i] = alphabet[value[i] % alphabet.length()];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
188
src/crypto.h
188
src/crypto.h
@@ -12,124 +12,148 @@
|
||||
#include "utility.h"
|
||||
|
||||
namespace crypto {
|
||||
struct creds_t {
|
||||
std::string x509;
|
||||
std::string pkey;
|
||||
};
|
||||
constexpr std::size_t digest_size = 256;
|
||||
struct creds_t {
|
||||
std::string x509;
|
||||
std::string pkey;
|
||||
};
|
||||
constexpr std::size_t digest_size = 256;
|
||||
|
||||
void md_ctx_destroy(EVP_MD_CTX *);
|
||||
void
|
||||
md_ctx_destroy(EVP_MD_CTX *);
|
||||
|
||||
using sha256_t = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>;
|
||||
using sha256_t = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>;
|
||||
|
||||
using aes_t = std::array<std::uint8_t, 16>;
|
||||
using x509_t = util::safe_ptr<X509, X509_free>;
|
||||
using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
|
||||
using x509_store_ctx_t = util::safe_ptr<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||
using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
|
||||
using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using pkey_ctx_t = util::safe_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using bignum_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
using aes_t = std::array<std::uint8_t, 16>;
|
||||
using x509_t = util::safe_ptr<X509, X509_free>;
|
||||
using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
|
||||
using x509_store_ctx_t = util::safe_ptr<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||
using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
|
||||
using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using pkey_ctx_t = util::safe_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using bignum_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
|
||||
sha256_t hash(const std::string_view &plaintext);
|
||||
sha256_t
|
||||
hash(const std::string_view &plaintext);
|
||||
|
||||
aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
|
||||
aes_t
|
||||
gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
|
||||
|
||||
x509_t x509(const std::string_view &x);
|
||||
pkey_t pkey(const std::string_view &k);
|
||||
std::string pem(x509_t &x509);
|
||||
std::string pem(pkey_t &pkey);
|
||||
x509_t
|
||||
x509(const std::string_view &x);
|
||||
pkey_t
|
||||
pkey(const std::string_view &k);
|
||||
std::string
|
||||
pem(x509_t &x509);
|
||||
std::string
|
||||
pem(pkey_t &pkey);
|
||||
|
||||
std::vector<uint8_t> sign256(const pkey_t &pkey, const std::string_view &data);
|
||||
bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature);
|
||||
std::vector<uint8_t>
|
||||
sign256(const pkey_t &pkey, const std::string_view &data);
|
||||
bool
|
||||
verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature);
|
||||
|
||||
creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits);
|
||||
creds_t
|
||||
gen_creds(const std::string_view &cn, std::uint32_t key_bits);
|
||||
|
||||
std::string_view signature(const x509_t &x);
|
||||
std::string_view
|
||||
signature(const x509_t &x);
|
||||
|
||||
std::string rand(std::size_t bytes);
|
||||
std::string rand_alphabet(std::size_t bytes,
|
||||
const std::string_view &alphabet = std::string_view { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-" });
|
||||
std::string
|
||||
rand(std::size_t bytes);
|
||||
std::string
|
||||
rand_alphabet(std::size_t bytes,
|
||||
const std::string_view &alphabet = std::string_view { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-" });
|
||||
|
||||
class cert_chain_t {
|
||||
public:
|
||||
KITTY_DECL_CONSTR(cert_chain_t)
|
||||
class cert_chain_t {
|
||||
public:
|
||||
KITTY_DECL_CONSTR(cert_chain_t)
|
||||
|
||||
void add(x509_t &&cert);
|
||||
void
|
||||
add(x509_t &&cert);
|
||||
|
||||
const char *verify(x509_t::element_type *cert);
|
||||
const char *
|
||||
verify(x509_t::element_type *cert);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<x509_t, x509_store_t>> _certs;
|
||||
x509_store_ctx_t _cert_ctx;
|
||||
};
|
||||
private:
|
||||
std::vector<std::pair<x509_t, x509_store_t>> _certs;
|
||||
x509_store_ctx_t _cert_ctx;
|
||||
};
|
||||
|
||||
namespace cipher {
|
||||
constexpr std::size_t tag_size = 16;
|
||||
constexpr std::size_t round_to_pkcs7_padded(std::size_t size) {
|
||||
return ((size + 15) / 16) * 16;
|
||||
}
|
||||
namespace cipher {
|
||||
constexpr std::size_t tag_size = 16;
|
||||
constexpr std::size_t
|
||||
round_to_pkcs7_padded(std::size_t size) {
|
||||
return ((size + 15) / 16) * 16;
|
||||
}
|
||||
|
||||
class cipher_t {
|
||||
public:
|
||||
cipher_ctx_t decrypt_ctx;
|
||||
cipher_ctx_t encrypt_ctx;
|
||||
class cipher_t {
|
||||
public:
|
||||
cipher_ctx_t decrypt_ctx;
|
||||
cipher_ctx_t encrypt_ctx;
|
||||
|
||||
aes_t key;
|
||||
aes_t key;
|
||||
|
||||
bool padding;
|
||||
};
|
||||
bool padding;
|
||||
};
|
||||
|
||||
class ecb_t : public cipher_t {
|
||||
public:
|
||||
ecb_t() = default;
|
||||
ecb_t(ecb_t &&) noexcept = default;
|
||||
ecb_t &operator=(ecb_t &&) noexcept = default;
|
||||
class ecb_t: public cipher_t {
|
||||
public:
|
||||
ecb_t() = default;
|
||||
ecb_t(ecb_t &&) noexcept = default;
|
||||
ecb_t &
|
||||
operator=(ecb_t &&) noexcept = default;
|
||||
|
||||
ecb_t(const aes_t &key, bool padding = true);
|
||||
ecb_t(const aes_t &key, bool padding = true);
|
||||
|
||||
int encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher);
|
||||
int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
|
||||
};
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher);
|
||||
int
|
||||
decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
|
||||
};
|
||||
|
||||
class gcm_t : public cipher_t {
|
||||
public:
|
||||
gcm_t() = default;
|
||||
gcm_t(gcm_t &&) noexcept = default;
|
||||
gcm_t &operator=(gcm_t &&) noexcept = default;
|
||||
class gcm_t: public cipher_t {
|
||||
public:
|
||||
gcm_t() = default;
|
||||
gcm_t(gcm_t &&) noexcept = default;
|
||||
gcm_t &
|
||||
operator=(gcm_t &&) noexcept = default;
|
||||
|
||||
gcm_t(const crypto::aes_t &key, bool padding = true);
|
||||
gcm_t(const crypto::aes_t &key, bool padding = true);
|
||||
|
||||
/**
|
||||
/**
|
||||
* length of cipher must be at least: round_to_pkcs7_padded(plaintext.size()) + crypto::cipher::tag_size
|
||||
*
|
||||
* return -1 on error
|
||||
* return bytes written on success
|
||||
*/
|
||||
int encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv);
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv);
|
||||
|
||||
int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv);
|
||||
};
|
||||
int
|
||||
decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv);
|
||||
};
|
||||
|
||||
class cbc_t : public cipher_t {
|
||||
public:
|
||||
cbc_t() = default;
|
||||
cbc_t(cbc_t &&) noexcept = default;
|
||||
cbc_t &operator=(cbc_t &&) noexcept = default;
|
||||
class cbc_t: public cipher_t {
|
||||
public:
|
||||
cbc_t() = default;
|
||||
cbc_t(cbc_t &&) noexcept = default;
|
||||
cbc_t &
|
||||
operator=(cbc_t &&) noexcept = default;
|
||||
|
||||
cbc_t(const crypto::aes_t &key, bool padding = true);
|
||||
cbc_t(const crypto::aes_t &key, bool padding = true);
|
||||
|
||||
/**
|
||||
/**
|
||||
* length of cipher must be at least: round_to_pkcs7_padded(plaintext.size())
|
||||
*
|
||||
* return -1 on error
|
||||
* return bytes written on success
|
||||
*/
|
||||
int encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
|
||||
};
|
||||
} // namespace cipher
|
||||
} // namespace crypto
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
|
||||
};
|
||||
} // namespace cipher
|
||||
} // namespace crypto
|
||||
|
||||
#endif //SUNSHINE_CRYPTO_H
|
||||
#endif //SUNSHINE_CRYPTO_H
|
||||
|
||||
@@ -27,209 +27,219 @@
|
||||
#include "uuid.h"
|
||||
|
||||
namespace http {
|
||||
using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
int reload_user_creds(const std::string &file);
|
||||
bool user_creds_exist(const std::string &file);
|
||||
int
|
||||
reload_user_creds(const std::string &file);
|
||||
bool
|
||||
user_creds_exist(const std::string &file);
|
||||
|
||||
std::string unique_id;
|
||||
net::net_e origin_pin_allowed;
|
||||
net::net_e origin_web_ui_allowed;
|
||||
std::string unique_id;
|
||||
net::net_e origin_pin_allowed;
|
||||
net::net_e origin_web_ui_allowed;
|
||||
|
||||
int init() {
|
||||
bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE];
|
||||
origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed);
|
||||
origin_web_ui_allowed = net::from_enum_string(config::nvhttp.origin_web_ui_allowed);
|
||||
int
|
||||
init() {
|
||||
bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE];
|
||||
origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed);
|
||||
origin_web_ui_allowed = net::from_enum_string(config::nvhttp.origin_web_ui_allowed);
|
||||
|
||||
if(clean_slate) {
|
||||
unique_id = util::uuid_t::generate().string();
|
||||
auto dir = std::filesystem::temp_directory_path() / "Sunshine"sv;
|
||||
config::nvhttp.cert = (dir / ("cert-"s + unique_id)).string();
|
||||
config::nvhttp.pkey = (dir / ("pkey-"s + unique_id)).string();
|
||||
}
|
||||
|
||||
if(!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) {
|
||||
if(create_creds(config::nvhttp.pkey, config::nvhttp.cert)) {
|
||||
return -1;
|
||||
if (clean_slate) {
|
||||
unique_id = uuid_util::uuid_t::generate().string();
|
||||
auto dir = std::filesystem::temp_directory_path() / "Sunshine"sv;
|
||||
config::nvhttp.cert = (dir / ("cert-"s + unique_id)).string();
|
||||
config::nvhttp.pkey = (dir / ("pkey-"s + unique_id)).string();
|
||||
}
|
||||
}
|
||||
if(user_creds_exist(config::sunshine.credentials_file)) {
|
||||
if(reload_user_creds(config::sunshine.credentials_file)) return -1;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Open the Web UI to set your new username and password and getting started";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) {
|
||||
pt::ptree outputTree;
|
||||
if (!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) {
|
||||
if (create_creds(config::nvhttp.pkey, config::nvhttp.cert)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (user_creds_exist(config::sunshine.credentials_file)) {
|
||||
if (reload_user_creds(config::sunshine.credentials_file)) return -1;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Open the Web UI to set your new username and password and getting started";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(fs::exists(file)) {
|
||||
int
|
||||
save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) {
|
||||
pt::ptree outputTree;
|
||||
|
||||
if (fs::exists(file)) {
|
||||
try {
|
||||
pt::read_json(file, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
auto salt = crypto::rand_alphabet(16);
|
||||
outputTree.put("username", username);
|
||||
outputTree.put("salt", salt);
|
||||
outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string());
|
||||
try {
|
||||
pt::read_json(file, outputTree);
|
||||
pt::write_json(file, outputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what();
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "generating user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "New credentials have been created"sv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto salt = crypto::rand_alphabet(16);
|
||||
outputTree.put("username", username);
|
||||
outputTree.put("salt", salt);
|
||||
outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string());
|
||||
try {
|
||||
pt::write_json(file, outputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << "generating user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
bool
|
||||
user_creds_exist(const std::string &file) {
|
||||
if (!fs::exists(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "New credentials have been created"sv;
|
||||
return 0;
|
||||
}
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(file, inputTree);
|
||||
return inputTree.find("username") != inputTree.not_found() &&
|
||||
inputTree.find("password") != inputTree.not_found() &&
|
||||
inputTree.find("salt") != inputTree.not_found();
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "validating user credentials: "sv << e.what();
|
||||
}
|
||||
|
||||
bool user_creds_exist(const std::string &file) {
|
||||
if(!fs::exists(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(file, inputTree);
|
||||
return inputTree.find("username") != inputTree.not_found() &&
|
||||
inputTree.find("password") != inputTree.not_found() &&
|
||||
inputTree.find("salt") != inputTree.not_found();
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << "validating user credentials: "sv << e.what();
|
||||
int
|
||||
reload_user_creds(const std::string &file) {
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(file, inputTree);
|
||||
config::sunshine.username = inputTree.get<std::string>("username");
|
||||
config::sunshine.password = inputTree.get<std::string>("password");
|
||||
config::sunshine.salt = inputTree.get<std::string>("salt");
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "loading user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
int
|
||||
create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::path pkey_path = pkey;
|
||||
fs::path cert_path = cert;
|
||||
|
||||
int reload_user_creds(const std::string &file) {
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(file, inputTree);
|
||||
config::sunshine.username = inputTree.get<std::string>("username");
|
||||
config::sunshine.password = inputTree.get<std::string>("password");
|
||||
config::sunshine.salt = inputTree.get<std::string>("salt");
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << "loading user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
auto creds = crypto::gen_creds("Sunshine Gamestream Host"sv, 2048);
|
||||
|
||||
int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::path pkey_path = pkey;
|
||||
fs::path cert_path = cert;
|
||||
auto pkey_dir = pkey_path;
|
||||
auto cert_dir = cert_path;
|
||||
pkey_dir.remove_filename();
|
||||
cert_dir.remove_filename();
|
||||
|
||||
auto creds = crypto::gen_creds("Sunshine Gamestream Host"sv, 2048);
|
||||
std::error_code err_code {};
|
||||
fs::create_directories(pkey_dir, err_code);
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto pkey_dir = pkey_path;
|
||||
auto cert_dir = cert_path;
|
||||
pkey_dir.remove_filename();
|
||||
cert_dir.remove_filename();
|
||||
fs::create_directories(cert_dir, err_code);
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::error_code err_code {};
|
||||
fs::create_directories(pkey_dir, err_code);
|
||||
if(err_code) {
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
if (write_file(pkey.c_str(), creds.pkey)) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write_file(cert.c_str(), creds.x509)) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::permissions(pkey_path,
|
||||
fs::perms::owner_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::permissions(cert_path,
|
||||
fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs::create_directories(cert_dir, err_code);
|
||||
if(err_code) {
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(write_file(pkey.c_str(), creds.pkey)) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(write_file(cert.c_str(), creds.x509)) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::permissions(pkey_path,
|
||||
fs::perms::owner_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if(err_code) {
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::permissions(cert_path,
|
||||
fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if(err_code) {
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool download_file(const std::string &url, const std::string &file) {
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) {
|
||||
BOOST_LOG(error) << "Couldn't create CURL instance";
|
||||
return false;
|
||||
}
|
||||
FILE *fp = fopen(file.c_str(), "wb");
|
||||
if(!fp) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << file << ']';
|
||||
curl_easy_cleanup(curl);
|
||||
return false;
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||
bool
|
||||
download_file(const std::string &url, const std::string &file) {
|
||||
CURL *curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
BOOST_LOG(error) << "Couldn't create CURL instance";
|
||||
return false;
|
||||
}
|
||||
FILE *fp = fopen(file.c_str(), "wb");
|
||||
if (!fp) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << file << ']';
|
||||
curl_easy_cleanup(curl);
|
||||
return false;
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||
#ifdef _WIN32
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
|
||||
#endif
|
||||
CURLcode result = curl_easy_perform(curl);
|
||||
if(result != CURLE_OK) {
|
||||
BOOST_LOG(error) << "Couldn't download ["sv << url << ", code:" << result << ']';
|
||||
CURLcode result = curl_easy_perform(curl);
|
||||
if (result != CURLE_OK) {
|
||||
BOOST_LOG(error) << "Couldn't download ["sv << url << ", code:" << result << ']';
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(fp);
|
||||
return result == CURLE_OK;
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(fp);
|
||||
return result == CURLE_OK;
|
||||
}
|
||||
|
||||
std::string url_escape(const std::string &url) {
|
||||
CURL *curl = curl_easy_init();
|
||||
char *string = curl_easy_escape(curl, url.c_str(), url.length());
|
||||
std::string result(string);
|
||||
curl_free(string);
|
||||
curl_easy_cleanup(curl);
|
||||
return result;
|
||||
}
|
||||
std::string
|
||||
url_escape(const std::string &url) {
|
||||
CURL *curl = curl_easy_init();
|
||||
char *string = curl_easy_escape(curl, url.c_str(), url.length());
|
||||
std::string result(string);
|
||||
curl_free(string);
|
||||
curl_easy_cleanup(curl);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string url_get_host(const std::string &url) {
|
||||
CURLU *curlu = curl_url();
|
||||
curl_url_set(curlu, CURLUPART_URL, url.c_str(), url.length());
|
||||
char *host;
|
||||
if(curl_url_get(curlu, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
|
||||
std::string
|
||||
url_get_host(const std::string &url) {
|
||||
CURLU *curlu = curl_url();
|
||||
curl_url_set(curlu, CURLUPART_URL, url.c_str(), url.length());
|
||||
char *host;
|
||||
if (curl_url_get(curlu, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
|
||||
curl_url_cleanup(curlu);
|
||||
return "";
|
||||
}
|
||||
std::string result(host);
|
||||
curl_free(host);
|
||||
curl_url_cleanup(curlu);
|
||||
return "";
|
||||
return result;
|
||||
}
|
||||
std::string result(host);
|
||||
curl_free(host);
|
||||
curl_url_cleanup(curlu);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace http
|
||||
|
||||
@@ -3,21 +3,28 @@
|
||||
|
||||
namespace http {
|
||||
|
||||
int init();
|
||||
int create_creds(const std::string &pkey, const std::string &cert);
|
||||
int save_user_creds(
|
||||
const std::string &file,
|
||||
const std::string &username,
|
||||
const std::string &password,
|
||||
bool run_our_mouth = false);
|
||||
int
|
||||
init();
|
||||
int
|
||||
create_creds(const std::string &pkey, const std::string &cert);
|
||||
int
|
||||
save_user_creds(
|
||||
const std::string &file,
|
||||
const std::string &username,
|
||||
const std::string &password,
|
||||
bool run_our_mouth = false);
|
||||
|
||||
int reload_user_creds(const std::string &file);
|
||||
bool download_file(const std::string &url, const std::string &file);
|
||||
std::string url_escape(const std::string &url);
|
||||
std::string url_get_host(const std::string &url);
|
||||
int
|
||||
reload_user_creds(const std::string &file);
|
||||
bool
|
||||
download_file(const std::string &url, const std::string &file);
|
||||
std::string
|
||||
url_escape(const std::string &url);
|
||||
std::string
|
||||
url_get_host(const std::string &url);
|
||||
|
||||
extern std::string unique_id;
|
||||
extern net::net_e origin_pin_allowed;
|
||||
extern net::net_e origin_web_ui_allowed;
|
||||
extern std::string unique_id;
|
||||
extern net::net_e origin_pin_allowed;
|
||||
extern net::net_e origin_web_ui_allowed;
|
||||
|
||||
} // namespace http
|
||||
} // namespace http
|
||||
|
||||
1243
src/input.cpp
1243
src/input.cpp
File diff suppressed because it is too large
Load Diff
34
src/input.h
34
src/input.h
@@ -9,25 +9,29 @@
|
||||
#include "thread_safe.h"
|
||||
|
||||
namespace input {
|
||||
struct input_t;
|
||||
struct input_t;
|
||||
|
||||
void print(void *input);
|
||||
void reset(std::shared_ptr<input_t> &input);
|
||||
void passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data);
|
||||
void
|
||||
print(void *input);
|
||||
void
|
||||
reset(std::shared_ptr<input_t> &input);
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t>
|
||||
init();
|
||||
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t> init();
|
||||
std::shared_ptr<input_t>
|
||||
alloc(safe::mail_t mail);
|
||||
|
||||
std::shared_ptr<input_t> alloc(safe::mail_t mail);
|
||||
struct touch_port_t: public platf::touch_port_t {
|
||||
int env_width, env_height;
|
||||
|
||||
struct touch_port_t : public platf::touch_port_t {
|
||||
int env_width, env_height;
|
||||
// Offset x and y coordinates of the client
|
||||
float client_offsetX, client_offsetY;
|
||||
|
||||
// Offset x and y coordinates of the client
|
||||
float client_offsetX, client_offsetY;
|
||||
float scalar_inv;
|
||||
};
|
||||
} // namespace input
|
||||
|
||||
float scalar_inv;
|
||||
};
|
||||
} // namespace input
|
||||
|
||||
#endif // SUNSHINE_INPUT_H
|
||||
#endif // SUNSHINE_INPUT_H
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user