mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
1 Commits
5.0.0
...
5.0.0-alph
Author | SHA1 | Date | |
---|---|---|---|
e1dc0c441c |
107
.clang-format
107
.clang-format
@ -1,107 +0,0 @@
|
|||||||
# please use clang-format version 8 or later
|
|
||||||
|
|
||||||
Standard: Cpp11
|
|
||||||
AccessModifierOffset: -8
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveAssignments: false
|
|
||||||
AlignConsecutiveDeclarations: false
|
|
||||||
AlignEscapedNewlines: Left
|
|
||||||
AlignOperands: true
|
|
||||||
AlignTrailingComments: true
|
|
||||||
#AllowAllArgumentsOnNextLine: false # requires clang-format 9
|
|
||||||
#AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: false
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: Inline
|
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
|
||||||
#AllowShortLambdasOnASingleLine: Inline # requires clang-format 9
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
AlwaysBreakAfterDefinitionReturnType: None
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
|
||||||
AlwaysBreakTemplateDeclarations: false
|
|
||||||
BinPackArguments: true
|
|
||||||
BinPackParameters: true
|
|
||||||
BraceWrapping:
|
|
||||||
AfterClass: false
|
|
||||||
AfterControlStatement: false
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: true
|
|
||||||
AfterNamespace: false
|
|
||||||
AfterObjCDeclaration: false
|
|
||||||
AfterStruct: false
|
|
||||||
AfterUnion: false
|
|
||||||
AfterExternBlock: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
IndentBraces: false
|
|
||||||
SplitEmptyFunction: true
|
|
||||||
SplitEmptyRecord: true
|
|
||||||
SplitEmptyNamespace: true
|
|
||||||
BreakBeforeBinaryOperators: None
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: BeforeColon
|
|
||||||
BreakStringLiterals: false # apparently unpredictable
|
|
||||||
ColumnLimit: 132
|
|
||||||
CompactNamespaces: false
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
|
||||||
ConstructorInitializerIndentWidth: 8
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
Cpp11BracedListStyle: true
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
DisableFormat: false
|
|
||||||
FixNamespaceComments: false
|
|
||||||
ForEachMacros:
|
|
||||||
- 'json_object_foreach'
|
|
||||||
- 'json_object_foreach_safe'
|
|
||||||
- 'json_array_foreach'
|
|
||||||
IncludeBlocks: Preserve
|
|
||||||
IndentCaseLabels: false
|
|
||||||
IndentPPDirectives: None
|
|
||||||
IndentWidth: 8
|
|
||||||
IndentWrappedFunctionNames: false
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
|
||||||
MaxEmptyLinesToKeep: 1
|
|
||||||
NamespaceIndentation: All
|
|
||||||
#ObjCBinPackProtocolList: Auto # requires clang-format 7
|
|
||||||
ObjCBlockIndentWidth: 8
|
|
||||||
ObjCSpaceAfterProperty: true
|
|
||||||
ObjCSpaceBeforeProtocolList: true
|
|
||||||
|
|
||||||
PenaltyBreakAssignment: 10
|
|
||||||
PenaltyBreakBeforeFirstCallParameter: 30
|
|
||||||
PenaltyBreakComment: 10
|
|
||||||
PenaltyBreakFirstLessLess: 0
|
|
||||||
PenaltyBreakString: 10
|
|
||||||
PenaltyExcessCharacter: 100
|
|
||||||
PenaltyReturnTypeOnItsOwnLine: 60
|
|
||||||
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: false
|
|
||||||
SortIncludes: false
|
|
||||||
SortUsingDeclarations: false
|
|
||||||
SpaceAfterCStyleCast: false
|
|
||||||
#SpaceAfterLogicalNot: false # requires clang-format 9
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
#SpaceBeforeCtorInitializerColon: true # requires clang-format 7
|
|
||||||
#SpaceBeforeInheritanceColon: true # requires clang-format 7
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
#SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
#StatementMacros: # requires clang-format 8
|
|
||||||
# - 'Q_OBJECT'
|
|
||||||
TabWidth: 8
|
|
||||||
#TypenameMacros: # requires clang-format 9
|
|
||||||
# - 'DARRAY'
|
|
||||||
UseTab: ForContinuationAndIndentation
|
|
||||||
---
|
|
||||||
Language: ObjC
|
|
12
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
12
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -27,11 +27,10 @@ body:
|
|||||||
- macOS 10.15
|
- macOS 10.15
|
||||||
- macOS 10.14
|
- macOS 10.14
|
||||||
- macOS 10.13
|
- macOS 10.13
|
||||||
- Ubuntu 22.04 LTS
|
|
||||||
- Ubuntu 21.04
|
- Ubuntu 21.04
|
||||||
- Ubuntu 20.10
|
- Ubuntu 20.10
|
||||||
- Ubuntu 20.04 LTS
|
- Ubuntu 20.04
|
||||||
- Ubuntu 18.04 LTS
|
- Ubuntu 18.04
|
||||||
- Other
|
- Other
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@ -49,11 +48,6 @@ body:
|
|||||||
label: OBS Studio Version
|
label: OBS Studio Version
|
||||||
description: What version of OBS Studio are you using?
|
description: What version of OBS Studio are you using?
|
||||||
options:
|
options:
|
||||||
- 27.2.4
|
|
||||||
- 27.2.3
|
|
||||||
- 27.2.2
|
|
||||||
- 27.2.1
|
|
||||||
- 27.2.0
|
|
||||||
- 27.1.3
|
- 27.1.3
|
||||||
- 27.1.1
|
- 27.1.1
|
||||||
- 27.1.0
|
- 27.1.0
|
||||||
@ -75,8 +69,6 @@ body:
|
|||||||
label: obs-websocket Version
|
label: obs-websocket Version
|
||||||
description: What version of obs-websocket are you using?
|
description: What version of obs-websocket are you using?
|
||||||
options:
|
options:
|
||||||
- 5.0.0
|
|
||||||
- 5.0.0-beta1
|
|
||||||
- 5.0.0-alpha3
|
- 5.0.0-alpha3
|
||||||
- 5.0.0-alpha2
|
- 5.0.0-alpha2
|
||||||
- 4.9.1
|
- 4.9.1
|
||||||
|
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -29,6 +29,6 @@ Tested OS(s):
|
|||||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||||
- [ ] I have read the [Contributing Guidelines](https://github.com/obsproject/obs-websocket/wiki/Contributing-Guidelines).
|
- [ ] I have read the [Contributing Guidelines](https://github.com/obsproject/obs-websocket/wiki/Contributing-Guidelines).
|
||||||
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
|
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
|
||||||
- [ ] My code is not on `master` or a `release/*` branch.
|
- [ ] My code is not on the `master` branch.
|
||||||
- [ ] The code has been tested.
|
- [ ] The code has been tested.
|
||||||
- [ ] I have included updates to all appropriate documentation.
|
- [ ] I have included updates to all appropriate documentation.
|
||||||
|
2
.github/workflows/generate_docs.yml
vendored
2
.github/workflows/generate_docs.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'docs/generated/**'
|
- 'docs/generated/**'
|
||||||
branches:
|
branches:
|
||||||
- release/5.0.0
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docs-build:
|
docs-build:
|
||||||
|
24
.github/workflows/lint.yml
vendored
24
.github/workflows/lint.yml
vendored
@ -1,24 +0,0 @@
|
|||||||
name: Code Quality
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- release/5.0.0
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- release/5.0.0
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
markdown:
|
|
||||||
name: Lint Markdown
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Generate docs
|
|
||||||
run: cd docs && ./build_docs.sh
|
|
||||||
- name: Run markdownlint-cli
|
|
||||||
uses: nosborn/github-action-markdown-cli@v3.0.1
|
|
||||||
with:
|
|
||||||
files: .
|
|
273
.github/workflows/main.yml
vendored
273
.github/workflows/main.yml
vendored
@ -1,11 +1,11 @@
|
|||||||
name: 'CI Multiplatform Build'
|
name: "CI Multiplatform Build"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
branches:
|
branches:
|
||||||
- release/5.0.0
|
- master
|
||||||
tags:
|
tags:
|
||||||
- '[45].[0-9]+.[0-9]+*'
|
- '[45].[0-9]+.[0-9]+*'
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -13,20 +13,20 @@ on:
|
|||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- '**.md'
|
- '**.md'
|
||||||
branches:
|
branches:
|
||||||
- release/5.0.0
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows:
|
windows:
|
||||||
name: 'Windows 32/64-bit'
|
name: 'Windows 32/64-bit'
|
||||||
runs-on: [windows-2019]
|
runs-on: [windows-latest]
|
||||||
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
||||||
env:
|
env:
|
||||||
QT_CACHE_VERSION: '2' # Change whenever updating OBS dependencies URL, in order to force a cache reset
|
QT_CACHE_VERSION: '2' # Change whenever updating OBS dependencies URL, in order to force a cache reset
|
||||||
QT_VERSION: '5.15.2'
|
QT_VERSION: '5.15.2'
|
||||||
WINDOWS_DEPS_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset
|
WINDOWS_DEPS_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset
|
||||||
WINDOWS_DEPS_VERSION: '2019'
|
WINDOWS_DEPS_VERSION: '2019'
|
||||||
CMAKE_GENERATOR: 'Visual Studio 16 2019'
|
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||||
CMAKE_SYSTEM_VERSION: '10.0'
|
CMAKE_SYSTEM_VERSION: "10.0"
|
||||||
steps:
|
steps:
|
||||||
- name: 'Add msbuild to PATH'
|
- name: 'Add msbuild to PATH'
|
||||||
uses: microsoft/setup-msbuild@v1.0.2
|
uses: microsoft/setup-msbuild@v1.0.2
|
||||||
@ -35,20 +35,20 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/obs-websocket
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: 'Checkout OBS Studio'
|
- name: 'Checkout OBS-Studio'
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: obsproject/obs-studio
|
repository: obsproject/obs-studio
|
||||||
path: ${{ github.workspace }}/obs-studio
|
path: ${{ github.workspace }}/obs-studio
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: 'Get OBS Studio Git Info'
|
- name: 'Get OBS-Studio Git Info'
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
git fetch --prune --unshallow
|
git fetch --prune --unshallow
|
||||||
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
||||||
- name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
@ -78,6 +78,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: Qt_${{ env.QT_VERSION }}.7z
|
path: Qt_${{ env.QT_VERSION }}.7z
|
||||||
key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}'
|
key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}'
|
||||||
|
restore-keys: |
|
||||||
|
qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}
|
||||||
- name: 'Download Prerequisite: Qt'
|
- name: 'Download Prerequisite: Qt'
|
||||||
if: steps.qtcache.outputs.cache-hit != 'true'
|
if: steps.qtcache.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
@ -85,18 +87,20 @@ jobs:
|
|||||||
- name: 'Extract Prerequisite: Qt'
|
- name: 'Extract Prerequisite: Qt'
|
||||||
run: |
|
run: |
|
||||||
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
|
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
|
||||||
- name: 'Restore Cached OBS Studio Dependencies'
|
- name: 'Restore Cached OBS-Studio Dependencies'
|
||||||
id: obscache
|
id: obscache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}\cmbuild\deps\**
|
path: ${{ github.workspace }}\cmbuild\deps\**
|
||||||
key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
|
key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
|
||||||
- name: 'Install Prerequisite: Pre-built OBS Studio dependencies'
|
restore-keys: |
|
||||||
|
obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}
|
||||||
|
- name: 'Install Prerequisite: Pre-built OBS-Studio dependencies'
|
||||||
if: steps.obscache.outputs.cache-hit != 'true'
|
if: steps.obscache.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
|
||||||
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
|
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
|
||||||
- name: 'Restore OBS Studio 32-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
|
- name: 'Restore OBS-Studio 32-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
|
||||||
id: build-cache-obs-32
|
id: build-cache-obs-32
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
env:
|
env:
|
||||||
@ -104,20 +108,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/obs-studio/build32
|
path: ${{ github.workspace }}/obs-studio/build32
|
||||||
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||||
- name: 'Configure OBS Studio 32-bit'
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||||
|
- name: 'Configure OBS-Studio 32-bit'
|
||||||
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\build32}
|
if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\build32}
|
||||||
cd .\build32
|
cd .\build32
|
||||||
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
|
||||||
- name: 'Build OBS Studio 32-bit'
|
- name: 'Build OBS-Studio 32-bit'
|
||||||
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
|
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
|
||||||
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
|
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
|
||||||
- name: 'Restore OBS Studio 64-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
|
- name: 'Restore OBS-Studio 64-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
|
||||||
id: build-cache-obs-64
|
id: build-cache-obs-64
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v1
|
||||||
env:
|
env:
|
||||||
@ -125,14 +131,16 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/obs-studio/build64
|
path: ${{ github.workspace }}/obs-studio/build64
|
||||||
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||||
- name: 'Configure OBS Studio 64-bit'
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||||
|
- name: 'Configure OBS-Studio 64-bit'
|
||||||
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\build64}
|
if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\build64}
|
||||||
cd .\build64
|
cd .\build64
|
||||||
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
|
||||||
- name: 'Build OBS Studio 64-bit'
|
- name: 'Build OBS-Studio 64-bit'
|
||||||
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
@ -180,7 +188,7 @@ jobs:
|
|||||||
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer'
|
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer'
|
||||||
path: ${{ github.workspace }}/obs-websocket/package/*.exe
|
path: ${{ github.workspace }}/obs-websocket/package/*.exe
|
||||||
ubuntu64:
|
ubuntu64:
|
||||||
name: 'Linux/Ubuntu 64-bit'
|
name: "Linux/Ubuntu 64-bit"
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
||||||
steps:
|
steps:
|
||||||
@ -189,20 +197,20 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/obs-websocket
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: 'Checkout OBS Studio'
|
- name: 'Checkout OBS-Studio'
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: obsproject/obs-studio
|
repository: obsproject/obs-studio
|
||||||
path: ${{ github.workspace }}/obs-studio
|
path: ${{ github.workspace }}/obs-studio
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: 'Get OBS Studio Git Info'
|
- name: 'Get OBS-Studio Git Info'
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
git fetch --prune --unshallow
|
git fetch --prune --unshallow
|
||||||
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
||||||
- name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
run: |
|
run: |
|
||||||
@ -276,21 +284,21 @@ jobs:
|
|||||||
libx11-xcb-dev \
|
libx11-xcb-dev \
|
||||||
libxcb1-dev \
|
libxcb1-dev \
|
||||||
libxss-dev \
|
libxss-dev \
|
||||||
- name: 'Configure OBS Studio'
|
- name: 'Configure OBS-Studio'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir ./build
|
mkdir ./build
|
||||||
cd ./build
|
cd ./build
|
||||||
cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
|
cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
- name: 'Build OBS Studio'
|
- name: 'Build OBS-Studio'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
cd ./build
|
cd ./build
|
||||||
make -j4 libobs obs-frontend-api
|
make -j4 libobs obs-frontend-api
|
||||||
- name: 'Install OBS Studio'
|
- name: 'Install OBS-Studio'
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@ -308,7 +316,7 @@ jobs:
|
|||||||
if [ "${{ env.GIT_TAG }}" ] ; then \
|
if [ "${{ env.GIT_TAG }}" ] ; then \
|
||||||
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DCMAKE_BUILD_TYPE=Release .. ; \
|
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DCMAKE_BUILD_TYPE=Release .. ; \
|
||||||
else \
|
else \
|
||||||
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DPLUGIN_TESTS=TRUE .. ; \
|
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" .. ; \
|
||||||
fi
|
fi
|
||||||
- name: 'Build obs-websocket'
|
- name: 'Build obs-websocket'
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
@ -334,9 +342,9 @@ jobs:
|
|||||||
cd ./build
|
cd ./build
|
||||||
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
|
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
|
||||||
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion="$CHECKINSTALL_VERSION" \
|
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion="$CHECKINSTALL_VERSION" \
|
||||||
--pkglicense="GPLv2.0" --maintainer="tt2468@irltoolkit.com" --pkggroup="video" \
|
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \
|
||||||
--pkgsource="${{ github.event.repository.html_url }}" \
|
--pkgsource="${{ github.event.repository.html_url }}" \
|
||||||
--requires="obs-studio,libqt5network5,qt5-image-formats-plugins" \
|
--requires="obs-studio,libqt5network5,libqt5concurrent5,qt5-image-formats-plugins" \
|
||||||
--pakdir="../package"
|
--pakdir="../package"
|
||||||
sudo chmod ao+r ../package/*
|
sudo chmod ao+r ../package/*
|
||||||
sudo mv ../package/* ../package/${{ env.LINUX_FILENAME }}
|
sudo mv ../package/* ../package/${{ env.LINUX_FILENAME }}
|
||||||
@ -347,214 +355,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64'
|
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64'
|
||||||
path: '${{ github.workspace }}/obs-websocket/package/*.deb'
|
path: '${{ github.workspace }}/obs-websocket/package/*.deb'
|
||||||
macOS:
|
|
||||||
name: 'macOS 64-bit'
|
|
||||||
runs-on: [macos-latest]
|
|
||||||
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
|
||||||
env:
|
|
||||||
MACOS_DEPS_VERSION: '2022-01-01'
|
|
||||||
MACOS_DEPS_CACHE_VERSION: '2' # Change whenever updating dependencies version, in order to force a cache reset
|
|
||||||
steps:
|
|
||||||
- name: 'Checkout obs-websocket'
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
path: ${{ github.workspace }}/obs-websocket
|
|
||||||
submodules: 'recursive'
|
|
||||||
- name: 'Checkout OBS Studio'
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
repository: obsproject/obs-studio
|
|
||||||
path: ${{ github.workspace }}/obs-studio
|
|
||||||
submodules: 'recursive'
|
|
||||||
- name: 'Install Prerequisite: Binary Signing Certificate'
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: apple-actions/import-codesign-certs@v1
|
|
||||||
with:
|
|
||||||
p12-file-base64: ${{ secrets.MACOS_SIGNING_CERT }}
|
|
||||||
p12-password: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
|
|
||||||
create-keychain: true
|
|
||||||
keychain-password: ${{ secrets.MACOS_TEMP_CI_KEYCHAIN_PASSWORD }}
|
|
||||||
- name: 'Install Prerequisite: Installer Signing Certificate'
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: apple-actions/import-codesign-certs@v1
|
|
||||||
with:
|
|
||||||
p12-file-base64: ${{ secrets.MACOS_INSTALLER_CERT }}
|
|
||||||
p12-password: ${{ secrets.MACOS_INSTALLER_CERT_PASSWORD }}
|
|
||||||
create-keychain: false
|
|
||||||
keychain-password: ${{ secrets.MACOS_TEMP_CI_KEYCHAIN_PASSWORD }}
|
|
||||||
- name: 'Get OBS Studio Git Info'
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
|
||||||
run: |
|
|
||||||
git fetch --prune --unshallow
|
|
||||||
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
|
||||||
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
|
||||||
- name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
|
||||||
run: |
|
|
||||||
git checkout ${{ env.OBS_GIT_TAG }}
|
|
||||||
git submodule update
|
|
||||||
- name: 'Get obs-websocket git info'
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
|
||||||
run: |
|
|
||||||
git fetch --prune --unshallow
|
|
||||||
GIT_HASH=$(git rev-parse --short HEAD)
|
|
||||||
echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV
|
|
||||||
GIT_TAG=$(git describe --exact-match --tags --abbrev=0) || GIT_TAG=""
|
|
||||||
echo "GIT_TAG=$GIT_TAG" >> $GITHUB_ENV
|
|
||||||
if [ "$GIT_TAG" ] ; then \
|
|
||||||
VERSION="$GIT_TAG" \
|
|
||||||
VERSION_SUFFIX=$(echo "$GIT_TAG" | cut -c6-20) ; \
|
|
||||||
else \
|
|
||||||
VERSION="$GIT_HASH-git" \
|
|
||||||
VERSION_SUFFIX="-$GIT_HASH-git" ; \
|
|
||||||
fi
|
|
||||||
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
|
|
||||||
echo "CMAKE_VERSION_SUFFIX=$VERSION_SUFFIX" >> $GITHUB_ENV
|
|
||||||
- name: 'Install Packages'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
|
|
||||||
sudo hdiutil attach ./Packages.dmg
|
|
||||||
sudo installer -pkg /Volumes/Packages\ 1.2.10/Install\ Packages.pkg -target /
|
|
||||||
- name: 'Restore Cached Qt & OBS Studio dependencies'
|
|
||||||
id: deps-cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ${{ github.workspace }}/obsdeps/**
|
|
||||||
key: 'deps-cache-${{ env.MACOS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
|
|
||||||
- name: 'Install Prerequisite: Qt + OBS Studio dependencies'
|
|
||||||
if: steps.deps-cache.outputs.cache-hit != 'true'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p obsdeps
|
|
||||||
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-qt-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz
|
|
||||||
tar -xf macos-deps-qt-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz -C "./obsdeps"
|
|
||||||
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz
|
|
||||||
tar -xf macos-deps-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz -C "./obsdeps"
|
|
||||||
- run: xattr -r -d com.apple.quarantine ./obsdeps
|
|
||||||
shell: bash
|
|
||||||
- name: 'Configue OBS Studio'
|
|
||||||
if: steps.cache-obs-build.outputs.cache-hit != 'true'
|
|
||||||
working-directory: ${{ github.workspace }}/obs-studio
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p ./build
|
|
||||||
cd ./build
|
|
||||||
cmake .. \
|
|
||||||
-DQTDIR=${{ github.workspace }}/obsdeps \
|
|
||||||
-DDepsPath=${{ github.workspace }}/obsdeps \
|
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
|
||||||
-DDISABLE_PLUGINS=true \
|
|
||||||
-DENABLE_SCRIPTING=0 \
|
|
||||||
-DCMAKE_PREFIX_PATH=${{ github.workspace }}/obsdeps/lib/cmake
|
|
||||||
- name: 'Build OBS Studio'
|
|
||||||
if: steps.cache-obs-build.outputs.cache-hit != 'true'
|
|
||||||
working-directory: ${{ github.workspace }}/obs-studio/build
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
make -j4 libobs obs-frontend-api
|
|
||||||
- name: 'Configure obs-websocket'
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p build
|
|
||||||
cd build
|
|
||||||
cmake .. \
|
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
|
||||||
-DQTDIR=${{ github.workspace }}/obsdeps \
|
|
||||||
-DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs \
|
|
||||||
-DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs \
|
|
||||||
-DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
|
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
|
||||||
-DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}"
|
|
||||||
- name: 'Build obs-websocket'
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket/build
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
make -j4
|
|
||||||
- name: 'Relink Qt'
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket/build
|
|
||||||
run: |
|
|
||||||
install_name_tool \
|
|
||||||
-change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets \
|
|
||||||
@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \
|
|
||||||
-change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui \
|
|
||||||
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \
|
|
||||||
-change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore \
|
|
||||||
@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \
|
|
||||||
-change /tmp/obsdeps/lib/QtNetwork.framework/Versions/5/QtNetwork \
|
|
||||||
@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork \
|
|
||||||
-change /tmp/obsdeps/lib/QtSvg.framework/Versions/5/QtSvg \
|
|
||||||
@executable_path/../Frameworks/QtSvg.framework/Versions/5/QtSvg \
|
|
||||||
./obs-websocket.so
|
|
||||||
- name: 'Sign plugin binary'
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket/build
|
|
||||||
run: |
|
|
||||||
codesign --sign "${{ secrets.MACOS_SIGNING_IDENTITY }}" ./obs-websocket.so
|
|
||||||
- name: 'Set PR Artifact Filename'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "MACOS_FILENAME=obs-websocket-${{ env.PACKAGE_VERSION }}-macOS.pkg" >> $GITHUB_ENV
|
|
||||||
echo "MACOS_FILENAME_UNSIGNED=obs-websocket-${{ env.PACKAGE_VERSION }}-macOS-Unsigned.pkg" >> $GITHUB_ENV
|
|
||||||
- name: 'Package ${{ env.MACOS_FILENAME_UNSIGNED }}'
|
|
||||||
if: success()
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
|
||||||
mv ./release/obs-websocket.pkg ./release/${{ env.MACOS_FILENAME_UNSIGNED }}
|
|
||||||
- name: 'Sign plugin package'
|
|
||||||
if: ${{ env.GIT_TAG != '' }}
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
|
||||||
run: |
|
|
||||||
productsign \
|
|
||||||
--sign "${{ secrets.MACOS_INSTALLER_IDENTITY }}" \
|
|
||||||
./release/${{ env.MACOS_FILENAME_UNSIGNED }} \
|
|
||||||
./release/${{ env.MACOS_FILENAME }}
|
|
||||||
rm ./release/${{ env.MACOS_FILENAME_UNSIGNED }}
|
|
||||||
- name: 'Notarize package'
|
|
||||||
if: ${{ env.GIT_TAG != '' }}
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
|
||||||
run: |
|
|
||||||
zip -r ./release/${{ env.MACOS_FILENAME }}.zip ./release/${{ env.MACOS_FILENAME }}
|
|
||||||
UPLOAD_RESULT=$(xcrun altool --notarize-app \
|
|
||||||
--primary-bundle-id "com.obsproject.obs-websocket" \
|
|
||||||
--username "${{ secrets.MACOS_NOTARIZATION_USERNAME }}" \
|
|
||||||
--password "${{ secrets.MACOS_NOTARIZATION_PASSWORD }}" \
|
|
||||||
--asc-provider "${{ secrets.ASC_PROVIDER_SHORTNAME }}" \
|
|
||||||
--file "./release/${{ env.MACOS_FILENAME }}.zip")
|
|
||||||
|
|
||||||
rm ./release/${{ env.MACOS_FILENAME }}.zip
|
|
||||||
|
|
||||||
REQUEST_UUID=$(echo $UPLOAD_RESULT | awk -F ' = ' '/RequestUUID/ {print $2}')
|
|
||||||
|
|
||||||
# Pieces of code borrowed from rednoah/notarized-app
|
|
||||||
while sleep 30 && date; do
|
|
||||||
CHECK_RESULT=$(xcrun altool \
|
|
||||||
--notarization-info "$REQUEST_UUID" \
|
|
||||||
--username "${{ secrets.MACOS_NOTARIZATION_USERNAME }}" \
|
|
||||||
--password "${{ secrets.MACOS_NOTARIZATION_PASSWORD }}" \
|
|
||||||
--asc-provider "${{ secrets.ASC_PROVIDER_SHORTNAME }}")
|
|
||||||
|
|
||||||
if ! grep -q "Status: in progress" <<< "$CHECK_RESULT"; then
|
|
||||||
xcrun stapler staple ./release/${{ env.MACOS_FILENAME }}
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
- name: 'Publish Packages'
|
|
||||||
if: success()
|
|
||||||
uses: actions/upload-artifact@v2-preview
|
|
||||||
with:
|
|
||||||
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-macOS'
|
|
||||||
path: '${{ github.workspace }}/obs-websocket/release/*.pkg'
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,4 +11,3 @@
|
|||||||
/docs/node_modules/
|
/docs/node_modules/
|
||||||
/src/plugin-macros.generated.h
|
/src/plugin-macros.generated.h
|
||||||
/installer/installer-windows.generated.iss
|
/installer/installer-windows.generated.iss
|
||||||
/cmake-build-debug/
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
/deps
|
|
||||||
/docs/comments/node_modules
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"line-length": false
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o pipefail
|
|
||||||
set -o nounset
|
|
||||||
|
|
||||||
if [ ${#} -eq 1 ]; then
|
|
||||||
VERBOSITY="--verbose"
|
|
||||||
else
|
|
||||||
VERBOSITY=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Runs the Clang Formatter in parallel on the code base.
|
|
||||||
# Return codes:
|
|
||||||
# - 1 there are files to be formatted
|
|
||||||
# - 0 everything looks fine
|
|
||||||
|
|
||||||
# Get CPU count
|
|
||||||
OS=$(uname)
|
|
||||||
NPROC=1
|
|
||||||
if [[ ${OS} = "Linux" ]] ; then
|
|
||||||
NPROC=$(nproc)
|
|
||||||
elif [[ ${OS} = "Darwin" ]] ; then
|
|
||||||
NPROC=$(sysctl -n hw.physicalcpu)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Discover clang-format
|
|
||||||
if type clang-format-12 2> /dev/null ; then
|
|
||||||
CLANG_FORMAT=clang-format-12
|
|
||||||
elif type clang-format 2> /dev/null ; then
|
|
||||||
# Clang format found, but need to check version
|
|
||||||
CLANG_FORMAT=clang-format
|
|
||||||
V=$(clang-format --version)
|
|
||||||
if [[ $V != *"version 12.0"* ]]; then
|
|
||||||
echo "clang-format is not 12.0 (returned ${V})"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "No appropriate clang-format found (expected clang-format-12.0.0, or clang-format)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
find . -type d \( \
|
|
||||||
-path ./\*build\* -o \
|
|
||||||
-path ./deps/websocketpp -o \
|
|
||||||
-path ./deps/asio -o \
|
|
||||||
-path ./deps/json -o \
|
|
||||||
-path ./deps/qr \
|
|
||||||
\) -prune -false -type f -o \
|
|
||||||
-name '*.h' -or \
|
|
||||||
-name '*.hpp' -or \
|
|
||||||
-name '*.m' -or \
|
|
||||||
-name '*.m,' -or \
|
|
||||||
-name '*.c' -or \
|
|
||||||
-name '*.cpp' \
|
|
||||||
| xargs -L100 -P ${NPROC} ${CLANG_FORMAT} ${VERBOSITY} -i -style=file -fallback-style=none
|
|
5
CI/macos/Brewfile
Normal file
5
CI/macos/Brewfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
brew "jack"
|
||||||
|
brew "speexdsp"
|
||||||
|
brew "cmake"
|
||||||
|
brew "freetype"
|
||||||
|
brew "fdk-aac"
|
27
CI/macos/build-plugin-macos.sh
Executable file
27
CI/macos/build-plugin-macos.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS build script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAS_CMAKE=$(type cmake 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_CMAKE}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[obs-websocket] Building 'obs-websocket' for macOS."
|
||||||
|
mkdir -p build && cd build
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
||||||
|
-DQTDIR=/tmp/obsdeps \
|
||||||
|
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
|
||||||
|
-DLIBOBS_LIB=../../obs-studio/libobs \
|
||||||
|
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
|
||||||
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
&& make -j4
|
39
CI/macos/install-build-obs-macos.sh
Executable file
39
CI/macos/install-build-obs-macos.sh
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS obs-studio build script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAS_CMAKE=$(type cmake 2>/dev/null)
|
||||||
|
HAS_GIT=$(type git 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_CMAKE}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${HAS_GIT}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] Git not installed - please install Xcode developer tools or via Homebrew."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build obs-studio
|
||||||
|
cd ..
|
||||||
|
echo "[obs-websocket] Cloning obs-studio from GitHub.."
|
||||||
|
git clone https://github.com/obsproject/obs-studio
|
||||||
|
cd obs-studio
|
||||||
|
OBSLatestTag=$(git describe --tags --abbrev=0)
|
||||||
|
git checkout $OBSLatestTag
|
||||||
|
mkdir build && cd build
|
||||||
|
echo "[obs-websocket] Building obs-studio.."
|
||||||
|
cmake .. \
|
||||||
|
-DQTDIR=/tmp/obsdeps \
|
||||||
|
-DDepsPath=/tmp/obsdeps \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
||||||
|
-DDISABLE_PLUGINS=true \
|
||||||
|
-DENABLE_SCRIPTING=0 \
|
||||||
|
-DCMAKE_PREFIX_PATH=/tmp/obsdeps/lib/cmake \
|
||||||
|
&& make -j4
|
57
CI/macos/install-dependencies-macos.sh
Executable file
57
CI/macos/install-dependencies-macos.sh
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS install dependencies script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAS_BREW=$(type brew 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_BREW}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] Please install Homebrew (https://www.brew.sh/) to build obs-websocket on macOS."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OBS Studio Brew Deps
|
||||||
|
echo "[obs-websocket] Updating Homebrew.."
|
||||||
|
brew update >/dev/null
|
||||||
|
echo "[obs-websocket] Checking installed Homebrew formulas.."
|
||||||
|
|
||||||
|
if [ -d /usr/local/opt/openssl@1.0.2t ]; then
|
||||||
|
brew uninstall openssl@1.0.2t
|
||||||
|
brew untap local/openssl
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /usr/local/opt/python@2.7.17 ]; then
|
||||||
|
brew uninstall python@2.7.17
|
||||||
|
brew untap local/python2
|
||||||
|
fi
|
||||||
|
|
||||||
|
brew bundle --file ./CI/macos/Brewfile
|
||||||
|
|
||||||
|
# Fetch and install Packages app
|
||||||
|
# =!= NOTICE =!=
|
||||||
|
# Installs a LaunchDaemon under /Library/LaunchDaemons/fr.whitebox.packages.build.dispatcher.plist
|
||||||
|
# =!= NOTICE =!=
|
||||||
|
|
||||||
|
HAS_PACKAGES=$(type packagesbuild 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_PACKAGES}" = "" ]; then
|
||||||
|
echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').."
|
||||||
|
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
|
||||||
|
sudo hdiutil attach ./Packages.dmg
|
||||||
|
sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target /
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OBS Deps
|
||||||
|
echo "[obs-websocket] Installing obs-websocket dependency 'OBS Deps ${OBS_DEPS_VERSION}'.."
|
||||||
|
wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-deps-${OBS_DEPS_VERSION}.tar.gz
|
||||||
|
tar -xf ./macos-deps-${OBS_DEPS_VERSION}.tar.gz -C /tmp
|
||||||
|
|
||||||
|
# Qt deps
|
||||||
|
echo "[obs-websocket] Installing obs-websocket dependency 'Qt ${QT_VERSION}'.."
|
||||||
|
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz
|
||||||
|
tar -xf ./macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz -C "/tmp"
|
||||||
|
xattr -r -d com.apple.quarantine /tmp/obsdeps
|
@ -514,7 +514,7 @@
|
|||||||
<key>CONCLUSION_ACTION</key>
|
<key>CONCLUSION_ACTION</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
<key>IDENTIFIER</key>
|
<key>IDENTIFIER</key>
|
||||||
<string>com.obsproject.obs-websocket</string>
|
<string>fr.palakis.obs-websocket</string>
|
||||||
<key>OVERWRITE_PERMISSIONS</key>
|
<key>OVERWRITE_PERMISSIONS</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>VERSION</key>
|
<key>VERSION</key>
|
||||||
|
93
CI/macos/package-plugin-macos.sh
Executable file
93
CI/macos/package-plugin-macos.sh
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS package script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[obs-websocket] Preparing package build"
|
||||||
|
|
||||||
|
GIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}')
|
||||||
|
|
||||||
|
VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG"
|
||||||
|
|
||||||
|
FILENAME_UNSIGNED="obs-websocket-$VERSION-Unsigned.pkg"
|
||||||
|
FILENAME="obs-websocket-$VERSION.pkg"
|
||||||
|
|
||||||
|
echo "[obs-websocket] Modifying obs-websocket.so linking"
|
||||||
|
install_name_tool \
|
||||||
|
-change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets \
|
||||||
|
@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \
|
||||||
|
-change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui \
|
||||||
|
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \
|
||||||
|
-change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore \
|
||||||
|
@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \
|
||||||
|
-change /tmp/obsdeps/lib/QtNetwork.framework/Versions/5/QtNetwork \
|
||||||
|
@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork \
|
||||||
|
-change /tmp/obsdeps/lib/QtSvg.framework/Versions/5/QtSvg \
|
||||||
|
@executable_path/../Frameworks/QtSvg.framework/Versions/5/QtSvg \
|
||||||
|
./build/obs-websocket.so
|
||||||
|
|
||||||
|
# Check if replacement worked
|
||||||
|
echo "[obs-websocket] Dependencies for obs-websocket"
|
||||||
|
otool -L ./build/obs-websocket.so
|
||||||
|
|
||||||
|
if [[ "$RELEASE_MODE" == "True" ]]; then
|
||||||
|
echo "[obs-websocket] Signing plugin binary: obs-websocket.so"
|
||||||
|
codesign --sign "$CODE_SIGNING_IDENTITY" ./build/obs-websocket.so
|
||||||
|
else
|
||||||
|
echo "[obs-websocket] Skipped plugin codesigning"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[obs-websocket] Actual package build"
|
||||||
|
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
||||||
|
|
||||||
|
echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME"
|
||||||
|
mv ./release/obs-websocket.pkg ./release/$FILENAME_UNSIGNED
|
||||||
|
|
||||||
|
if [[ "$RELEASE_MODE" == "True" ]]; then
|
||||||
|
echo "[obs-websocket] Signing installer: $FILENAME"
|
||||||
|
productsign \
|
||||||
|
--sign "$INSTALLER_SIGNING_IDENTITY" \
|
||||||
|
./release/$FILENAME_UNSIGNED \
|
||||||
|
./release/$FILENAME
|
||||||
|
rm ./release/$FILENAME_UNSIGNED
|
||||||
|
|
||||||
|
echo "[obs-websocket] Submitting installer $FILENAME for notarization"
|
||||||
|
zip -r ./release/$FILENAME.zip ./release/$FILENAME
|
||||||
|
UPLOAD_RESULT=$(xcrun altool \
|
||||||
|
--notarize-app \
|
||||||
|
--primary-bundle-id "fr.palakis.obs-websocket" \
|
||||||
|
--username "$AC_USERNAME" \
|
||||||
|
--password "$AC_PASSWORD" \
|
||||||
|
--asc-provider "$AC_PROVIDER_SHORTNAME" \
|
||||||
|
--file "./release/$FILENAME.zip")
|
||||||
|
rm ./release/$FILENAME.zip
|
||||||
|
|
||||||
|
REQUEST_UUID=$(echo $UPLOAD_RESULT | awk -F ' = ' '/RequestUUID/ {print $2}')
|
||||||
|
echo "Request UUID: $REQUEST_UUID"
|
||||||
|
|
||||||
|
echo "[obs-websocket] Wait for notarization result"
|
||||||
|
# Pieces of code borrowed from rednoah/notarized-app
|
||||||
|
while sleep 30 && date; do
|
||||||
|
CHECK_RESULT=$(xcrun altool \
|
||||||
|
--notarization-info "$REQUEST_UUID" \
|
||||||
|
--username "$AC_USERNAME" \
|
||||||
|
--password "$AC_PASSWORD" \
|
||||||
|
--asc-provider "$AC_PROVIDER_SHORTNAME")
|
||||||
|
echo $CHECK_RESULT
|
||||||
|
|
||||||
|
if ! grep -q "Status: in progress" <<< "$CHECK_RESULT"; then
|
||||||
|
echo "[obs-websocket] Staple ticket to installer: $FILENAME"
|
||||||
|
xcrun stapler staple ./release/$FILENAME
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "[obs-websocket] Skipped installer codesigning and notarization"
|
||||||
|
fi
|
@ -85,7 +85,7 @@ configure_file(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Include sources
|
# Inlude sources
|
||||||
set(obs-websocket_SOURCES
|
set(obs-websocket_SOURCES
|
||||||
src/obs-websocket.cpp
|
src/obs-websocket.cpp
|
||||||
src/Config.cpp
|
src/Config.cpp
|
||||||
@ -114,7 +114,6 @@ set(obs-websocket_SOURCES
|
|||||||
src/requesthandler/RequestHandler_Transitions.cpp
|
src/requesthandler/RequestHandler_Transitions.cpp
|
||||||
src/requesthandler/RequestHandler_Filters.cpp
|
src/requesthandler/RequestHandler_Filters.cpp
|
||||||
src/requesthandler/RequestHandler_SceneItems.cpp
|
src/requesthandler/RequestHandler_SceneItems.cpp
|
||||||
src/requesthandler/RequestHandler_Outputs.cpp
|
|
||||||
src/requesthandler/RequestHandler_Stream.cpp
|
src/requesthandler/RequestHandler_Stream.cpp
|
||||||
src/requesthandler/RequestHandler_Record.cpp
|
src/requesthandler/RequestHandler_Record.cpp
|
||||||
src/requesthandler/RequestHandler_MediaInputs.cpp
|
src/requesthandler/RequestHandler_MediaInputs.cpp
|
||||||
@ -129,6 +128,7 @@ set(obs-websocket_SOURCES
|
|||||||
src/utils/Json.cpp
|
src/utils/Json.cpp
|
||||||
src/utils/Obs.cpp
|
src/utils/Obs.cpp
|
||||||
src/utils/Obs_StringHelper.cpp
|
src/utils/Obs_StringHelper.cpp
|
||||||
|
src/utils/Obs_EnumHelper.cpp
|
||||||
src/utils/Obs_NumberHelper.cpp
|
src/utils/Obs_NumberHelper.cpp
|
||||||
src/utils/Obs_ArrayHelper.cpp
|
src/utils/Obs_ArrayHelper.cpp
|
||||||
src/utils/Obs_ObjectHelper.cpp
|
src/utils/Obs_ObjectHelper.cpp
|
||||||
|
15
README.md
15
README.md
@ -1,5 +1,3 @@
|
|||||||
<!-- markdownlint-disable no-inline-html -->
|
|
||||||
|
|
||||||
# obs-websocket
|
# obs-websocket
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -8,6 +6,7 @@
|
|||||||
|
|
||||||
WebSocket API for OBS Studio.
|
WebSocket API for OBS Studio.
|
||||||
|
|
||||||
|
[](https://github.com/obs-websocket/obs-websocket/actions/workflows/main.yml)
|
||||||
[](https://discord.gg/WBaSQ3A)
|
[](https://discord.gg/WBaSQ3A)
|
||||||
[](https://opencollective.com/obs-websocket-dev)
|
[](https://opencollective.com/obs-websocket-dev)
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ Binaries for Windows, MacOS, and Linux are available in the [Releases](https://g
|
|||||||
|
|
||||||
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
|
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
|
||||||
|
|
||||||
(Psst. You can use `--websocket_port`(value), `--websocket_password`(value), `--websocket_debug`(flag) and `--websocket_ipv4_only`(flag) on the command line to override the configured values.)
|
(Psst. You can use `--websocket_port`(value), `--websocket_password`(value), and `--websocket_debug`(flag) on the command line to override the configured values.)
|
||||||
|
|
||||||
### Possible use cases
|
### Possible use cases
|
||||||
|
|
||||||
@ -28,22 +27,18 @@ It is **highly recommended** to protect obs-websocket with a password against un
|
|||||||
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
|
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
|
||||||
|
|
||||||
### Client software
|
### Client software
|
||||||
|
- (No known clients supporting 5.0.0 at the moment. Send a message in Discord if you have one!)
|
||||||
- (No known clients supporting 5.0.0 at the moment. Ping us in the Discord if you have one!)
|
|
||||||
|
|
||||||
### Client libraries (for developers)
|
### Client libraries (for developers)
|
||||||
|
|
||||||
Here's a list of available language APIs for obs-websocket:
|
Here's a list of available language APIs for obs-websocket:
|
||||||
|
|
||||||
- Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit
|
- Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit
|
||||||
- Rust: [obws](https://github.com/dnaka91/obws/tree/v5-api) by dnaka91
|
- Rust: [obws](https://github.com/dnaka91/obws/tree/v5-api) by dnaka91
|
||||||
- Godot 3.4.x: [obs-websocket-gd](https://github.com/you-win/obs-websocket-gd) by you-win
|
|
||||||
- Javascript (Node and web): [obs-websocket-js](https://github.com/obs-websocket-community-projects/obs-websocket-js) by OBS Websocket Community
|
|
||||||
|
|
||||||
The 5.x server is a typical WebSocket server running by default on port 4455 (the port number can be changed in the Settings dialog under `Tools`).
|
The server is a typical Websockets server running by default on port 4444 (the port number can be changed in the Settings dialog under `Tools`).
|
||||||
The protocol we use is documented in [PROTOCOL.md](docs/generated/protocol.md).
|
The protocol we use is documented in [PROTOCOL.md](docs/generated/protocol.md).
|
||||||
|
|
||||||
We'd like to know what you're building with obs-websocket! If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A)
|
We'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
2
deps/asio
vendored
2
deps/asio
vendored
Submodule deps/asio updated: b73dc1d2c0...08a7029cb1
@ -1,13 +1,11 @@
|
|||||||
# obs-websocket documentation
|
# obs-websocket documentation
|
||||||
|
|
||||||
This is the documentation for obs-websocket. Run `build_docs.sh` to auto generate the latest docs from the `src` directory. There are 3 components to the docs generation:
|
This is the documentation for obs-websocket. Run build_docs.sh to auto generate the latest docs from the `src` directory. There are 3 components to the docs generation:
|
||||||
|
|
||||||
- `comments/comments.js`: Generates the `work/comments.json` file from the code comments in the src directory.
|
- `comments/comments.js`: Generates the `work/comments.json` file from the code comments in the src directory.
|
||||||
- `docs/process_comments.py`: Processes `work/comments.json` to create `generated/protocol.json`, which is a machine-readable documentation format that can be used to create obs-websocket client libraries.
|
- `docs/process_comments.py`: Processes `work/comments.json` to create `generated/protocol.json`, which is a machine-readable documentation format that can be used to create obs-websocket client libraries.
|
||||||
- `docs/generate_md.py`: Processes `generated/protocol.json` to create `generated/protocol.md`, which is the actual human-readable documentation.
|
- `docs/generate_md.py`: Processes `generated/protocol.json` to create `generated/protocol.md`, which is the actual human-readable documentation.
|
||||||
|
|
||||||
Some notes about documenting:
|
Some notes about documenting:
|
||||||
|
|
||||||
- The `complexity` comment line is a suggestion to the user about how much knowledge about OBS's inner workings is required to safely use the associated feature. `1` for easy, `5` for megadeath-expert.
|
- The `complexity` comment line is a suggestion to the user about how much knowledge about OBS's inner workings is required to safely use the associated feature. `1` for easy, `5` for megadeath-expert.
|
||||||
- The `rpcVersion` comment line is used to specify the latest available version that the feature is available in. If a feature is deprecated, then the placeholder value of `-1` should be replaced with the last RPC version that the feature will be available in. Manually specifying an RPC version automatically adds the `Deprecated` line to the entry in `generated/protocol.md`.
|
- The `rpcVersion` comment line is used to specify the latest available version that the feature is available in. If a feature is deprecated, then the placeholder value of `-1` should be replaced with the last RPC version that the feature will be available in. Manually specifying an RPC version automatically adds the `Deprecated` line to the entry in `generated/protocol.md`.
|
||||||
- The description can be multiple lines, but must be contained above the first documentation property (the lines starting with `@`).
|
- The description can be multiple lines, but must be contained above the first documentation property (the lines starting with `@`).
|
||||||
@ -15,15 +13,13 @@ Some notes about documenting:
|
|||||||
- `Array` types follow this format: `Array<subtype>`, for example `Array<String>` to specify an array of strings.
|
- `Array` types follow this format: `Array<subtype>`, for example `Array<String>` to specify an array of strings.
|
||||||
|
|
||||||
Formatting notes:
|
Formatting notes:
|
||||||
|
|
||||||
- Fields should have their columns aligned. So in a request, the columns of all `@requestField`s should be aligned.
|
- Fields should have their columns aligned. So in a request, the columns of all `@requestField`s should be aligned.
|
||||||
- We suggest looking at how other enums/events/requests have been documented before documenting one of your own, to get a general feel of how things have been formatted.
|
- We suggest looking at how other enums/events/requests have been documented before documenting one of your own, to get a general feel of how things have been formatted.
|
||||||
|
|
||||||
## Creating enum documentation
|
## Creating enum documentation
|
||||||
|
|
||||||
Enums follow this code comment format:
|
Enums follow this code comment format:
|
||||||
|
```
|
||||||
```js
|
|
||||||
/**
|
/**
|
||||||
* [description]
|
* [description]
|
||||||
*
|
*
|
||||||
@ -37,8 +33,7 @@ Enums follow this code comment format:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Example code comment:
|
Example code comment:
|
||||||
|
```
|
||||||
```js
|
|
||||||
/**
|
/**
|
||||||
* The initial message sent by obs-websocket to newly connected clients.
|
* The initial message sent by obs-websocket to newly connected clients.
|
||||||
*
|
*
|
||||||
@ -50,14 +45,12 @@ Example code comment:
|
|||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
|
||||||
- This is the documentation for the `WebSocketOpCode::Hello` enum identifier.
|
- This is the documentation for the `WebSocketOpCode::Hello` enum identifier.
|
||||||
|
|
||||||
## Creating event documentation
|
## Creating event documentation
|
||||||
|
|
||||||
Events follow this code comment format:
|
Events follow this code comment format:
|
||||||
|
```
|
||||||
```js
|
|
||||||
/**
|
/**
|
||||||
* [description]
|
* [description]
|
||||||
*
|
*
|
||||||
@ -75,8 +68,7 @@ Events follow this code comment format:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Example code comment:
|
Example code comment:
|
||||||
|
```
|
||||||
```js
|
|
||||||
/**
|
/**
|
||||||
* Studio mode has been enabled or disabled.
|
* Studio mode has been enabled or disabled.
|
||||||
*
|
*
|
||||||
@ -95,8 +87,7 @@ Example code comment:
|
|||||||
## Creating request documentation
|
## Creating request documentation
|
||||||
|
|
||||||
Requests follow this code comment format:
|
Requests follow this code comment format:
|
||||||
|
```
|
||||||
```js
|
|
||||||
/**
|
/**
|
||||||
* [description]
|
* [description]
|
||||||
*
|
*
|
||||||
@ -114,12 +105,10 @@ Requests follow this code comment format:
|
|||||||
* @api requests
|
* @api requests
|
||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
|
||||||
- The optional flag is a `?` that prefixes the field name, telling the docs processor that the field is optionally specified.
|
- The optional flag is a `?` that prefixes the field name, telling the docs processor that the field is optionally specified.
|
||||||
|
|
||||||
Example code comment:
|
Example code comment:
|
||||||
|
```
|
||||||
```js
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of a "slot" from the selected persistent data realm.
|
* Gets the value of a "slot" from the selected persistent data realm.
|
||||||
*
|
*
|
||||||
|
@ -30,6 +30,7 @@ categoryOrder = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
requestFieldHeader = """
|
requestFieldHeader = """
|
||||||
|
|
||||||
**Request Fields:**
|
**Request Fields:**
|
||||||
|
|
||||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||||
@ -37,6 +38,7 @@ requestFieldHeader = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
responseFieldHeader = """
|
responseFieldHeader = """
|
||||||
|
|
||||||
**Response Fields:**
|
**Response Fields:**
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
@ -44,6 +46,7 @@ responseFieldHeader = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
dataFieldHeader = """
|
dataFieldHeader = """
|
||||||
|
|
||||||
**Data Fields:**
|
**Data Fields:**
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
@ -127,8 +130,6 @@ def get_enums(enums):
|
|||||||
ret += '- Added in v{}\n'.format(enumIdentifier['initialVersion'])
|
ret += '- Added in v{}\n'.format(enumIdentifier['initialVersion'])
|
||||||
if enumIdentifier != enum['enumIdentifiers'][-1]:
|
if enumIdentifier != enum['enumIdentifiers'][-1]:
|
||||||
ret += '\n---\n\n'
|
ret += '\n---\n\n'
|
||||||
else:
|
|
||||||
ret += '\n'
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_requests_toc(requests):
|
def get_requests_toc(requests):
|
||||||
@ -142,7 +143,7 @@ def get_requests_toc(requests):
|
|||||||
if not len(requestsOut):
|
if not len(requestsOut):
|
||||||
continue
|
continue
|
||||||
categoryFragment = get_fragment(category, False)
|
categoryFragment = get_fragment(category, False)
|
||||||
ret += '- [{} Requests](#{}-requests)\n'.format(category, categoryFragment)
|
ret += '- [{}](#{})\n'.format(category, categoryFragment)
|
||||||
for request in requestsOut:
|
for request in requestsOut:
|
||||||
requestType = request['requestType']
|
requestType = request['requestType']
|
||||||
requestTypeFragment = get_fragment(requestType, False)
|
requestTypeFragment = get_fragment(requestType, False)
|
||||||
@ -160,7 +161,7 @@ def get_requests(requests):
|
|||||||
if not len(requestsOut):
|
if not len(requestsOut):
|
||||||
continue
|
continue
|
||||||
categoryFragment = get_fragment(category)
|
categoryFragment = get_fragment(category)
|
||||||
ret += '## {} Requests\n\n'.format(category)
|
ret += '\n\n## {}\n\n'.format(category)
|
||||||
for request in requestsOut:
|
for request in requestsOut:
|
||||||
requestType = request['requestType']
|
requestType = request['requestType']
|
||||||
requestTypeFragment = get_fragment(requestType)
|
requestTypeFragment = get_fragment(requestType)
|
||||||
@ -192,8 +193,6 @@ def get_requests(requests):
|
|||||||
|
|
||||||
if request != requestsOut[-1]:
|
if request != requestsOut[-1]:
|
||||||
ret += '\n---\n\n'
|
ret += '\n---\n\n'
|
||||||
else:
|
|
||||||
ret += '\n'
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_events_toc(events):
|
def get_events_toc(events):
|
||||||
@ -207,7 +206,7 @@ def get_events_toc(events):
|
|||||||
if not len(eventsOut):
|
if not len(eventsOut):
|
||||||
continue
|
continue
|
||||||
categoryFragment = get_fragment(category, False)
|
categoryFragment = get_fragment(category, False)
|
||||||
ret += '- [{} Events](#{}-events)\n'.format(category, categoryFragment)
|
ret += '- [{}](#{})\n'.format(category, categoryFragment)
|
||||||
for event in eventsOut:
|
for event in eventsOut:
|
||||||
eventType = event['eventType']
|
eventType = event['eventType']
|
||||||
eventTypeFragment = get_fragment(eventType, False)
|
eventTypeFragment = get_fragment(eventType, False)
|
||||||
@ -225,7 +224,7 @@ def get_events(events):
|
|||||||
if not len(eventsOut):
|
if not len(eventsOut):
|
||||||
continue
|
continue
|
||||||
categoryFragment = get_fragment(category)
|
categoryFragment = get_fragment(category)
|
||||||
ret += '## {} Events\n\n'.format(category)
|
ret += '## {}\n\n'.format(category)
|
||||||
for event in eventsOut:
|
for event in eventsOut:
|
||||||
eventType = event['eventType']
|
eventType = event['eventType']
|
||||||
eventTypeFragment = get_fragment(eventType)
|
eventTypeFragment = get_fragment(eventType)
|
||||||
@ -248,8 +247,6 @@ def get_events(events):
|
|||||||
|
|
||||||
if event != eventsOut[-1]:
|
if event != eventsOut[-1]:
|
||||||
ret += '\n---\n\n'
|
ret += '\n---\n\n'
|
||||||
else:
|
|
||||||
ret += '\n'
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
# Actual code
|
# Actual code
|
||||||
@ -267,41 +264,40 @@ except IOError:
|
|||||||
with open('../generated/protocol.json', 'r') as f:
|
with open('../generated/protocol.json', 'r') as f:
|
||||||
protocol = json.load(f)
|
protocol = json.load(f)
|
||||||
|
|
||||||
output = "<!-- This file was automatically generated. Do not edit directly! -->\n"
|
output = "<!-- This file was automatically generated. Do not edit directly! -->\n\n"
|
||||||
output += "<!-- markdownlint-disable no-bare-urls -->\n"
|
|
||||||
|
|
||||||
# Insert introduction partial
|
# Insert introduction partial
|
||||||
output += read_file('partials/introduction.md')
|
output += read_file('partials/introduction.md')
|
||||||
logging.info('Inserted introduction section.')
|
logging.info('Inserted introduction section.')
|
||||||
|
|
||||||
output += '\n'
|
output += '\n\n'
|
||||||
|
|
||||||
# Generate enums MD
|
# Generate enums MD
|
||||||
output += read_file('partials/enumsHeader.md')
|
output += read_file('partials/enumsHeader.md')
|
||||||
output += '\n'
|
|
||||||
output += get_enums_toc(protocol['enums'])
|
output += get_enums_toc(protocol['enums'])
|
||||||
output += '\n'
|
output += '\n\n'
|
||||||
output += get_enums(protocol['enums'])
|
output += get_enums(protocol['enums'])
|
||||||
logging.info('Inserted enums section.')
|
logging.info('Inserted enums section.')
|
||||||
|
|
||||||
|
output += '\n\n'
|
||||||
|
|
||||||
# Generate events MD
|
# Generate events MD
|
||||||
output += read_file('partials/eventsHeader.md')
|
output += read_file('partials/eventsHeader.md')
|
||||||
output += '\n'
|
|
||||||
output += get_events_toc(protocol['events'])
|
output += get_events_toc(protocol['events'])
|
||||||
output += '\n'
|
output += '\n\n'
|
||||||
output += get_events(protocol['events'])
|
output += get_events(protocol['events'])
|
||||||
logging.info('Inserted events section.')
|
logging.info('Inserted events section.')
|
||||||
|
|
||||||
|
output += '\n\n'
|
||||||
|
|
||||||
# Generate requests MD
|
# Generate requests MD
|
||||||
output += read_file('partials/requestsHeader.md')
|
output += read_file('partials/requestsHeader.md')
|
||||||
output += '\n'
|
|
||||||
output += get_requests_toc(protocol['requests'])
|
output += get_requests_toc(protocol['requests'])
|
||||||
output += '\n'
|
output += '\n\n'
|
||||||
output += get_requests(protocol['requests'])
|
output += get_requests(protocol['requests'])
|
||||||
logging.info('Inserted requests section.')
|
logging.info('Inserted requests section.')
|
||||||
|
|
||||||
if output.endswith('\n\n'):
|
output += '\n\n'
|
||||||
output = output[:-1]
|
|
||||||
|
|
||||||
# Write new protocol MD
|
# Write new protocol MD
|
||||||
with open('../generated/protocol.md', 'w') as f:
|
with open('../generated/protocol.md', 'w') as f:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Enums
|
# Enums
|
||||||
|
|
||||||
These are enumeration declarations, which are referenced throughout obs-websocket's protocol.
|
These are enumeration declarations, which are referenced throughout obs-websocket's protocol.
|
||||||
|
|
||||||
## Enumerations Table of Contents
|
### Enumerations Table of Contents
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# Events
|
# Events
|
||||||
|
|
||||||
## Events Table of Contents
|
### Events Table of Contents
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
|
# Main Table of Contents
|
||||||
|
- [obs-websocket 5.0.0 Protocol](#obs-websocket-500-protocol)
|
||||||
|
- [Connecting to obs-websocket](#connecting-to-obs-websocket)
|
||||||
|
- [Connection steps](#connection-steps)
|
||||||
|
- [Creating an authentication string](#creating-an-authentication-string)
|
||||||
|
- [Base message types](#message-types)
|
||||||
|
- [OpCode 0 Hello](#hello-opcode-0)
|
||||||
|
- [OpCode 1 Identify](#identify-opcode-1)
|
||||||
|
- [OpCode 2 Identified](#identified-opcode-2)
|
||||||
|
- [OpCode 3 Reidentify](#reidentify-opcode-3)
|
||||||
|
- [OpCode 5 Event](#event-opcode-5)
|
||||||
|
- [OpCode 6 Request](#request-opcode-6)
|
||||||
|
- [OpCode 7 RequestResponse](#requestresponse-opcode-7)
|
||||||
|
- [OpCode 8 RequestBatch](#requestbatch-opcode-8)
|
||||||
|
- [OpCode 9 RequestBatchResponse](#requestbatchresponse-opcode-9)
|
||||||
|
- [Enums](#enums)
|
||||||
|
- [Events](#events)
|
||||||
|
- [Requests](#requests)
|
||||||
|
|
||||||
# obs-websocket 5.0.0 Protocol
|
# obs-websocket 5.0.0 Protocol
|
||||||
|
|
||||||
## Main Table of Contents
|
|
||||||
|
|
||||||
- [General Intro](#general-intro)
|
|
||||||
- [Design Goals](#design-goals)
|
|
||||||
- [Connecting to obs-websocket](#connecting-to-obs-websocket)
|
|
||||||
- [Connection steps](#connection-steps)
|
|
||||||
- [Connection Notes](#connection-notes)
|
|
||||||
- [Creating an authentication string](#creating-an-authentication-string)
|
|
||||||
- [Message Types (OpCodes)](#message-types-opcodes)
|
|
||||||
- [Hello (OpCode 0)](#hello-opcode-0)
|
|
||||||
- [Identify (OpCode 1)](#identify-opcode-1)
|
|
||||||
- [Identified (OpCode 2)](#identified-opcode-2)
|
|
||||||
- [Reidentify (OpCode 3)](#reidentify-opcode-3)
|
|
||||||
- [Event (OpCode 5)](#event-opcode-5)
|
|
||||||
- [Request (OpCode 6)](#request-opcode-6)
|
|
||||||
- [RequestResponse (OpCode 7)](#requestresponse-opcode-7)
|
|
||||||
- [RequestBatch (OpCode 8)](#requestbatch-opcode-8)
|
|
||||||
- [RequestBatchResponse (OpCode 9)](#requestbatchresponse-opcode-9)
|
|
||||||
|
|
||||||
## General Intro
|
## General Intro
|
||||||
|
|
||||||
obs-websocket provides a feature-rich RPC communication protocol, giving access to much of OBS's feature set. This document contains everything you should know in order to make a connection and use obs-websocket's functionality to the fullest.
|
obs-websocket provides a feature-rich RPC communication protocol, giving access to much of OBS's feature set. This document contains everything you should know in order to make a connection and use obs-websocket's functionality to the fullest.
|
||||||
|
|
||||||
### Design Goals
|
### Design Goals
|
||||||
|
|
||||||
- Abstraction of identification, events, requests, and batch requests into dedicated message types
|
- Abstraction of identification, events, requests, and batch requests into dedicated message types
|
||||||
- Conformity of request naming using similar terms like `Get`, `Set`, `Get[x]List`, `Start[x]`, `Toggle[x]`
|
- Conformity of request naming using similar terms like `Get`, `Set`, `Get[x]List`, `Start[x]`, `Toggle[x]`
|
||||||
- Conformity of OBS data field names like `sourceName`, `sourceKind`, `sourceType`, `sceneName`, `sceneItemName`
|
- Conformity of OBS data field names like `sourceName`, `sourceKind`, `sourceType`, `sceneName`, `sceneItemName`
|
||||||
@ -34,14 +31,13 @@ obs-websocket provides a feature-rich RPC communication protocol, giving access
|
|||||||
- PubSub system - Allow clients to specify which events they do or don't want to receive from OBS
|
- PubSub system - Allow clients to specify which events they do or don't want to receive from OBS
|
||||||
- RPC versioning - Client and server negotiate the latest version of the obs-websocket protocol to communicate with.
|
- RPC versioning - Client and server negotiate the latest version of the obs-websocket protocol to communicate with.
|
||||||
|
|
||||||
## Connecting to obs-websocket
|
|
||||||
|
|
||||||
|
## Connecting to obs-websocket
|
||||||
Here's info on how to connect to obs-websocket
|
Here's info on how to connect to obs-websocket
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Connection steps
|
### Connection steps
|
||||||
|
|
||||||
These steps should be followed precisely. Failure to connect to the server as instructed will likely result in your client being treated in an undefined way.
|
These steps should be followed precisely. Failure to connect to the server as instructed will likely result in your client being treated in an undefined way.
|
||||||
|
|
||||||
- Initial HTTP request made to the obs-websocket server.
|
- Initial HTTP request made to the obs-websocket server.
|
||||||
@ -68,7 +64,6 @@ These steps should be followed precisely. Failure to connect to the server as in
|
|||||||
- At any time after a client has been identified, it may send an [OpCode 3 `Reidentify`](#reidentify-opcode-3) message to update certain allowed session parameters. The server will respond in the same way it does during initial identification.
|
- At any time after a client has been identified, it may send an [OpCode 3 `Reidentify`](#reidentify-opcode-3) message to update certain allowed session parameters. The server will respond in the same way it does during initial identification.
|
||||||
|
|
||||||
#### Connection Notes
|
#### Connection Notes
|
||||||
|
|
||||||
- If a binary frame is received when using the `obswebsocket.json` (default) subprotocol, or a text frame is received while using the `obswebsocket.msgpack` subprotocol, the connection is closed with `WebSocketCloseCode::MessageDecodeError`.
|
- If a binary frame is received when using the `obswebsocket.json` (default) subprotocol, or a text frame is received while using the `obswebsocket.msgpack` subprotocol, the connection is closed with `WebSocketCloseCode::MessageDecodeError`.
|
||||||
- The obs-websocket server listens for any messages containing a `request-type` field in the first level JSON from unidentified clients. If a message matches, the connection is closed with `WebSocketCloseCode::UnsupportedRpcVersion` and a warning is logged.
|
- The obs-websocket server listens for any messages containing a `request-type` field in the first level JSON from unidentified clients. If a message matches, the connection is closed with `WebSocketCloseCode::UnsupportedRpcVersion` and a warning is logged.
|
||||||
- If a message with a `messageType` is not recognized to the obs-websocket server, the connection is closed with `WebSocketCloseCode::UnknownOpCode`.
|
- If a message with a `messageType` is not recognized to the obs-websocket server, the connection is closed with `WebSocketCloseCode::UnknownOpCode`.
|
||||||
@ -77,13 +72,11 @@ These steps should be followed precisely. Failure to connect to the server as in
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Creating an authentication string
|
### Creating an authentication string
|
||||||
|
|
||||||
obs-websocket uses SHA256 to transmit authentication credentials. The server starts by sending an object in the `authentication` field of its `Hello` message data. The client processes the authentication challenge and responds via the `authentication` string in the `Identify` message data.
|
obs-websocket uses SHA256 to transmit authentication credentials. The server starts by sending an object in the `authentication` field of its `Hello` message data. The client processes the authentication challenge and responds via the `authentication` string in the `Identify` message data.
|
||||||
|
|
||||||
For this guide, we'll be using `supersecretpassword` as the password.
|
For this guide, we'll be using `supersecretpassword` as the password.
|
||||||
|
|
||||||
The `authentication` object in `Hello` looks like this (example):
|
The `authentication` object in `Hello` looks like this (example):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"challenge": "+IxH4CnCiqpX1rM9scsNynZzbOe4KhDeYcTNS3PDaeY=",
|
"challenge": "+IxH4CnCiqpX1rM9scsNynZzbOe4KhDeYcTNS3PDaeY=",
|
||||||
@ -92,7 +85,6 @@ The `authentication` object in `Hello` looks like this (example):
|
|||||||
```
|
```
|
||||||
|
|
||||||
To generate the authentication string, follow these steps:
|
To generate the authentication string, follow these steps:
|
||||||
|
|
||||||
- Concatenate the websocket password with the `salt` provided by the server (`password + salt`)
|
- Concatenate the websocket password with the `salt` provided by the server (`password + salt`)
|
||||||
- Generate an SHA256 binary hash of the result and base64 encode it, known as a base64 secret.
|
- Generate an SHA256 binary hash of the result and base64 encode it, known as a base64 secret.
|
||||||
- Concatenate the base64 secret with the `challenge` sent by the server (`base64_secret + challenge`)
|
- Concatenate the base64 secret with the `challenge` sent by the server (`base64_secret + challenge`)
|
||||||
@ -100,45 +92,39 @@ To generate the authentication string, follow these steps:
|
|||||||
|
|
||||||
For real-world examples of the `authentication` string creation, refer to the obs-websocket client libraries listed on the [README](README.md).
|
For real-world examples of the `authentication` string creation, refer to the obs-websocket client libraries listed on the [README](README.md).
|
||||||
|
|
||||||
## Message Types (OpCodes)
|
|
||||||
|
|
||||||
|
## Message Types (OpCodes)
|
||||||
The following message types are the low-level message types which may be sent to and from obs-websocket.
|
The following message types are the low-level message types which may be sent to and from obs-websocket.
|
||||||
|
|
||||||
Messages sent from the obs-websocket server or client may contain these first-level fields, known as the base object:
|
Messages sent from the obs-websocket server or client may contain these first-level fields, known as the base object:
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"op": number,
|
"op": number,
|
||||||
"d": object
|
"d": object
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `op` is a `WebSocketOpCode` OpCode.
|
- `op` is a `WebSocketOpCode` OpCode.
|
||||||
- `d` is an object of the data fields associated with the operation.
|
- `d` is an object of the data fields associated with the operation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Hello (OpCode 0)
|
### Hello (OpCode 0)
|
||||||
|
|
||||||
- Sent from: obs-websocket
|
- Sent from: obs-websocket
|
||||||
- Sent to: Freshly connected websocket client
|
- Sent to: Freshly connected websocket client
|
||||||
- Description: First message sent from the server immediately on client connection. Contains authentication information if auth is required. Also contains RPC version for version negotiation.
|
- Description: First message sent from the server immediately on client connection. Contains authentication information if auth is required. Also contains RPC version for version negotiation.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"obsWebSocketVersion": string,
|
"obsWebSocketVersion": string,
|
||||||
"rpcVersion": number,
|
"rpcVersion": number,
|
||||||
"authentication": object(optional)
|
"authentication": object(optional)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `rpcVersion` is a version number which gets incremented on each **breaking change** to the obs-websocket protocol. Its usage in this context is to provide the current rpc version that the server would like to use.
|
- `rpcVersion` is a version number which gets incremented on each **breaking change** to the obs-websocket protocol. Its usage in this context is to provide the current rpc version that the server would like to use.
|
||||||
|
|
||||||
**Example Messages:**
|
**Example Messages:**
|
||||||
Authentication is required
|
Authentication is required
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 0,
|
"op": 0,
|
||||||
@ -154,7 +140,6 @@ Authentication is required
|
|||||||
```
|
```
|
||||||
|
|
||||||
Authentication is not required
|
Authentication is not required
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 0,
|
"op": 0,
|
||||||
@ -168,26 +153,22 @@ Authentication is not required
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Identify (OpCode 1)
|
### Identify (OpCode 1)
|
||||||
|
|
||||||
- Sent from: Freshly connected websocket client
|
- Sent from: Freshly connected websocket client
|
||||||
- Sent to: obs-websocket
|
- Sent to: obs-websocket
|
||||||
- Description: Response to `Hello` message, should contain authentication string if authentication is required, along with PubSub subscriptions and other session parameters.
|
- Description: Response to `Hello` message, should contain authentication string if authentication is required, along with PubSub subscriptions and other session parameters.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"rpcVersion": number,
|
"rpcVersion": number,
|
||||||
"authentication": string(optional),
|
"authentication": string(optional),
|
||||||
"eventSubscriptions": number(optional) = (EventSubscription::All)
|
"eventSubscriptions": number(optional) = (EventSubscription::All)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `rpcVersion` is the version number that the client would like the obs-websocket server to use.
|
- `rpcVersion` is the version number that the client would like the obs-websocket server to use.
|
||||||
- `eventSubscriptions` is a bitmask of `EventSubscriptions` items to subscribe to events and event categories at will. By default, all event categories are subscribed, except for events marked as high volume. High volume events must be explicitly subscribed to.
|
- `eventSubscriptions` is a bitmask of `EventSubscriptions` items to subscribe to events and event categories at will. By default, all event categories are subscribed, except for events marked as high volume. High volume events must be explicitly subscribed to.
|
||||||
|
|
||||||
**Example Message:**
|
**Example Message:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 1,
|
"op": 1,
|
||||||
@ -202,23 +183,19 @@ Authentication is not required
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Identified (OpCode 2)
|
### Identified (OpCode 2)
|
||||||
|
|
||||||
- Sent from: obs-websocket
|
- Sent from: obs-websocket
|
||||||
- Sent to: Freshly identified client
|
- Sent to: Freshly identified client
|
||||||
- Description: The identify request was received and validated, and the connection is now ready for normal operation.
|
- Description: The identify request was received and validated, and the connection is now ready for normal operation.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"negotiatedRpcVersion": number
|
"negotiatedRpcVersion": number
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- If rpc version negotiation succeeds, the server determines the RPC version to be used and gives it to the client as `negotiatedRpcVersion`
|
- If rpc version negotiation succeeds, the server determines the RPC version to be used and gives it to the client as `negotiatedRpcVersion`
|
||||||
|
|
||||||
**Example Message:**
|
**Example Message:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 2,
|
"op": 2,
|
||||||
@ -231,43 +208,36 @@ Authentication is not required
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Reidentify (OpCode 3)
|
### Reidentify (OpCode 3)
|
||||||
|
|
||||||
- Sent from: Identified client
|
- Sent from: Identified client
|
||||||
- Sent to: obs-websocket
|
- Sent to: obs-websocket
|
||||||
- Description: Sent at any time after initial identification to update the provided session parameters.
|
- Description: Sent at any time after initial identification to update the provided session parameters.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"eventSubscriptions": number(optional) = (EventSubscription::All)
|
"eventSubscriptions": number(optional) = (EventSubscription::All)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Only the listed parameters may be changed after initial identification. To change a parameter not listed, you must reconnect to the obs-websocket server.
|
- Only the listed parameters may be changed after initial identification. To change a parameter not listed, you must reconnect to the obs-websocket server.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Event (OpCode 5)
|
### Event (OpCode 5)
|
||||||
|
|
||||||
- Sent from: obs-websocket
|
- Sent from: obs-websocket
|
||||||
- Sent to: All subscribed and identified clients
|
- Sent to: All subscribed and identified clients
|
||||||
- Description: An event coming from OBS has occured. Eg scene switched, source muted.
|
- Description: An event coming from OBS has occured. Eg scene switched, source muted.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"eventType": string,
|
"eventType": string,
|
||||||
"eventIntent": number,
|
"eventIntent": number,
|
||||||
"eventData": object(optional)
|
"eventData": object(optional)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `eventIntent` is the original intent required to be subscribed to in order to receive the event.
|
- `eventIntent` is the original intent required to be subscribed to in order to receive the event.
|
||||||
|
|
||||||
**Example Message:**
|
**Example Message:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 5,
|
"op": 5,
|
||||||
@ -284,14 +254,12 @@ Authentication is not required
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Request (OpCode 6)
|
### Request (OpCode 6)
|
||||||
|
|
||||||
- Sent from: Identified client
|
- Sent from: Identified client
|
||||||
- Sent to: obs-websocket
|
- Sent to: obs-websocket
|
||||||
- Description: Client is making a request to obs-websocket. Eg get current scene, create source.
|
- Description: Client is making a request to obs-websocket. Eg get current scene, create source.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"requestType": string,
|
"requestType": string,
|
||||||
"requestId": string,
|
"requestId": string,
|
||||||
@ -301,7 +269,6 @@ Authentication is not required
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Example Message:**
|
**Example Message:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 6,
|
"op": 6,
|
||||||
@ -318,14 +285,12 @@ Authentication is not required
|
|||||||
---
|
---
|
||||||
|
|
||||||
### RequestResponse (OpCode 7)
|
### RequestResponse (OpCode 7)
|
||||||
|
|
||||||
- Sent from: obs-websocket
|
- Sent from: obs-websocket
|
||||||
- Sent to: Identified client which made the request
|
- Sent to: Identified client which made the request
|
||||||
- Description: obs-websocket is responding to a request coming from a client.
|
- Description: obs-websocket is responding to a request coming from a client.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"requestType": string,
|
"requestType": string,
|
||||||
"requestId": string,
|
"requestId": string,
|
||||||
@ -333,26 +298,22 @@ Authentication is not required
|
|||||||
"responseData": object(optional)
|
"responseData": object(optional)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- The `requestType` and `requestId` are simply mirrors of what was sent by the client.
|
- The `requestType` and `requestId` are simply mirrors of what was sent by the client.
|
||||||
|
|
||||||
`requestStatus` object:
|
`requestStatus` object:
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"result": bool,
|
"result": bool,
|
||||||
"code": number,
|
"code": number,
|
||||||
"comment": string(optional)
|
"comment": string(optional)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `result` is `true` if the request resulted in `RequestStatus::Success`. False if otherwise.
|
- `result` is `true` if the request resulted in `RequestStatus::Success`. False if otherwise.
|
||||||
- `code` is a `RequestStatus` code.
|
- `code` is a `RequestStatus` code.
|
||||||
- `comment` may be provided by the server on errors to offer further details on why a request failed.
|
- `comment` may be provided by the server on errors to offer further details on why a request failed.
|
||||||
|
|
||||||
**Example Messages:**
|
**Example Messages:**
|
||||||
Successful Response
|
Successful Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 7,
|
"op": 7,
|
||||||
@ -368,7 +329,6 @@ Successful Response
|
|||||||
```
|
```
|
||||||
|
|
||||||
Failure Response
|
Failure Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": 7,
|
"op": 7,
|
||||||
@ -387,14 +347,12 @@ Failure Response
|
|||||||
---
|
---
|
||||||
|
|
||||||
### RequestBatch (OpCode 8)
|
### RequestBatch (OpCode 8)
|
||||||
|
|
||||||
- Sent from: Identified client
|
- Sent from: Identified client
|
||||||
- Sent to: obs-websocket
|
- Sent to: obs-websocket
|
||||||
- Description: Client is making a batch of requests for obs-websocket. Requests are processed serially (in order) by the server.
|
- Description: Client is making a batch of requests for obs-websocket. Requests are processed serially (in order) by the server.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"requestId": string,
|
"requestId": string,
|
||||||
"haltOnFailure": bool(optional) = false,
|
"haltOnFailure": bool(optional) = false,
|
||||||
@ -402,21 +360,18 @@ Failure Response
|
|||||||
"requests": array<object>
|
"requests": array<object>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- When `haltOnFailure` is `true`, the processing of requests will be halted on first failure. Returns only the processed requests in [`RequestBatchResponse`](#requestbatchresponse-opcode-9).
|
- When `haltOnFailure` is `true`, the processing of requests will be halted on first failure. Returns only the processed requests in [`RequestBatchResponse`](#requestbatchresponse-opcode-9).
|
||||||
- Requests in the `requests` array follow the same structure as the `Request` payload data format, however `requestId` is an optional field.
|
- Requests in the `requests` array follow the same structure as the `Request` payload data format, however `requestId` is an optional field.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### RequestBatchResponse (OpCode 9)
|
### RequestBatchResponse (OpCode 9)
|
||||||
|
|
||||||
- Sent from: obs-websocket
|
- Sent from: obs-websocket
|
||||||
- Sent to: Identified client which made the request
|
- Sent to: Identified client which made the request
|
||||||
- Description: obs-websocket is responding to a request batch coming from the client.
|
- Description: obs-websocket is responding to a request batch coming from the client.
|
||||||
|
|
||||||
**Data Keys:**
|
**Data Keys:**
|
||||||
|
```
|
||||||
```txt
|
|
||||||
{
|
{
|
||||||
"requestId": string,
|
"requestId": string,
|
||||||
"results": array<object>
|
"results": array<object>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# Requests
|
# Requests
|
||||||
|
|
||||||
## Requests Table of Contents
|
### Requests Table of Contents
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
#define MyAppName "obs-websocket"
|
#define MyAppName "obs-websocket"
|
||||||
#define MyAppVersion "@OBS_WEBSOCKET_VERSION@"
|
#define MyAppVersion "@OBS_WEBSOCKET_VERSION@"
|
||||||
#define MyAppPublisher "OBS Project"
|
#define MyAppPublisher "obs-websocket"
|
||||||
#define MyAppURL "http://github.com/obsproject/obs-websocket"
|
#define MyAppURL "http://github.com/obsproject/obs-websocket"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
|
@ -1,24 +1,3 @@
|
|||||||
/*
|
|
||||||
obs-websocket
|
|
||||||
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Similar example code can be found in ../../src/obs-websocket.cpp
|
|
||||||
// You can test that sample code by specifying -DPLUGIN_TESTS=TRUE
|
|
||||||
|
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
|
|
||||||
#include "../obs-websocket-api.h"
|
#include "../obs-websocket-api.h"
|
||||||
@ -45,24 +24,6 @@ void obs_module_post_load(void)
|
|||||||
|
|
||||||
if (!obs_websocket_vendor_register_request(vendor, "example_request", example_request_cb, NULL))
|
if (!obs_websocket_vendor_register_request(vendor, "example_request", example_request_cb, NULL))
|
||||||
blog(LOG_ERROR, "Failed to register `example_request` request with obs-websocket.");
|
blog(LOG_ERROR, "Failed to register `example_request` request with obs-websocket.");
|
||||||
|
|
||||||
uint api_version = obs_websocket_get_api_version();
|
|
||||||
if (api_version == 0) {
|
|
||||||
blog(LOG_ERROR, "Unable to fetch obs-websocket plugin API version.");
|
|
||||||
return;
|
|
||||||
} else if (api_version == 1) {
|
|
||||||
blog(LOG_WARNING, "Unsupported obs-websocket plugin API version for calling requests.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
|
|
||||||
if (!response) {
|
|
||||||
blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
|
|
||||||
response->status_code, response->comment, response->response_data);
|
|
||||||
obs_websocket_request_response_free(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void obs_module_unload(void)
|
void obs_module_unload(void)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
|
||||||
Copyright (C) 2020-2022 Kyle Manning <tt2468@gmail.com>
|
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -22,8 +22,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <obs.h>
|
#include <obs.h>
|
||||||
|
|
||||||
#define OBS_WEBSOCKET_API_VERSION 2
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -31,22 +29,12 @@ extern "C" {
|
|||||||
typedef void* obs_websocket_vendor;
|
typedef void* obs_websocket_vendor;
|
||||||
typedef void (*obs_websocket_request_callback_function)(obs_data_t*, obs_data_t*, void*);
|
typedef void (*obs_websocket_request_callback_function)(obs_data_t*, obs_data_t*, void*);
|
||||||
|
|
||||||
struct obs_websocket_request_response {
|
|
||||||
unsigned int status_code;
|
|
||||||
char *comment;
|
|
||||||
char *response_data; // JSON string, because obs_data_t* only supports array<object>, so conversions would break API.
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ==================== INTERNAL DEFINITIONS ==================== */
|
|
||||||
|
|
||||||
struct obs_websocket_request_callback {
|
struct obs_websocket_request_callback {
|
||||||
obs_websocket_request_callback_function callback;
|
obs_websocket_request_callback_function callback;
|
||||||
void *priv_data;
|
void *priv_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline proc_handler_t *_ph;
|
inline proc_handler_t *ph;
|
||||||
|
|
||||||
/* ==================== INTERNAL API FUNCTIONS ==================== */
|
|
||||||
|
|
||||||
static inline proc_handler_t *obs_websocket_get_ph(void)
|
static inline proc_handler_t *obs_websocket_get_ph(void)
|
||||||
{
|
{
|
||||||
@ -62,98 +50,30 @@ static inline proc_handler_t *obs_websocket_get_ph(void)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool obs_websocket_ensure_ph(void)
|
static inline bool obs_websocket_run_simple_proc(obs_websocket_vendor vendor, const char *proc_name, calldata_t *cd)
|
||||||
{
|
{
|
||||||
if (!_ph)
|
if (!ph || !vendor || !proc_name || !strlen(proc_name) || !cd)
|
||||||
_ph = obs_websocket_get_ph();
|
|
||||||
return _ph != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor, const char *proc_name, calldata_t *cd)
|
|
||||||
{
|
|
||||||
if (!obs_websocket_ensure_ph())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!vendor || !proc_name || !strlen(proc_name) || !cd)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
calldata_set_ptr(cd, "vendor", vendor);
|
calldata_set_ptr(cd, "vendor", vendor);
|
||||||
|
|
||||||
proc_handler_call(_ph, proc_name, cd);
|
proc_handler_call(ph, proc_name, cd);
|
||||||
return calldata_bool(cd, "success");
|
return calldata_bool(cd, "success");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== GENERAL API FUNCTIONS ==================== */
|
// ALWAYS CALL VIA `obs_module_post_load()` CALLBACK!
|
||||||
|
|
||||||
// Gets the API version built with the obs-websocket plugin
|
|
||||||
static inline unsigned int obs_websocket_get_api_version(void)
|
|
||||||
{
|
|
||||||
if (!obs_websocket_ensure_ph())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
calldata_t cd = {0};
|
|
||||||
|
|
||||||
if (!proc_handler_call(_ph, "get_api_version", &cd))
|
|
||||||
return 1; // API v1 does not include get_api_version
|
|
||||||
|
|
||||||
unsigned int ret = calldata_int(&cd, "version");
|
|
||||||
|
|
||||||
calldata_free(&cd);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
|
|
||||||
static inline obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data = NULL)
|
|
||||||
{
|
|
||||||
if (!obs_websocket_ensure_ph())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
const char *request_data_string = NULL;
|
|
||||||
if (request_data)
|
|
||||||
request_data_string = obs_data_get_json(request_data);
|
|
||||||
|
|
||||||
calldata_t cd = {0};
|
|
||||||
|
|
||||||
calldata_set_string(&cd, "request_type", request_type);
|
|
||||||
calldata_set_string(&cd, "request_data", request_data_string);
|
|
||||||
|
|
||||||
proc_handler_call(_ph, "call_request", &cd);
|
|
||||||
|
|
||||||
auto ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
|
|
||||||
|
|
||||||
calldata_free(&cd);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free a request response object returned by `obs_websocket_call_request()`
|
|
||||||
static inline void obs_websocket_request_response_free(struct obs_websocket_request_response *response)
|
|
||||||
{
|
|
||||||
if (!response)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (response->comment)
|
|
||||||
bfree(response->comment);
|
|
||||||
if (response->response_data)
|
|
||||||
bfree(response->response_data);
|
|
||||||
bfree(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================== VENDOR API FUNCTIONS ==================== */
|
|
||||||
|
|
||||||
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
|
|
||||||
// Registers a new "vendor" (Example: obs-ndi)
|
// Registers a new "vendor" (Example: obs-ndi)
|
||||||
static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name)
|
static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name)
|
||||||
{
|
{
|
||||||
if (!obs_websocket_ensure_ph())
|
ph = obs_websocket_get_ph();
|
||||||
|
if (!ph)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
|
|
||||||
calldata_set_string(&cd, "name", vendor_name);
|
calldata_set_string(&cd, "name", vendor_name);
|
||||||
|
|
||||||
proc_handler_call(_ph, "vendor_register", &cd);
|
proc_handler_call(ph, "vendor_register", &cd);
|
||||||
obs_websocket_vendor ret = calldata_ptr(&cd, "vendor");
|
obs_websocket_vendor ret = calldata_ptr(&cd, "vendor");
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
@ -161,8 +81,7 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Registers a new request for a vendor
|
// Registers a new request for a vendor
|
||||||
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type,
|
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type, obs_websocket_request_callback_function request_callback, void* priv_data)
|
||||||
obs_websocket_request_callback_function request_callback, void *priv_data)
|
|
||||||
{
|
{
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
|
|
||||||
@ -173,7 +92,7 @@ static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor ve
|
|||||||
calldata_set_string(&cd, "type", request_type);
|
calldata_set_string(&cd, "type", request_type);
|
||||||
calldata_set_ptr(&cd, "callback", &cb);
|
calldata_set_ptr(&cd, "callback", &cb);
|
||||||
|
|
||||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_register", &cd);
|
bool success = obs_websocket_run_simple_proc(vendor, "vendor_request_register", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@ -186,7 +105,7 @@ static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor
|
|||||||
|
|
||||||
calldata_set_string(&cd, "type", request_type);
|
calldata_set_string(&cd, "type", request_type);
|
||||||
|
|
||||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_unregister", &cd);
|
bool success = obs_websocket_run_simple_proc(vendor, "vendor_request_unregister", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@ -201,14 +120,12 @@ static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor,
|
|||||||
calldata_set_string(&cd, "type", event_name);
|
calldata_set_string(&cd, "type", event_name);
|
||||||
calldata_set_ptr(&cd, "data", (void*)event_data);
|
calldata_set_ptr(&cd, "data", (void*)event_data);
|
||||||
|
|
||||||
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd);
|
bool success = obs_websocket_run_simple_proc(vendor, "vendor_event_emit", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== END API FUNCTIONS ==================== */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -33,17 +33,15 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define PARAM_PASSWORD "ServerPassword"
|
#define PARAM_PASSWORD "ServerPassword"
|
||||||
|
|
||||||
#define CMDLINE_WEBSOCKET_PORT "websocket_port"
|
#define CMDLINE_WEBSOCKET_PORT "websocket_port"
|
||||||
#define CMDLINE_WEBSOCKET_IPV4_ONLY "websocket_ipv4_only"
|
|
||||||
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
|
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
|
||||||
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
|
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
|
||||||
|
|
||||||
Config::Config()
|
Config::Config() :
|
||||||
: PortOverridden(false),
|
PortOverridden(false),
|
||||||
PasswordOverridden(false),
|
PasswordOverridden(false),
|
||||||
FirstLoad(true),
|
FirstLoad(true),
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4455),
|
ServerPort(4444),
|
||||||
Ipv4Only(false),
|
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(false),
|
AlertsEnabled(false),
|
||||||
AuthRequired(true),
|
AuthRequired(true),
|
||||||
@ -95,12 +93,6 @@ void Config::Load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process `--websocket_ipv4_only` override
|
|
||||||
if (Utils::Platform::GetCommandLineFlagSet(CMDLINE_WEBSOCKET_IPV4_ONLY)) {
|
|
||||||
blog(LOG_INFO, "[Config::Load] --websocket_ipv4_only passed. Binding only to IPv4 interfaces.");
|
|
||||||
Ipv4Only = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process `--websocket_password` override
|
// Process `--websocket_password` override
|
||||||
QString passwordArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PASSWORD);
|
QString passwordArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PASSWORD);
|
||||||
if (passwordArgument != "") {
|
if (passwordArgument != "") {
|
||||||
|
@ -38,7 +38,6 @@ struct Config {
|
|||||||
std::atomic<bool> FirstLoad;
|
std::atomic<bool> FirstLoad;
|
||||||
std::atomic<bool> ServerEnabled;
|
std::atomic<bool> ServerEnabled;
|
||||||
std::atomic<uint16_t> ServerPort;
|
std::atomic<uint16_t> ServerPort;
|
||||||
std::atomic<bool> Ipv4Only;
|
|
||||||
std::atomic<bool> DebugEnabled;
|
std::atomic<bool> DebugEnabled;
|
||||||
std::atomic<bool> AlertsEnabled;
|
std::atomic<bool> AlertsEnabled;
|
||||||
std::atomic<bool> AuthRequired;
|
std::atomic<bool> AuthRequired;
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
#include "WebSocketApi.h"
|
#include "WebSocketApi.h"
|
||||||
#include "requesthandler/RequestHandler.h"
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "utils/Json.h"
|
|
||||||
|
|
||||||
#define RETURN_STATUS(status) \
|
#define RETURN_STATUS(status) { calldata_set_bool(cd, "success", status); return; }
|
||||||
{ \
|
|
||||||
calldata_set_bool(cd, "success", status); \
|
|
||||||
return; \
|
|
||||||
}
|
|
||||||
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
||||||
#define RETURN_FAILURE() RETURN_STATUS(false);
|
#define RETURN_FAILURE() RETURN_STATUS(false);
|
||||||
|
|
||||||
@ -28,16 +22,10 @@ WebSocketApi::WebSocketApi()
|
|||||||
|
|
||||||
_procHandler = proc_handler_create();
|
_procHandler = proc_handler_create();
|
||||||
|
|
||||||
proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
|
|
||||||
proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)",
|
|
||||||
&call_request, nullptr);
|
|
||||||
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
|
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
|
||||||
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)",
|
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)", &vendor_request_register_cb, this);
|
||||||
&vendor_request_register_cb, this);
|
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)", &vendor_request_unregister_cb, this);
|
||||||
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)",
|
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb, this);
|
||||||
&vendor_request_unregister_cb, this);
|
|
||||||
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb,
|
|
||||||
this);
|
|
||||||
|
|
||||||
proc_handler_t *ph = obs_get_proc_handler();
|
proc_handler_t *ph = obs_get_proc_handler();
|
||||||
assert(ph != NULL);
|
assert(ph != NULL);
|
||||||
@ -66,8 +54,7 @@ void WebSocketApi::SetEventCallback(EventCallback cb)
|
|||||||
_eventCallback = cb;
|
_eventCallback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType,
|
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData)
|
||||||
obs_data_t *requestData, obs_data_t *responseData)
|
|
||||||
{
|
{
|
||||||
std::shared_lock l(_mutex);
|
std::shared_lock l(_mutex);
|
||||||
|
|
||||||
@ -101,49 +88,6 @@ void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd)
|
|||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketApi::get_api_version(void *, calldata_t *cd)
|
|
||||||
{
|
|
||||||
calldata_set_int(cd, "version", OBS_WEBSOCKET_API_VERSION);
|
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketApi::call_request(void *, calldata_t *cd)
|
|
||||||
{
|
|
||||||
const char *request_type = calldata_string(cd, "request_type");
|
|
||||||
const char *request_data = calldata_string(cd, "request_data");
|
|
||||||
|
|
||||||
if (!request_type)
|
|
||||||
RETURN_FAILURE();
|
|
||||||
|
|
||||||
auto response = static_cast<obs_websocket_request_response *>(bzalloc(sizeof(struct obs_websocket_request_response)));
|
|
||||||
if (!response)
|
|
||||||
RETURN_FAILURE();
|
|
||||||
|
|
||||||
json requestData;
|
|
||||||
if (request_data)
|
|
||||||
requestData = json::parse(request_data);
|
|
||||||
|
|
||||||
RequestHandler requestHandler;
|
|
||||||
Request request(request_type, requestData);
|
|
||||||
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
|
||||||
|
|
||||||
response->status_code = (unsigned int)requestResult.StatusCode;
|
|
||||||
if (!requestResult.Comment.empty())
|
|
||||||
response->comment = bstrdup(requestResult.Comment.c_str());
|
|
||||||
if (requestResult.ResponseData.is_object()) {
|
|
||||||
std::string responseData = requestResult.ResponseData.dump();
|
|
||||||
response->response_data = bstrdup(responseData.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
calldata_set_ptr(cd, "response", response);
|
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type,
|
|
||||||
response->status_code);
|
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<WebSocketApi*>(priv_data);
|
auto c = static_cast<WebSocketApi*>(priv_data);
|
||||||
@ -158,8 +102,7 @@ void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
|||||||
std::unique_lock l(c->_mutex);
|
std::unique_lock l(c->_mutex);
|
||||||
|
|
||||||
if (c->_vendors.count(vendorName)) {
|
if (c->_vendors.count(vendorName)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
|
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.", vendorName);
|
||||||
vendorName);
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,17 +126,13 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
|
|||||||
|
|
||||||
const char *requestType;
|
const char *requestType;
|
||||||
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.", v->_name.c_str());
|
||||||
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.",
|
|
||||||
v->_name.c_str());
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *voidCallback;
|
void *voidCallback;
|
||||||
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.", v->_name.c_str());
|
||||||
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.",
|
|
||||||
v->_name.c_str());
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,16 +141,13 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
|
|||||||
std::unique_lock l(v->_mutex);
|
std::unique_lock l(v->_mutex);
|
||||||
|
|
||||||
if (v->_requests.count(requestType)) {
|
if (v->_requests.count(requestType)) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.", v->_name.c_str(), requestType);
|
||||||
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.",
|
|
||||||
v->_name.c_str(), requestType);
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
v->_requests[requestType] = *cb;
|
v->_requests[requestType] = *cb;
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s",
|
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s", v->_name.c_str(), requestType);
|
||||||
v->_name.c_str(), requestType);
|
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -224,25 +160,20 @@ void WebSocketApi::vendor_request_unregister_cb(void *, calldata_t *cd)
|
|||||||
|
|
||||||
const char *requestType;
|
const char *requestType;
|
||||||
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str());
|
||||||
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.",
|
|
||||||
v->_name.c_str());
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock l(v->_mutex);
|
std::unique_lock l(v->_mutex);
|
||||||
|
|
||||||
if (!v->_requests.count(requestType)) {
|
if (!v->_requests.count(requestType)) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.", v->_name.c_str(), requestType);
|
||||||
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.",
|
|
||||||
v->_name.c_str(), requestType);
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
v->_requests.erase(requestType);
|
v->_requests.erase(requestType);
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s",
|
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s", v->_name.c_str(), requestType);
|
||||||
v->_name.c_str(), requestType);
|
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -257,15 +188,13 @@ void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
|
|||||||
|
|
||||||
const char *eventType;
|
const char *eventType;
|
||||||
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
|
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
|
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str());
|
||||||
v->_name.c_str());
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *voidEventData;
|
void *voidEventData;
|
||||||
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
|
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.", v->_name.c_str());
|
||||||
v->_name.c_str());
|
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +30,9 @@ public:
|
|||||||
|
|
||||||
void SetEventCallback(EventCallback cb);
|
void SetEventCallback(EventCallback cb);
|
||||||
|
|
||||||
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData,
|
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData, obs_data_t *responseData);
|
||||||
obs_data_t *responseData);
|
|
||||||
|
|
||||||
static void get_ph_cb(void *priv_data, calldata_t *cd);
|
static void get_ph_cb(void *priv_data, calldata_t *cd);
|
||||||
static void get_api_version(void *, calldata_t *cd);
|
|
||||||
static void call_request(void *, calldata_t *cd);
|
|
||||||
static void vendor_register_cb(void *priv_data, calldata_t *cd);
|
static void vendor_register_cb(void *priv_data, calldata_t *cd);
|
||||||
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
|
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
|
||||||
static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
|
static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
|
||||||
|
@ -19,8 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
EventHandler::EventHandler()
|
EventHandler::EventHandler() :
|
||||||
: _obsLoaded(false),
|
_obsLoaded(false),
|
||||||
_inputVolumeMetersRef(0),
|
_inputVolumeMetersRef(0),
|
||||||
_inputActiveStateChangedRef(0),
|
_inputActiveStateChangedRef(0),
|
||||||
_inputShowStateChangedRef(0),
|
_inputShowStateChangedRef(0),
|
||||||
@ -80,8 +80,7 @@ void EventHandler::ProcessSubscription(uint64_t eventSubscriptions)
|
|||||||
if (_inputVolumeMetersHandler)
|
if (_inputVolumeMetersHandler)
|
||||||
blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
|
blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
|
||||||
else
|
else
|
||||||
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(
|
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
|
||||||
std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
|
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
|
||||||
@ -116,7 +115,6 @@ void EventHandler::BroadcastEvent(uint64_t requiredIntent, std::string eventType
|
|||||||
_broadcastCallback(requiredIntent, eventType, eventData, rpcVersion);
|
_broadcastCallback(requiredIntent, eventType, eventData, rpcVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect source signals for Inputs, Scenes, and Transitions. Filters are automatically connected.
|
|
||||||
void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inputs and scenes
|
void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inputs and scenes
|
||||||
{
|
{
|
||||||
if (!source || obs_source_removed(source))
|
if (!source || obs_source_removed(source))
|
||||||
@ -130,17 +128,17 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
|
|||||||
obs_source_type sourceType = obs_source_get_type(source);
|
obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
|
|
||||||
signal_handler_connect(sh, "activate", HandleInputActiveStateChanged, this);
|
signal_handler_connect(sh, "activate", HandleInputActiveStateChanged, this);
|
||||||
signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this);
|
signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this);
|
||||||
signal_handler_connect(sh, "show", HandleInputShowStateChanged, this);
|
signal_handler_connect(sh, "show", HandleInputShowStateChanged, this);
|
||||||
signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this);
|
signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this);
|
||||||
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this);
|
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this);
|
||||||
signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this);
|
signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this);
|
||||||
signal_handler_connect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
|
|
||||||
signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
|
signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
|
||||||
signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
|
signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
|
||||||
signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
|
//signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
|
||||||
|
|
||||||
|
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
|
||||||
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
||||||
signal_handler_connect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
|
signal_handler_connect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
|
||||||
signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
|
signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
|
||||||
@ -158,37 +156,10 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
|
|||||||
signal_handler_connect(sh, "reorder", HandleSceneItemListReindexed, this);
|
signal_handler_connect(sh, "reorder", HandleSceneItemListReindexed, this);
|
||||||
signal_handler_connect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
|
signal_handler_connect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
|
||||||
signal_handler_connect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
|
signal_handler_connect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
|
||||||
signal_handler_connect(sh, "item_select", HandleSceneItemSelected, this);
|
|
||||||
signal_handler_connect(sh, "item_transform", HandleSceneItemTransformChanged, this);
|
signal_handler_connect(sh, "item_transform", HandleSceneItemTransformChanged, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenes and Inputs
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) {
|
|
||||||
signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
|
|
||||||
signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, this);
|
|
||||||
signal_handler_connect(sh, "filter_remove", FilterRemoveMultiHandler, this);
|
|
||||||
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
eventHandler->ConnectSourceSignals(filter);
|
|
||||||
};
|
|
||||||
obs_source_enum_filters(source, enumFilters, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transitions
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_TRANSITION) {
|
|
||||||
signal_handler_connect(sh, "transition_start", HandleSceneTransitionStarted, this);
|
|
||||||
signal_handler_connect(sh, "transition_stop", HandleSceneTransitionEnded, this);
|
|
||||||
signal_handler_connect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_FILTER) {
|
|
||||||
signal_handler_connect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
|
|
||||||
signal_handler_connect(sh, "rename", HandleSourceFilterNameChanged, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect source signals for Inputs, Scenes, and Transitions. Filters are automatically disconnected.
|
|
||||||
void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
||||||
{
|
{
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -196,20 +167,16 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
|||||||
|
|
||||||
signal_handler_t* sh = obs_source_get_signal_handler(source);
|
signal_handler_t* sh = obs_source_get_signal_handler(source);
|
||||||
|
|
||||||
obs_source_type sourceType = obs_source_get_type(source);
|
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
|
|
||||||
signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this);
|
signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this);
|
signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this);
|
signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
|
signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
|
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
|
signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
|
||||||
signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
|
|
||||||
signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
|
signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
|
||||||
signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
|
signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
|
||||||
signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
|
//signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
|
||||||
signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
||||||
signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
|
signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
|
||||||
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
|
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
|
||||||
@ -218,99 +185,52 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
|||||||
signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this);
|
signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this);
|
||||||
signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this);
|
signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this);
|
||||||
signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this);
|
signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this);
|
||||||
}
|
|
||||||
|
|
||||||
// Scenes
|
// Scenes
|
||||||
if (sourceType == OBS_SOURCE_TYPE_SCENE) {
|
|
||||||
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
|
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
|
||||||
signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this);
|
signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this);
|
||||||
signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this);
|
signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this);
|
||||||
signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
|
signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
|
signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
|
||||||
signal_handler_disconnect(sh, "item_select", HandleSceneItemSelected, this);
|
|
||||||
signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this);
|
signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inputs and Scenes
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) {
|
|
||||||
signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
|
|
||||||
signal_handler_disconnect(sh, "filter_add", FilterAddMultiHandler, this);
|
|
||||||
signal_handler_disconnect(sh, "filter_remove", FilterRemoveMultiHandler, this);
|
|
||||||
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
eventHandler->DisconnectSourceSignals(filter);
|
|
||||||
};
|
|
||||||
obs_source_enum_filters(source, enumFilters, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transitions
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_TRANSITION) {
|
|
||||||
signal_handler_disconnect(sh, "transition_start", HandleSceneTransitionStarted, this);
|
|
||||||
signal_handler_disconnect(sh, "transition_stop", HandleSceneTransitionEnded, this);
|
|
||||||
signal_handler_disconnect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_FILTER) {
|
|
||||||
signal_handler_disconnect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
|
|
||||||
signal_handler_disconnect(sh, "rename", HandleSourceFilterNameChanged, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data)
|
void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(private_data);
|
auto eventHandler = reinterpret_cast<EventHandler*>(private_data);
|
||||||
|
|
||||||
if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
// General
|
|
||||||
case OBS_FRONTEND_EVENT_FINISHED_LOADING:
|
|
||||||
blog_debug(
|
|
||||||
"[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
|
|
||||||
|
|
||||||
|
if (!eventHandler->_obsLoaded.load()) {
|
||||||
|
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
||||||
|
blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
|
||||||
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
|
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
|
||||||
eventHandler->_obsLoaded.store(true);
|
eventHandler->_obsLoaded.store(true);
|
||||||
|
|
||||||
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()`
|
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()`
|
||||||
// Enumerate inputs and connect each one
|
// Enumerate inputs and connect each one
|
||||||
{
|
obs_enum_sources([](void* param, obs_source_t* source) {
|
||||||
auto enumInputs = [](void *param, obs_source_t *source) {
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
eventHandler->ConnectSourceSignals(source);
|
eventHandler->ConnectSourceSignals(source);
|
||||||
return true;
|
return true;
|
||||||
};
|
}, private_data);
|
||||||
obs_enum_sources(enumInputs, private_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerate scenes and connect each one
|
// Enumerate scenes and connect each one
|
||||||
{
|
obs_enum_scenes([](void* param, obs_source_t* source) {
|
||||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
eventHandler->ConnectSourceSignals(source);
|
eventHandler->ConnectSourceSignals(source);
|
||||||
return true;
|
return true;
|
||||||
};
|
}, private_data);
|
||||||
obs_enum_scenes(enumScenes, private_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerate all scene transitions and connect each one
|
|
||||||
{
|
|
||||||
obs_frontend_source_list transitions = {};
|
|
||||||
obs_frontend_get_transitions(&transitions);
|
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
|
||||||
obs_source_t *transition = transitions.sources.array[i];
|
|
||||||
eventHandler->ConnectSourceSignals(transition);
|
|
||||||
}
|
|
||||||
obs_frontend_source_list_free(&transitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
||||||
|
|
||||||
if (eventHandler->_obsLoadedCallback)
|
if (eventHandler->_obsLoadedCallback)
|
||||||
eventHandler->_obsLoadedCallback();
|
eventHandler->_obsLoadedCallback();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
switch (event) {
|
||||||
|
// General
|
||||||
case OBS_FRONTEND_EVENT_EXIT:
|
case OBS_FRONTEND_EVENT_EXIT:
|
||||||
eventHandler->HandleExitStarted();
|
eventHandler->HandleExitStarted();
|
||||||
|
|
||||||
@ -320,35 +240,18 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
|
|
||||||
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()`
|
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()`
|
||||||
// Enumerate inputs and disconnect each one
|
// Enumerate inputs and disconnect each one
|
||||||
{
|
obs_enum_sources([](void* param, obs_source_t* source) {
|
||||||
auto enumInputs = [](void *param, obs_source_t *source) {
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
eventHandler->DisconnectSourceSignals(source);
|
eventHandler->DisconnectSourceSignals(source);
|
||||||
return true;
|
return true;
|
||||||
};
|
}, private_data);
|
||||||
obs_enum_sources(enumInputs, private_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerate scenes and disconnect each one
|
// Enumerate scenes and disconnect each one
|
||||||
{
|
obs_enum_scenes([](void* param, obs_source_t* source) {
|
||||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
eventHandler->DisconnectSourceSignals(source);
|
eventHandler->DisconnectSourceSignals(source);
|
||||||
return true;
|
return true;
|
||||||
};
|
}, private_data);
|
||||||
obs_enum_scenes(enumScenes, private_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerate all scene transitions and disconnect each one
|
|
||||||
{
|
|
||||||
obs_frontend_source_list transitions = {};
|
|
||||||
obs_frontend_get_transitions(&transitions);
|
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
|
||||||
obs_source_t *transition = transitions.sources.array[i];
|
|
||||||
eventHandler->DisconnectSourceSignals(transition);
|
|
||||||
}
|
|
||||||
obs_frontend_source_list_free(&transitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
||||||
|
|
||||||
@ -361,34 +264,18 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING: {
|
//case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
|
||||||
obs_frontend_source_list transitions = {};
|
// eventHandler->HandleCurrentSceneCollectionChanging();
|
||||||
obs_frontend_get_transitions(&transitions);
|
// break;
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
|
||||||
obs_source_t *transition = transitions.sources.array[i];
|
|
||||||
eventHandler->DisconnectSourceSignals(transition);
|
|
||||||
}
|
|
||||||
obs_frontend_source_list_free(&transitions);
|
|
||||||
}
|
|
||||||
eventHandler->HandleCurrentSceneCollectionChanging();
|
|
||||||
break;
|
|
||||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: {
|
|
||||||
obs_frontend_source_list transitions = {};
|
|
||||||
obs_frontend_get_transitions(&transitions);
|
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
|
||||||
obs_source_t *transition = transitions.sources.array[i];
|
|
||||||
eventHandler->ConnectSourceSignals(transition);
|
|
||||||
}
|
|
||||||
obs_frontend_source_list_free(&transitions);
|
|
||||||
}
|
|
||||||
eventHandler->HandleCurrentSceneCollectionChanged();
|
eventHandler->HandleCurrentSceneCollectionChanged();
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
|
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
|
||||||
eventHandler->HandleSceneCollectionListChanged();
|
eventHandler->HandleSceneCollectionListChanged();
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
|
//case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
|
||||||
eventHandler->HandleCurrentProfileChanging();
|
// eventHandler->HandleCurrentProfileChanging();
|
||||||
break;
|
// break;
|
||||||
case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
|
case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
|
||||||
eventHandler->HandleCurrentProfileChanged();
|
eventHandler->HandleCurrentProfileChanged();
|
||||||
break;
|
break;
|
||||||
@ -409,19 +296,10 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
|
|
||||||
// Transitions
|
// Transitions
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
|
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
|
||||||
eventHandler->HandleCurrentSceneTransitionChanged();
|
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: {
|
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED:
|
||||||
obs_frontend_source_list transitions = {};
|
break;
|
||||||
obs_frontend_get_transitions(&transitions);
|
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
|
||||||
obs_source_t *transition = transitions.sources.array[i];
|
|
||||||
eventHandler->ConnectSourceSignals(transition);
|
|
||||||
}
|
|
||||||
obs_frontend_source_list_free(&transitions);
|
|
||||||
} break;
|
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
|
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
|
||||||
eventHandler->HandleCurrentSceneTransitionDurationChanged();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
@ -485,7 +363,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
// Only called for creation of a public source
|
// Only called for creation of a public source
|
||||||
void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
// Don't react to signals until OBS has finished loading
|
// Don't react to signals until OBS has finished loading
|
||||||
if (!eventHandler->_obsLoaded.load())
|
if (!eventHandler->_obsLoaded.load())
|
||||||
@ -509,11 +387,10 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only called for destruction of a public sourcs
|
// Only called for destruction of a public source
|
||||||
// Used as a fallback if an input/scene is not explicitly removed
|
|
||||||
void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
// We can't use any smart types here because releasing the source will cause infinite recursion
|
// We can't use any smart types here because releasing the source will cause infinite recursion
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
@ -529,25 +406,19 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
switch (obs_source_get_type(source)) {
|
switch (obs_source_get_type(source)) {
|
||||||
case OBS_SOURCE_TYPE_INPUT:
|
case OBS_SOURCE_TYPE_INPUT:
|
||||||
// Only emit removed if the input has not already been removed. This is the case when removing the last scene item of an input.
|
// We have to call `InputRemoved` with source_destroy because source_removed is not called when an input's last scene item is removed
|
||||||
if (!obs_source_removed(source))
|
|
||||||
eventHandler->HandleInputRemoved(source);
|
eventHandler->HandleInputRemoved(source);
|
||||||
break;
|
break;
|
||||||
case OBS_SOURCE_TYPE_SCENE:
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
// Only emit removed if the scene has not already been removed.
|
|
||||||
if (!obs_source_removed(source))
|
|
||||||
eventHandler->HandleSceneRemoved(source);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We prefer remove signals over destroy signals because they are more time-accurate.
|
|
||||||
// For example, if an input is "removed" but there is a dangling ref, you still want to know that it shouldn't exist, but it's not guaranteed to be destroyed.
|
|
||||||
void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_obsLoaded.load())
|
if (!eventHandler->_obsLoaded.load())
|
||||||
return;
|
return;
|
||||||
@ -558,9 +429,9 @@ void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
switch (obs_source_get_type(source)) {
|
switch (obs_source_get_type(source)) {
|
||||||
case OBS_SOURCE_TYPE_INPUT:
|
case OBS_SOURCE_TYPE_INPUT:
|
||||||
eventHandler->HandleInputRemoved(source);
|
|
||||||
break;
|
break;
|
||||||
case OBS_SOURCE_TYPE_SCENE:
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
|
// Scenes emit the `removed` signal when they are removed from OBS, as expected
|
||||||
eventHandler->HandleSceneRemoved(source);
|
eventHandler->HandleSceneRemoved(source);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -570,7 +441,7 @@ void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_obsLoaded.load())
|
if (!eventHandler->_obsLoaded.load())
|
||||||
return;
|
return;
|
||||||
@ -588,6 +459,8 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
|
|||||||
case OBS_SOURCE_TYPE_INPUT:
|
case OBS_SOURCE_TYPE_INPUT:
|
||||||
eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName);
|
eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName);
|
||||||
break;
|
break;
|
||||||
|
case OBS_SOURCE_TYPE_FILTER:
|
||||||
|
break;
|
||||||
case OBS_SOURCE_TYPE_TRANSITION:
|
case OBS_SOURCE_TYPE_TRANSITION:
|
||||||
break;
|
break;
|
||||||
case OBS_SOURCE_TYPE_SCENE:
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
|
@ -29,7 +29,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Obs_VolumeMeter.h"
|
#include "../utils/Obs_VolumeMeter.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class EventHandler {
|
class EventHandler
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
EventHandler();
|
EventHandler();
|
||||||
~EventHandler();
|
~EventHandler();
|
||||||
@ -76,6 +77,7 @@ private:
|
|||||||
static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
|
||||||
|
|
||||||
|
|
||||||
// General
|
// General
|
||||||
void HandleExitStarted();
|
void HandleExitStarted();
|
||||||
void HandleStudioModeStateChanged(bool enabled);
|
void HandleStudioModeStateChanged(bool enabled);
|
||||||
@ -105,27 +107,10 @@ private:
|
|||||||
static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleInputAudioBalanceChanged(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
|
||||||
|
|
||||||
// Transitions
|
|
||||||
void HandleCurrentSceneTransitionChanged();
|
|
||||||
void HandleCurrentSceneTransitionDurationChanged();
|
|
||||||
static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleSceneTransitionVideoEnded(void *param, calldata_t *data); // Direct callback
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void FilterRemoveMultiHandler(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback
|
|
||||||
void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter);
|
|
||||||
void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter);
|
|
||||||
static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
|
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
void HandleStreamStateChanged(ObsOutputState state);
|
void HandleStreamStateChanged(ObsOutputState state);
|
||||||
void HandleRecordStateChanged(ObsOutputState state);
|
void HandleRecordStateChanged(ObsOutputState state);
|
||||||
@ -139,7 +124,6 @@ private:
|
|||||||
static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback
|
static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleSceneItemSelected(void *param, calldata_t *data); // Direct callback
|
|
||||||
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
|
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
|
||||||
|
|
||||||
// Media Inputs
|
// Media Inputs
|
||||||
|
@ -18,184 +18,3 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
|
|
||||||
|
|
||||||
if (!(source && filter))
|
|
||||||
return;
|
|
||||||
|
|
||||||
eventHandler->ConnectSourceSignals(filter);
|
|
||||||
|
|
||||||
eventHandler->HandleSourceFilterCreated(source, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
|
|
||||||
|
|
||||||
if (!(source && filter))
|
|
||||||
return;
|
|
||||||
|
|
||||||
eventHandler->DisconnectSourceSignals(filter);
|
|
||||||
|
|
||||||
eventHandler->HandleSourceFilterRemoved(source, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A source's filter list has been reindexed.
|
|
||||||
*
|
|
||||||
* @dataField sourceName | String | Name of the source
|
|
||||||
* @dataField filters | Array<Object> | Array of filter objects
|
|
||||||
*
|
|
||||||
* @eventType SourceFilterListReindexed
|
|
||||||
* @eventSubscription Filters
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["sourceName"] = obs_source_get_name(source);
|
|
||||||
eventData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterListReindexed", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A filter has been added to a source.
|
|
||||||
*
|
|
||||||
* @dataField sourceName | String | Name of the source the filter was added to
|
|
||||||
* @dataField filterName | String | Name of the filter
|
|
||||||
* @dataField filterKind | String | The kind of the filter
|
|
||||||
* @dataField filterIndex | Number | Index position of the filter
|
|
||||||
* @dataField filterSettings | Object | The settings configured to the filter when it was created
|
|
||||||
* @dataField defaultFilterSettings | Object | The default settings for the filter
|
|
||||||
*
|
|
||||||
* @eventType SourceFilterCreated
|
|
||||||
* @eventSubscription Filters
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter)
|
|
||||||
{
|
|
||||||
std::string filterKind = obs_source_get_id(filter);
|
|
||||||
OBSDataAutoRelease filterSettings = obs_source_get_settings(filter);
|
|
||||||
OBSDataAutoRelease defaultFilterSettings = obs_get_source_defaults(filterKind.c_str());
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["sourceName"] = obs_source_get_name(source);
|
|
||||||
eventData["filterName"] = obs_source_get_name(filter);
|
|
||||||
eventData["filterKind"] = filterKind;
|
|
||||||
eventData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
|
|
||||||
eventData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
|
|
||||||
eventData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultFilterSettings, true);
|
|
||||||
BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A filter has been removed from a source.
|
|
||||||
*
|
|
||||||
* @dataField sourceName | String | Name of the source the filter was on
|
|
||||||
* @dataField filterName | String | Name of the filter
|
|
||||||
*
|
|
||||||
* @eventType SourceFilterRemoved
|
|
||||||
* @eventSubscription Filters
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter)
|
|
||||||
{
|
|
||||||
json eventData;
|
|
||||||
eventData["sourceName"] = obs_source_get_name(source);
|
|
||||||
eventData["filterName"] = obs_source_get_name(filter);
|
|
||||||
BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of a source filter has changed.
|
|
||||||
*
|
|
||||||
* @dataField sourceName | String | The source the filter is on
|
|
||||||
* @dataField oldFilterName | String | Old name of the filter
|
|
||||||
* @dataField filterName | String | New name of the filter
|
|
||||||
*
|
|
||||||
* @eventType SourceFilterNameChanged
|
|
||||||
* @eventSubscription Filters
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!filter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["sourceName"] = obs_source_get_name(obs_filter_get_parent(filter));
|
|
||||||
eventData["oldFilterName"] = calldata_string(data, "prev_name");
|
|
||||||
eventData["filterName"] = calldata_string(data, "new_name");
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterNameChanged", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A source filter's enable state has changed.
|
|
||||||
*
|
|
||||||
* @dataField sourceName | String | Name of the source the filter is on
|
|
||||||
* @dataField filterName | String | Name of the filter
|
|
||||||
* @dataField filterEnabled | Boolean | Whether the filter is enabled
|
|
||||||
*
|
|
||||||
* @eventType SourceFilterEnableStateChanged
|
|
||||||
* @eventSubscription Filters
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!filter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Not OBSSourceAutoRelease as get_parent doesn't increment refcount
|
|
||||||
obs_source_t *source = obs_filter_get_parent(filter);
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool filterEnabled = calldata_bool(data, "enabled");
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["sourceName"] = obs_source_get_name(source);
|
|
||||||
eventData["filterName"] = obs_source_get_name(filter);
|
|
||||||
eventData["filterEnabled"] = filterEnabled;
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterEnableStateChanged", eventData);
|
|
||||||
}
|
|
||||||
|
@ -111,7 +111,7 @@ void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputNa
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_inputActiveStateChangedRef.load())
|
if (!eventHandler->_inputActiveStateChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -147,7 +147,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_inputShowStateChangedRef.load())
|
if (!eventHandler->_inputShowStateChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -181,7 +181,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -213,7 +213,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -236,39 +236,6 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputVolumeChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputVolumeChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The audio balance value of an input has changed.
|
|
||||||
*
|
|
||||||
* @dataField inputName | String | Name of the affected input
|
|
||||||
* @dataField inputAudioBalance | Number | New audio balance value of the input
|
|
||||||
*
|
|
||||||
* @eventType InputAudioBalanceChanged
|
|
||||||
* @eventSubscription Inputs
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category inputs
|
|
||||||
* @api events
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
float inputAudioBalance = (float)calldata_float(data, "balance");
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["inputName"] = obs_source_get_name(source);
|
|
||||||
eventData["inputAudioBalance"] = inputAudioBalance;
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioBalanceChanged", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sync offset of an input has changed.
|
* The sync offset of an input has changed.
|
||||||
*
|
*
|
||||||
@ -285,7 +252,7 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -306,7 +273,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
|||||||
* The audio tracks of an input have changed.
|
* The audio tracks of an input have changed.
|
||||||
*
|
*
|
||||||
* @dataField inputName | String | Name of the input
|
* @dataField inputName | String | Name of the input
|
||||||
* @dataField inputAudioTracks | Object | Object of audio tracks along with their associated enable states
|
* @dataField inputAudioTracks | Array<Boolean> | Array of audio tracks along with their associated enable states
|
||||||
*
|
*
|
||||||
* @eventType InputAudioTracksChanged
|
* @eventType InputAudioTracksChanged
|
||||||
* @eventSubscription Inputs
|
* @eventSubscription Inputs
|
||||||
@ -318,7 +285,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -344,7 +311,6 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
|||||||
* The monitor type of an input has changed.
|
* The monitor type of an input has changed.
|
||||||
*
|
*
|
||||||
* Available types are:
|
* Available types are:
|
||||||
*
|
|
||||||
* - `OBS_MONITORING_TYPE_NONE`
|
* - `OBS_MONITORING_TYPE_NONE`
|
||||||
* - `OBS_MONITORING_TYPE_MONITOR_ONLY`
|
* - `OBS_MONITORING_TYPE_MONITOR_ONLY`
|
||||||
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
|
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
|
||||||
@ -362,7 +328,7 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -373,9 +339,11 @@ void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *d
|
|||||||
|
|
||||||
enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type");
|
enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type");
|
||||||
|
|
||||||
|
std::string monitorTypeString = Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["inputName"] = obs_source_get_name(source);
|
eventData["inputName"] = obs_source_get_name(source);
|
||||||
eventData["monitorType"] = monitorType;
|
eventData["monitorType"] = monitorTypeString;
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
#define CASE(x) \
|
#define CASE(x) case x: return #x;
|
||||||
case x: \
|
|
||||||
return #x;
|
|
||||||
|
|
||||||
std::string GetMediaInputActionString(ObsMediaInputAction action)
|
std::string GetMediaInputActionString(ObsMediaInputAction action) {
|
||||||
{
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
||||||
@ -38,7 +35,7 @@ std::string GetMediaInputActionString(ObsMediaInputAction action)
|
|||||||
|
|
||||||
void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -52,7 +49,7 @@ void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -66,7 +63,7 @@ void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -80,7 +77,7 @@ void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -94,7 +91,7 @@ void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -108,7 +105,7 @@ void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -135,7 +132,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
|
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -164,7 +161,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||||
if (!source)
|
if (!source)
|
||||||
|
@ -19,8 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
static bool GetOutputStateActive(ObsOutputState state)
|
static bool GetOutputStateActive(ObsOutputState state) {
|
||||||
{
|
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case OBS_WEBSOCKET_OUTPUT_STARTED:
|
case OBS_WEBSOCKET_OUTPUT_STARTED:
|
||||||
case OBS_WEBSOCKET_OUTPUT_RESUMED:
|
case OBS_WEBSOCKET_OUTPUT_RESUMED:
|
||||||
@ -53,7 +52,7 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = state;
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +61,6 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
|||||||
*
|
*
|
||||||
* @dataField outputActive | Boolean | Whether the output is active
|
* @dataField outputActive | Boolean | Whether the output is active
|
||||||
* @dataField outputState | String | The specific state of the output
|
* @dataField outputState | String | The specific state of the output
|
||||||
* @dataField outputPath | String | File name for the saved recording, if record stopped. `null` otherwise
|
|
||||||
*
|
*
|
||||||
* @eventType RecordStateChanged
|
* @eventType RecordStateChanged
|
||||||
* @eventSubscription Outputs
|
* @eventSubscription Outputs
|
||||||
@ -76,12 +74,7 @@ void EventHandler::HandleRecordStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = state;
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
if (state == OBS_WEBSOCKET_OUTPUT_STOPPED) {
|
|
||||||
eventData["outputPath"] = Utils::Obs::StringHelper::GetLastRecordFileName();
|
|
||||||
} else {
|
|
||||||
eventData["outputPath"] = nullptr;
|
|
||||||
}
|
|
||||||
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +96,7 @@ void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = state;
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +118,7 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = state;
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +138,6 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
|||||||
void EventHandler::HandleReplayBufferSaved()
|
void EventHandler::HandleReplayBufferSaved()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
|
eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -74,7 +74,7 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -107,7 +107,7 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -136,7 +136,7 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -160,7 +160,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
|
|||||||
*
|
*
|
||||||
* @dataField sceneName | String | Name of the scene the item is in
|
* @dataField sceneName | String | Name of the scene the item is in
|
||||||
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
||||||
* @dataField sceneItemLocked | Boolean | Whether the scene item is locked
|
* @dataField sceneItemEnabled | Boolean | Whether the scene item is locked
|
||||||
*
|
*
|
||||||
* @eventType SceneItemLockStateChanged
|
* @eventType SceneItemLockStateChanged
|
||||||
* @eventSubscription SceneItems
|
* @eventSubscription SceneItems
|
||||||
@ -172,7 +172,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -191,38 +191,6 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A scene item has been selected in the Ui.
|
|
||||||
*
|
|
||||||
* @dataField sceneName | String | Name of the scene the item is in
|
|
||||||
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
|
||||||
*
|
|
||||||
* @eventType SceneItemSelected
|
|
||||||
* @eventSubscription SceneItems
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category scene items
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
|
|
||||||
if (!scene)
|
|
||||||
return;
|
|
||||||
|
|
||||||
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
|
|
||||||
if (!sceneItem)
|
|
||||||
return;
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
|
||||||
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemSelected", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The transform/crop of a scene item has changed.
|
* The transform/crop of a scene item has changed.
|
||||||
*
|
*
|
||||||
@ -240,7 +208,7 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
|
|||||||
*/
|
*/
|
||||||
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_sceneItemTransformChangedRef.load())
|
if (!eventHandler->_sceneItemTransformChangedRef.load())
|
||||||
return;
|
return;
|
||||||
|
@ -18,130 +18,3 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* The current scene transition has changed.
|
|
||||||
*
|
|
||||||
* @dataField transitionName | String | Name of the new transition
|
|
||||||
*
|
|
||||||
* @eventType CurrentSceneTransitionChanged
|
|
||||||
* @eventSubscription Transitions
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleCurrentSceneTransitionChanged()
|
|
||||||
{
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["transitionName"] = obs_source_get_name(transition);
|
|
||||||
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionChanged", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current scene transition duration has changed.
|
|
||||||
*
|
|
||||||
* @dataField transitionDuration | Number | Transition duration in milliseconds
|
|
||||||
*
|
|
||||||
* @eventType CurrentSceneTransitionDurationChanged
|
|
||||||
* @eventSubscription Transitions
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleCurrentSceneTransitionDurationChanged()
|
|
||||||
{
|
|
||||||
json eventData;
|
|
||||||
eventData["transitionDuration"] = obs_frontend_get_transition_duration();
|
|
||||||
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionDurationChanged", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A scene transition has started.
|
|
||||||
*
|
|
||||||
* @dataField transitionName | String | Scene transition name
|
|
||||||
*
|
|
||||||
* @eventType SceneTransitionStarted
|
|
||||||
* @eventSubscription Transitions
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["transitionName"] = obs_source_get_name(source);
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionStarted", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A scene transition has completed fully.
|
|
||||||
*
|
|
||||||
* Note: Does not appear to trigger when the transition is interrupted by the user.
|
|
||||||
*
|
|
||||||
* @dataField transitionName | String | Scene transition name
|
|
||||||
*
|
|
||||||
* @eventType SceneTransitionEnded
|
|
||||||
* @eventSubscription Transitions
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["transitionName"] = obs_source_get_name(source);
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionEnded", eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A scene transition's video has completed fully.
|
|
||||||
*
|
|
||||||
* Useful for stinger transitions to tell when the video *actually* ends.
|
|
||||||
* `SceneTransitionEnded` only signifies the cut point, not the completion of transition playback.
|
|
||||||
*
|
|
||||||
* Note: Appears to be called by every transition, regardless of relevance.
|
|
||||||
*
|
|
||||||
* @dataField transitionName | String | Scene transition name
|
|
||||||
*
|
|
||||||
* @eventType SceneTransitionVideoEnded
|
|
||||||
* @eventSubscription Transitions
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api events
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data)
|
|
||||||
{
|
|
||||||
auto eventHandler = static_cast<EventHandler *>(param);
|
|
||||||
|
|
||||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
|
||||||
if (!source)
|
|
||||||
return;
|
|
||||||
|
|
||||||
json eventData;
|
|
||||||
eventData["transitionName"] = obs_source_get_name(source);
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionVideoEnded", eventData);
|
|
||||||
}
|
|
||||||
|
@ -157,15 +157,13 @@ namespace EventSubscription {
|
|||||||
* Helper to receive all non-high-volume events.
|
* Helper to receive all non-high-volume events.
|
||||||
*
|
*
|
||||||
* @enumIdentifier All
|
* @enumIdentifier All
|
||||||
* @enumValue (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors | Ui)
|
* @enumValue (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors)
|
||||||
* @enumType EventSubscription
|
* @enumType EventSubscription
|
||||||
* @rpcVersion -1
|
* @rpcVersion -1
|
||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors |
|
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Ui | Vendors),
|
||||||
Ui),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
||||||
*
|
*
|
||||||
|
@ -28,13 +28,18 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../Config.h"
|
#include "../Config.h"
|
||||||
#include "../utils/Platform.h"
|
#include "../utils/Platform.h"
|
||||||
|
|
||||||
ConnectInfo::ConnectInfo(QWidget *parent) : QDialog(parent, Qt::Dialog), ui(new Ui::ConnectInfo)
|
ConnectInfo::ConnectInfo(QWidget* parent) :
|
||||||
|
QDialog(parent, Qt::Dialog),
|
||||||
|
ui(new Ui::ConnectInfo)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->copyServerIpButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerIpButtonClicked);
|
connect(ui->copyServerIpButton, &QPushButton::clicked,
|
||||||
connect(ui->copyServerPortButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPortButtonClicked);
|
this, &ConnectInfo::CopyServerIpButtonClicked);
|
||||||
connect(ui->copyServerPasswordButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPasswordButtonClicked);
|
connect(ui->copyServerPortButton, &QPushButton::clicked,
|
||||||
|
this, &ConnectInfo::CopyServerPortButtonClicked);
|
||||||
|
connect(ui->copyServerPasswordButton, &QPushButton::clicked,
|
||||||
|
this, &ConnectInfo::CopyServerPasswordButtonClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectInfo::~ConnectInfo()
|
ConnectInfo::~ConnectInfo()
|
||||||
|
@ -25,7 +25,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_ConnectInfo.h"
|
#include "ui_ConnectInfo.h"
|
||||||
|
|
||||||
class ConnectInfo : public QDialog {
|
class ConnectInfo : public QDialog
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -37,8 +37,8 @@ QString GetToolTipIconHtml()
|
|||||||
return iconTemplate.arg(iconFile);
|
return iconTemplate.arg(iconFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget *parent)
|
SettingsDialog::SettingsDialog(QWidget* parent) :
|
||||||
: QDialog(parent, Qt::Dialog),
|
QDialog(parent, Qt::Dialog),
|
||||||
ui(new Ui::SettingsDialog),
|
ui(new Ui::SettingsDialog),
|
||||||
connectInfo(new ConnectInfo),
|
connectInfo(new ConnectInfo),
|
||||||
sessionTableTimer(new QTimer),
|
sessionTableTimer(new QTimer),
|
||||||
@ -54,13 +54,18 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
|||||||
// Set the appropriate tooltip icon for the theme
|
// Set the appropriate tooltip icon for the theme
|
||||||
ui->enableDebugLoggingToolTipLabel->setText(GetToolTipIconHtml());
|
ui->enableDebugLoggingToolTipLabel->setText(GetToolTipIconHtml());
|
||||||
|
|
||||||
connect(sessionTableTimer, &QTimer::timeout, this, &SettingsDialog::FillSessionTable);
|
connect(sessionTableTimer, &QTimer::timeout,
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &SettingsDialog::DialogButtonClicked);
|
this, &SettingsDialog::FillSessionTable);
|
||||||
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged, this,
|
connect(ui->buttonBox, &QDialogButtonBox::clicked,
|
||||||
&SettingsDialog::EnableAuthenticationCheckBoxChanged);
|
this, &SettingsDialog::DialogButtonClicked);
|
||||||
connect(ui->generatePasswordButton, &QPushButton::clicked, this, &SettingsDialog::GeneratePasswordButtonClicked);
|
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged,
|
||||||
connect(ui->showConnectInfoButton, &QPushButton::clicked, this, &SettingsDialog::ShowConnectInfoButtonClicked);
|
this, &SettingsDialog::EnableAuthenticationCheckBoxChanged);
|
||||||
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited, this, &SettingsDialog::PasswordEdited);
|
connect(ui->generatePasswordButton, &QPushButton::clicked,
|
||||||
|
this, &SettingsDialog::GeneratePasswordButtonClicked);
|
||||||
|
connect(ui->showConnectInfoButton, &QPushButton::clicked,
|
||||||
|
this, &SettingsDialog::ShowConnectInfoButtonClicked);
|
||||||
|
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited,
|
||||||
|
this, &SettingsDialog::PasswordEdited);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
@ -78,8 +83,18 @@ void SettingsDialog::showEvent(QShowEvent *)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf->PortOverridden)
|
ui->enableWebSocketServerCheckBox->setChecked(conf->ServerEnabled);
|
||||||
|
ui->enableSystemTrayAlertsCheckBox->setChecked(conf->AlertsEnabled);
|
||||||
|
ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled);
|
||||||
|
ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired);
|
||||||
|
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
|
||||||
|
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
|
||||||
|
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
|
||||||
|
ui->serverPortSpinBox->setValue(conf->ServerPort);
|
||||||
|
|
||||||
|
if (conf->PortOverridden) {
|
||||||
ui->serverPortSpinBox->setEnabled(false);
|
ui->serverPortSpinBox->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (conf->PasswordOverridden) {
|
if (conf->PasswordOverridden) {
|
||||||
ui->enableAuthenticationCheckBox->setEnabled(false);
|
ui->enableAuthenticationCheckBox->setEnabled(false);
|
||||||
@ -89,7 +104,7 @@ void SettingsDialog::showEvent(QShowEvent *)
|
|||||||
|
|
||||||
passwordManuallyEdited = false;
|
passwordManuallyEdited = false;
|
||||||
|
|
||||||
RefreshData();
|
FillSessionTable();
|
||||||
|
|
||||||
sessionTableTimer->start(1000);
|
sessionTableTimer->start(1000);
|
||||||
}
|
}
|
||||||
@ -110,27 +125,6 @@ void SettingsDialog::ToggleShowHide()
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::RefreshData()
|
|
||||||
{
|
|
||||||
auto conf = GetConfig();
|
|
||||||
if (!conf) {
|
|
||||||
blog(LOG_ERROR, "[SettingsDialog::RefreshData] Unable to retreive config!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->enableWebSocketServerCheckBox->setChecked(conf->ServerEnabled);
|
|
||||||
ui->enableSystemTrayAlertsCheckBox->setChecked(conf->AlertsEnabled);
|
|
||||||
ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled);
|
|
||||||
ui->serverPortSpinBox->setValue(conf->ServerPort);
|
|
||||||
ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired);
|
|
||||||
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
|
|
||||||
|
|
||||||
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
|
|
||||||
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
|
|
||||||
|
|
||||||
FillSessionTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialog::DialogButtonClicked(QAbstractButton *button)
|
void SettingsDialog::DialogButtonClicked(QAbstractButton *button)
|
||||||
{
|
{
|
||||||
if (button == ui->buttonBox->button(QDialogButtonBox::Ok)) {
|
if (button == ui->buttonBox->button(QDialogButtonBox::Ok)) {
|
||||||
@ -177,21 +171,19 @@ void SettingsDialog::SaveFormData()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsRestart =
|
bool needsRestart = (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
|
||||||
(conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
|
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text()) ||
|
||||||
(conf->ServerPort != ui->serverPortSpinBox->value()) ||
|
(conf->ServerPort != ui->serverPortSpinBox->value());
|
||||||
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text());
|
|
||||||
|
|
||||||
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
|
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
|
||||||
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
|
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
|
||||||
conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked();
|
conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked();
|
||||||
conf->ServerPort = ui->serverPortSpinBox->value();
|
|
||||||
conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked();
|
conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked();
|
||||||
conf->ServerPassword = ui->serverPasswordLineEdit->text();
|
conf->ServerPassword = ui->serverPasswordLineEdit->text();
|
||||||
|
conf->ServerPort = ui->serverPortSpinBox->value();
|
||||||
|
|
||||||
conf->Save();
|
conf->Save();
|
||||||
|
|
||||||
RefreshData();
|
|
||||||
connectInfo->RefreshData();
|
connectInfo->RefreshData();
|
||||||
|
|
||||||
if (needsRestart) {
|
if (needsRestart) {
|
||||||
@ -234,8 +226,7 @@ void SettingsDialog::FillSessionTable()
|
|||||||
QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
|
QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
|
||||||
ui->websocketSessionTable->setItem(i, 1, durationItem);
|
ui->websocketSessionTable->setItem(i, 1, durationItem);
|
||||||
|
|
||||||
QTableWidgetItem *statsItem =
|
QTableWidgetItem *statsItem = new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
|
||||||
new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
|
|
||||||
ui->websocketSessionTable->setItem(i, 2, statsItem);
|
ui->websocketSessionTable->setItem(i, 2, statsItem);
|
||||||
|
|
||||||
QLabel *identifiedLabel = new QLabel();
|
QLabel *identifiedLabel = new QLabel();
|
||||||
@ -255,7 +246,9 @@ void SettingsDialog::FillSessionTable()
|
|||||||
invalidateButtonLayout->setContentsMargins(0, 0, 0, 0);
|
invalidateButtonLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
invalidateButtonWidget->setLayout(invalidateButtonLayout);
|
invalidateButtonWidget->setLayout(invalidateButtonLayout);
|
||||||
ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget);
|
ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget);
|
||||||
connect(invalidateButton, &QPushButton::clicked, [=]() { webSocketServer->InvalidateSession(session.hdl); });
|
connect(invalidateButton, &QPushButton::clicked, [=]() {
|
||||||
|
webSocketServer->InvalidateSession(session.hdl);
|
||||||
|
});
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_SettingsDialog.h"
|
#include "ui_SettingsDialog.h"
|
||||||
|
|
||||||
class SettingsDialog : public QDialog {
|
class SettingsDialog : public QDialog
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -36,7 +37,6 @@ public:
|
|||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
void hideEvent(QHideEvent *event);
|
void hideEvent(QHideEvent *event);
|
||||||
void ToggleShowHide();
|
void ToggleShowHide();
|
||||||
void RefreshData();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void DialogButtonClicked(QAbstractButton *button);
|
void DialogButtonClicked(QAbstractButton *button);
|
||||||
|
@ -151,7 +151,7 @@
|
|||||||
<number>65534</number>
|
<number>65534</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>4455</number>
|
<number>4444</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -32,14 +32,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
||||||
OBS_MODULE_AUTHOR("OBSProject")
|
OBS_MODULE_AUTHOR("OBSProject")
|
||||||
const char *obs_module_name(void)
|
const char *obs_module_name(void) { return "obs-websocket"; }
|
||||||
{
|
const char *obs_module_description(void) { return obs_module_text("OBSWebSocket.Plugin.Description"); }
|
||||||
return "obs-websocket";
|
|
||||||
}
|
|
||||||
const char *obs_module_description(void)
|
|
||||||
{
|
|
||||||
return obs_module_text("OBSWebSocket.Plugin.Description");
|
|
||||||
}
|
|
||||||
|
|
||||||
os_cpu_usage_info_t* _cpuUsageInfo;
|
os_cpu_usage_info_t* _cpuUsageInfo;
|
||||||
ConfigPtr _config;
|
ConfigPtr _config;
|
||||||
@ -52,10 +46,8 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType, ob
|
|||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION,
|
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION, OBS_WEBSOCKET_RPC_VERSION);
|
||||||
OBS_WEBSOCKET_RPC_VERSION);
|
|
||||||
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
|
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
|
||||||
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
|
|
||||||
|
|
||||||
// Initialize the cpu stats
|
// Initialize the cpu stats
|
||||||
_cpuUsageInfo = os_cpu_usage_info_start();
|
_cpuUsageInfo = os_cpu_usage_info_start();
|
||||||
@ -76,7 +68,7 @@ bool obs_module_load(void)
|
|||||||
|
|
||||||
// Initialize the settings dialog
|
// Initialize the settings dialog
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
_settingsDialog = new SettingsDialog(mainWindow);
|
_settingsDialog = new SettingsDialog(mainWindow);
|
||||||
obs_frontend_pop_ui_translation();
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
@ -197,26 +189,12 @@ void obs_module_post_load()
|
|||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Post load started.");
|
blog(LOG_INFO, "[obs_module_post_load] Post load started.");
|
||||||
|
|
||||||
// Test plugin API version fetch
|
|
||||||
uint apiVersion = obs_websocket_get_api_version();
|
|
||||||
blog(LOG_INFO, "[obs_module_post_load] obs-websocket plugin API version: %u", apiVersion);
|
|
||||||
|
|
||||||
// Test calling obs-websocket requests
|
|
||||||
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
|
|
||||||
if (response) {
|
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
|
|
||||||
response->status_code, response->comment, response->response_data);
|
|
||||||
obs_websocket_request_response_free(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test vendor creation
|
|
||||||
auto vendor = obs_websocket_register_vendor("obs-websocket-test");
|
auto vendor = obs_websocket_register_vendor("obs-websocket-test");
|
||||||
if (!vendor) {
|
if (!vendor) {
|
||||||
blog(LOG_WARNING, "[obs_module_post_load] Failed to create vendor!");
|
blog(LOG_WARNING, "[obs_module_post_load] Failed to create vendor!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test vendor request registration
|
|
||||||
if (!obs_websocket_vendor_register_request(vendor, "TestRequest", test_vendor_request_cb, vendor)) {
|
if (!obs_websocket_vendor_register_request(vendor, "TestRequest", test_vendor_request_cb, vendor)) {
|
||||||
blog(LOG_WARNING, "[obs_module_post_load] Failed to register vendor request!");
|
blog(LOG_WARNING, "[obs_module_post_load] Failed to register vendor request!");
|
||||||
return;
|
return;
|
||||||
|
@ -21,7 +21,13 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma push_macro("strtoll")
|
||||||
|
#endif
|
||||||
#include <util/platform.h>
|
#include <util/platform.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma pop_macro("strtoll")
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "utils/Obs.h"
|
#include "utils/Obs.h"
|
||||||
#include "plugin-macros.generated.h"
|
#include "plugin-macros.generated.h"
|
||||||
|
@ -24,7 +24,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Compat.h"
|
#include "../utils/Compat.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
struct SerialFrameBatch {
|
struct SerialFrameBatch
|
||||||
|
{
|
||||||
RequestHandler &requestHandler;
|
RequestHandler &requestHandler;
|
||||||
std::queue<RequestBatchRequest> requests;
|
std::queue<RequestBatchRequest> requests;
|
||||||
std::vector<RequestResult> results;
|
std::vector<RequestResult> results;
|
||||||
@ -36,46 +37,43 @@ struct SerialFrameBatch {
|
|||||||
std::mutex conditionMutex;
|
std::mutex conditionMutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
|
|
||||||
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure)
|
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure) :
|
||||||
: requestHandler(requestHandler),
|
requestHandler(requestHandler),
|
||||||
variables(variables),
|
variables(variables),
|
||||||
haltOnFailure(haltOnFailure),
|
haltOnFailure(haltOnFailure),
|
||||||
frameCount(0),
|
frameCount(0),
|
||||||
sleepUntilFrame(0)
|
sleepUntilFrame(0)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParallelBatchResults {
|
struct ParallelBatchResults
|
||||||
|
{
|
||||||
RequestHandler &requestHandler;
|
RequestHandler &requestHandler;
|
||||||
std::vector<RequestResult> results;
|
std::vector<RequestResult> results;
|
||||||
|
|
||||||
std::mutex conditionMutex;
|
std::mutex conditionMutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
|
|
||||||
ParallelBatchResults(RequestHandler &requestHandler) : requestHandler(requestHandler) {}
|
ParallelBatchResults(RequestHandler &requestHandler) :
|
||||||
|
requestHandler(requestHandler)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
|
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
|
||||||
static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
|
static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
|
||||||
{
|
{
|
||||||
if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() ||
|
if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() || !request.RequestData.is_object())
|
||||||
!request.RequestData.is_object())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto& [key, value] : request.InputVariables.items()) {
|
for (auto& [key, value] : request.InputVariables.items()) {
|
||||||
if (!value.is_string()) {
|
if (!value.is_string()) {
|
||||||
blog_debug(
|
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!", key.c_str());
|
||||||
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!",
|
|
||||||
key.c_str());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string valueString = value;
|
std::string valueString = value;
|
||||||
if (!variables.contains(valueString)) {
|
if (!variables.contains(valueString)) {
|
||||||
blog_debug(
|
blog_debug("[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!", valueString.c_str());
|
||||||
"[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!",
|
|
||||||
valueString.c_str());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,17 +91,13 @@ static void PostProcessVariables(json &variables, const RequestBatchRequest &req
|
|||||||
|
|
||||||
for (auto& [key, value] : request.OutputVariables.items()) {
|
for (auto& [key, value] : request.OutputVariables.items()) {
|
||||||
if (!value.is_string()) {
|
if (!value.is_string()) {
|
||||||
blog_debug(
|
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!", key.c_str());
|
||||||
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!",
|
|
||||||
key.c_str());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string valueString = value;
|
std::string valueString = value;
|
||||||
if (!requestResult.ResponseData.contains(valueString)) {
|
if (!requestResult.ResponseData.contains(valueString)) {
|
||||||
blog_debug(
|
blog_debug("[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!", valueString.c_str());
|
||||||
"[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!",
|
|
||||||
valueString.c_str());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +109,7 @@ static void ObsTickCallback(void *param, float)
|
|||||||
{
|
{
|
||||||
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
|
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
|
||||||
|
|
||||||
auto serialFrameBatch = static_cast<SerialFrameBatch *>(param);
|
auto serialFrameBatch = reinterpret_cast<SerialFrameBatch*>(param);
|
||||||
|
|
||||||
// Increment frame count
|
// Increment frame count
|
||||||
serialFrameBatch->frameCount++;
|
serialFrameBatch->frameCount++;
|
||||||
@ -162,10 +156,7 @@ static void ObsTickCallback(void *param, float)
|
|||||||
serialFrameBatch->condition.notify_one();
|
serialFrameBatch->condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<RequestResult>
|
std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
|
||||||
RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
|
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
|
||||||
std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
|
|
||||||
{
|
{
|
||||||
RequestHandler requestHandler(session);
|
RequestHandler requestHandler(session);
|
||||||
if (executionType == RequestBatchExecutionType::SerialRealtime) {
|
if (executionType == RequestBatchExecutionType::SerialRealtime) {
|
||||||
@ -224,9 +215,7 @@ RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr ses
|
|||||||
|
|
||||||
// Wait for the last request to finish processing
|
// Wait for the last request to finish processing
|
||||||
size_t requestCount = requests.size();
|
size_t requestCount = requests.size();
|
||||||
parallelResults.condition.wait(lock, [¶llelResults, requestCount] {
|
parallelResults.condition.wait(lock, [¶llelResults, requestCount]{return parallelResults.results.size() == requestCount;});
|
||||||
return parallelResults.results.size() == requestCount;
|
|
||||||
});
|
|
||||||
|
|
||||||
return parallelResults.results;
|
return parallelResults.results;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,5 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "rpc/RequestBatchRequest.h"
|
#include "rpc/RequestBatchRequest.h"
|
||||||
|
|
||||||
namespace RequestBatchHandler {
|
namespace RequestBatchHandler {
|
||||||
std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
|
std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure);
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
|
||||||
std::vector<RequestBatchRequest> &requests, json &variables,
|
|
||||||
bool haltOnFailure);
|
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,10 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef PLUGIN_TESTS
|
|
||||||
#include <util/profiler.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap{
|
const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||||
|
{
|
||||||
// General
|
// General
|
||||||
{"GetVersion", &RequestHandler::GetVersion},
|
{"GetVersion", &RequestHandler::GetVersion},
|
||||||
{"GetStats", &RequestHandler::GetStats},
|
{"GetStats", &RequestHandler::GetStats},
|
||||||
@ -50,18 +47,14 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"SetVideoSettings", &RequestHandler::SetVideoSettings},
|
{"SetVideoSettings", &RequestHandler::SetVideoSettings},
|
||||||
{"GetStreamServiceSettings", &RequestHandler::GetStreamServiceSettings},
|
{"GetStreamServiceSettings", &RequestHandler::GetStreamServiceSettings},
|
||||||
{"SetStreamServiceSettings", &RequestHandler::SetStreamServiceSettings},
|
{"SetStreamServiceSettings", &RequestHandler::SetStreamServiceSettings},
|
||||||
{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
|
|
||||||
|
|
||||||
// Sources
|
// Sources
|
||||||
{"GetSourceActive", &RequestHandler::GetSourceActive},
|
{"GetSourceActive", &RequestHandler::GetSourceActive},
|
||||||
{"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot},
|
{"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot},
|
||||||
{"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot},
|
{"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot},
|
||||||
{"GetSourcePrivateSettings", &RequestHandler::GetSourcePrivateSettings},
|
|
||||||
{"SetSourcePrivateSettings", &RequestHandler::SetSourcePrivateSettings},
|
|
||||||
|
|
||||||
// Scenes
|
// Scenes
|
||||||
{"GetSceneList", &RequestHandler::GetSceneList},
|
{"GetSceneList", &RequestHandler::GetSceneList},
|
||||||
{"GetGroupList", &RequestHandler::GetGroupList},
|
|
||||||
{"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene},
|
{"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene},
|
||||||
{"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene},
|
{"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene},
|
||||||
{"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene},
|
{"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene},
|
||||||
@ -69,15 +62,12 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"CreateScene", &RequestHandler::CreateScene},
|
{"CreateScene", &RequestHandler::CreateScene},
|
||||||
{"RemoveScene", &RequestHandler::RemoveScene},
|
{"RemoveScene", &RequestHandler::RemoveScene},
|
||||||
{"SetSceneName", &RequestHandler::SetSceneName},
|
{"SetSceneName", &RequestHandler::SetSceneName},
|
||||||
{"GetSceneSceneTransitionOverride", &RequestHandler::GetSceneSceneTransitionOverride},
|
|
||||||
{"SetSceneSceneTransitionOverride", &RequestHandler::SetSceneSceneTransitionOverride},
|
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
{"GetInputList", &RequestHandler::GetInputList},
|
{"GetInputList", &RequestHandler::GetInputList},
|
||||||
{"GetInputKindList", &RequestHandler::GetInputKindList},
|
{"GetInputKindList", &RequestHandler::GetInputKindList},
|
||||||
{"GetSpecialInputs", &RequestHandler::GetSpecialInputs},
|
|
||||||
{"CreateInput", &RequestHandler::CreateInput},
|
{"CreateInput", &RequestHandler::CreateInput},
|
||||||
{"RemoveInput", &RequestHandler::RemoveInput},
|
//{"RemoveInput", &RequestHandler::RemoveInput}, // Disabled for now. Pending obs-studio#5276
|
||||||
{"SetInputName", &RequestHandler::SetInputName},
|
{"SetInputName", &RequestHandler::SetInputName},
|
||||||
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
|
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
|
||||||
{"GetInputSettings", &RequestHandler::GetInputSettings},
|
{"GetInputSettings", &RequestHandler::GetInputSettings},
|
||||||
@ -87,14 +77,10 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"ToggleInputMute", &RequestHandler::ToggleInputMute},
|
{"ToggleInputMute", &RequestHandler::ToggleInputMute},
|
||||||
{"GetInputVolume", &RequestHandler::GetInputVolume},
|
{"GetInputVolume", &RequestHandler::GetInputVolume},
|
||||||
{"SetInputVolume", &RequestHandler::SetInputVolume},
|
{"SetInputVolume", &RequestHandler::SetInputVolume},
|
||||||
{"GetInputAudioBalance", &RequestHandler::GetInputAudioBalance},
|
|
||||||
{"SetInputAudioBalance", &RequestHandler::SetInputAudioBalance},
|
|
||||||
{"GetInputAudioSyncOffset", &RequestHandler::GetInputAudioSyncOffset},
|
{"GetInputAudioSyncOffset", &RequestHandler::GetInputAudioSyncOffset},
|
||||||
{"SetInputAudioSyncOffset", &RequestHandler::SetInputAudioSyncOffset},
|
{"SetInputAudioSyncOffset", &RequestHandler::SetInputAudioSyncOffset},
|
||||||
{"GetInputAudioMonitorType", &RequestHandler::GetInputAudioMonitorType},
|
{"GetInputAudioMonitorType", &RequestHandler::GetInputAudioMonitorType},
|
||||||
{"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType},
|
{"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType},
|
||||||
{"GetInputAudioTracks", &RequestHandler::GetInputAudioTracks},
|
|
||||||
{"SetInputAudioTracks", &RequestHandler::SetInputAudioTracks},
|
|
||||||
{"GetInputPropertiesListPropertyItems", &RequestHandler::GetInputPropertiesListPropertyItems},
|
{"GetInputPropertiesListPropertyItems", &RequestHandler::GetInputPropertiesListPropertyItems},
|
||||||
{"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton},
|
{"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton},
|
||||||
|
|
||||||
@ -105,20 +91,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
|
{"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
|
||||||
{"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
|
{"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
|
||||||
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
|
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
|
||||||
{"GetCurrentSceneTransitionCursor", &RequestHandler::GetCurrentSceneTransitionCursor},
|
|
||||||
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
|
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
|
||||||
{"SetTBarPosition", &RequestHandler::SetTBarPosition},
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
|
|
||||||
{"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings},
|
|
||||||
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
|
|
||||||
{"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter},
|
|
||||||
{"SetSourceFilterName", &RequestHandler::SetSourceFilterName},
|
|
||||||
{"GetSourceFilter", &RequestHandler::GetSourceFilter},
|
|
||||||
{"SetSourceFilterIndex", &RequestHandler::SetSourceFilterIndex},
|
|
||||||
{"SetSourceFilterSettings", &RequestHandler::SetSourceFilterSettings},
|
|
||||||
{"SetSourceFilterEnabled", &RequestHandler::SetSourceFilterEnabled},
|
|
||||||
|
|
||||||
// Scene Items
|
// Scene Items
|
||||||
{"GetSceneItemList", &RequestHandler::GetSceneItemList},
|
{"GetSceneItemList", &RequestHandler::GetSceneItemList},
|
||||||
@ -135,36 +108,12 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
|
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
|
||||||
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
|
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
|
||||||
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
|
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
|
||||||
{"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode},
|
|
||||||
{"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode},
|
|
||||||
{"GetSceneItemPrivateSettings", &RequestHandler::GetSceneItemPrivateSettings},
|
|
||||||
{"SetSceneItemPrivateSettings", &RequestHandler::SetSceneItemPrivateSettings},
|
|
||||||
|
|
||||||
// Outputs
|
|
||||||
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
|
|
||||||
{"ToggleVirtualCam", &RequestHandler::ToggleVirtualCam},
|
|
||||||
{"StartVirtualCam", &RequestHandler::StartVirtualCam},
|
|
||||||
{"StopVirtualCam", &RequestHandler::StopVirtualCam},
|
|
||||||
{"GetReplayBufferStatus", &RequestHandler::GetReplayBufferStatus},
|
|
||||||
{"ToggleReplayBuffer", &RequestHandler::ToggleReplayBuffer},
|
|
||||||
{"StartReplayBuffer", &RequestHandler::StartReplayBuffer},
|
|
||||||
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
|
|
||||||
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
|
|
||||||
{"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
|
|
||||||
{"GetOutputList", &RequestHandler::GetOutputList},
|
|
||||||
{"GetOutputStatus", &RequestHandler::GetOutputStatus},
|
|
||||||
{"ToggleOutput", &RequestHandler::ToggleOutput},
|
|
||||||
{"StartOutput", &RequestHandler::StartOutput},
|
|
||||||
{"StopOutput", &RequestHandler::StopOutput},
|
|
||||||
{"GetOutputSettings", &RequestHandler::GetOutputSettings},
|
|
||||||
{"SetOutputSettings", &RequestHandler::SetOutputSettings},
|
|
||||||
|
|
||||||
// Stream
|
// Stream
|
||||||
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
||||||
{"ToggleStream", &RequestHandler::ToggleStream},
|
{"ToggleStream", &RequestHandler::ToggleStream},
|
||||||
{"StartStream", &RequestHandler::StartStream},
|
{"StartStream", &RequestHandler::StartStream},
|
||||||
{"StopStream", &RequestHandler::StopStream},
|
{"StopStream", &RequestHandler::StopStream},
|
||||||
{"SendStreamCaption", &RequestHandler::SendStreamCaption},
|
|
||||||
|
|
||||||
// Record
|
// Record
|
||||||
{"GetRecordStatus", &RequestHandler::GetRecordStatus},
|
{"GetRecordStatus", &RequestHandler::GetRecordStatus},
|
||||||
@ -174,6 +123,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
|
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
|
||||||
{"PauseRecord", &RequestHandler::PauseRecord},
|
{"PauseRecord", &RequestHandler::PauseRecord},
|
||||||
{"ResumeRecord", &RequestHandler::ResumeRecord},
|
{"ResumeRecord", &RequestHandler::ResumeRecord},
|
||||||
|
//{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
|
||||||
|
|
||||||
// Media Inputs
|
// Media Inputs
|
||||||
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},
|
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},
|
||||||
@ -184,27 +134,20 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
// Ui
|
// Ui
|
||||||
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
||||||
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
||||||
{"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog},
|
|
||||||
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
|
||||||
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
|
||||||
{"GetMonitorList", &RequestHandler::GetMonitorList},
|
|
||||||
{"OpenVideoMixProjector", &RequestHandler::OpenVideoMixProjector},
|
|
||||||
{"OpenSourceProjector", &RequestHandler::OpenSourceProjector},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RequestHandler::RequestHandler(SessionPtr session) : _session(session) {}
|
RequestHandler::RequestHandler(SessionPtr session) :
|
||||||
|
_session(session)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
RequestResult RequestHandler::ProcessRequest(const Request& request)
|
RequestResult RequestHandler::ProcessRequest(const Request& request)
|
||||||
{
|
{
|
||||||
#ifdef PLUGIN_TESTS
|
|
||||||
ScopeProfiler prof{"obs_websocket_request_processing"};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!request.RequestData.is_object() && !request.RequestData.is_null())
|
if (!request.RequestData.is_object() && !request.RequestData.is_null())
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
|
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
|
||||||
|
|
||||||
if (request.RequestType.empty())
|
if (request.RequestType.empty())
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestType, "Your request's `requestType` may not be empty.");
|
return RequestResult::Error(RequestStatus::MissingRequestType, "Your request is missing a `requestType`");
|
||||||
|
|
||||||
RequestMethodHandler handler;
|
RequestMethodHandler handler;
|
||||||
try {
|
try {
|
||||||
|
@ -19,7 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <map>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ typedef RequestResult (RequestHandler::*RequestMethodHandler)(const Request &);
|
|||||||
|
|
||||||
class RequestHandler {
|
class RequestHandler {
|
||||||
public:
|
public:
|
||||||
RequestHandler(SessionPtr session = nullptr);
|
RequestHandler(SessionPtr session);
|
||||||
|
|
||||||
RequestResult ProcessRequest(const Request& request);
|
RequestResult ProcessRequest(const Request& request);
|
||||||
std::vector<std::string> GetRequestList();
|
std::vector<std::string> GetRequestList();
|
||||||
@ -69,18 +69,14 @@ private:
|
|||||||
RequestResult SetVideoSettings(const Request&);
|
RequestResult SetVideoSettings(const Request&);
|
||||||
RequestResult GetStreamServiceSettings(const Request&);
|
RequestResult GetStreamServiceSettings(const Request&);
|
||||||
RequestResult SetStreamServiceSettings(const Request&);
|
RequestResult SetStreamServiceSettings(const Request&);
|
||||||
RequestResult GetRecordDirectory(const Request &);
|
|
||||||
|
|
||||||
// Sources
|
// Sources
|
||||||
RequestResult GetSourceActive(const Request&);
|
RequestResult GetSourceActive(const Request&);
|
||||||
RequestResult GetSourceScreenshot(const Request&);
|
RequestResult GetSourceScreenshot(const Request&);
|
||||||
RequestResult SaveSourceScreenshot(const Request&);
|
RequestResult SaveSourceScreenshot(const Request&);
|
||||||
RequestResult GetSourcePrivateSettings(const Request &);
|
|
||||||
RequestResult SetSourcePrivateSettings(const Request &);
|
|
||||||
|
|
||||||
// Scenes
|
// Scenes
|
||||||
RequestResult GetSceneList(const Request&);
|
RequestResult GetSceneList(const Request&);
|
||||||
RequestResult GetGroupList(const Request &);
|
|
||||||
RequestResult GetCurrentProgramScene(const Request&);
|
RequestResult GetCurrentProgramScene(const Request&);
|
||||||
RequestResult SetCurrentProgramScene(const Request&);
|
RequestResult SetCurrentProgramScene(const Request&);
|
||||||
RequestResult GetCurrentPreviewScene(const Request&);
|
RequestResult GetCurrentPreviewScene(const Request&);
|
||||||
@ -88,13 +84,10 @@ private:
|
|||||||
RequestResult CreateScene(const Request&);
|
RequestResult CreateScene(const Request&);
|
||||||
RequestResult RemoveScene(const Request&);
|
RequestResult RemoveScene(const Request&);
|
||||||
RequestResult SetSceneName(const Request&);
|
RequestResult SetSceneName(const Request&);
|
||||||
RequestResult GetSceneSceneTransitionOverride(const Request &);
|
|
||||||
RequestResult SetSceneSceneTransitionOverride(const Request &);
|
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
RequestResult GetInputList(const Request&);
|
RequestResult GetInputList(const Request&);
|
||||||
RequestResult GetInputKindList(const Request&);
|
RequestResult GetInputKindList(const Request&);
|
||||||
RequestResult GetSpecialInputs(const Request &);
|
|
||||||
RequestResult CreateInput(const Request&);
|
RequestResult CreateInput(const Request&);
|
||||||
RequestResult RemoveInput(const Request&);
|
RequestResult RemoveInput(const Request&);
|
||||||
RequestResult SetInputName(const Request&);
|
RequestResult SetInputName(const Request&);
|
||||||
@ -106,14 +99,10 @@ private:
|
|||||||
RequestResult ToggleInputMute(const Request&);
|
RequestResult ToggleInputMute(const Request&);
|
||||||
RequestResult GetInputVolume(const Request&);
|
RequestResult GetInputVolume(const Request&);
|
||||||
RequestResult SetInputVolume(const Request&);
|
RequestResult SetInputVolume(const Request&);
|
||||||
RequestResult GetInputAudioBalance(const Request &);
|
|
||||||
RequestResult SetInputAudioBalance(const Request &);
|
|
||||||
RequestResult GetInputAudioSyncOffset(const Request&);
|
RequestResult GetInputAudioSyncOffset(const Request&);
|
||||||
RequestResult SetInputAudioSyncOffset(const Request&);
|
RequestResult SetInputAudioSyncOffset(const Request&);
|
||||||
RequestResult GetInputAudioMonitorType(const Request&);
|
RequestResult GetInputAudioMonitorType(const Request&);
|
||||||
RequestResult SetInputAudioMonitorType(const Request&);
|
RequestResult SetInputAudioMonitorType(const Request&);
|
||||||
RequestResult GetInputAudioTracks(const Request &);
|
|
||||||
RequestResult SetInputAudioTracks(const Request &);
|
|
||||||
RequestResult GetInputPropertiesListPropertyItems(const Request&);
|
RequestResult GetInputPropertiesListPropertyItems(const Request&);
|
||||||
RequestResult PressInputPropertiesButton(const Request&);
|
RequestResult PressInputPropertiesButton(const Request&);
|
||||||
|
|
||||||
@ -124,20 +113,7 @@ private:
|
|||||||
RequestResult SetCurrentSceneTransition(const Request&);
|
RequestResult SetCurrentSceneTransition(const Request&);
|
||||||
RequestResult SetCurrentSceneTransitionDuration(const Request&);
|
RequestResult SetCurrentSceneTransitionDuration(const Request&);
|
||||||
RequestResult SetCurrentSceneTransitionSettings(const Request&);
|
RequestResult SetCurrentSceneTransitionSettings(const Request&);
|
||||||
RequestResult GetCurrentSceneTransitionCursor(const Request &);
|
|
||||||
RequestResult TriggerStudioModeTransition(const Request&);
|
RequestResult TriggerStudioModeTransition(const Request&);
|
||||||
RequestResult SetTBarPosition(const Request &);
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
RequestResult GetSourceFilterList(const Request &);
|
|
||||||
RequestResult GetSourceFilterDefaultSettings(const Request &);
|
|
||||||
RequestResult CreateSourceFilter(const Request &);
|
|
||||||
RequestResult RemoveSourceFilter(const Request &);
|
|
||||||
RequestResult SetSourceFilterName(const Request &);
|
|
||||||
RequestResult GetSourceFilter(const Request &);
|
|
||||||
RequestResult SetSourceFilterIndex(const Request &);
|
|
||||||
RequestResult SetSourceFilterSettings(const Request &);
|
|
||||||
RequestResult SetSourceFilterEnabled(const Request &);
|
|
||||||
|
|
||||||
// Scene Items
|
// Scene Items
|
||||||
RequestResult GetSceneItemList(const Request&);
|
RequestResult GetSceneItemList(const Request&);
|
||||||
@ -154,36 +130,12 @@ private:
|
|||||||
RequestResult SetSceneItemLocked(const Request&);
|
RequestResult SetSceneItemLocked(const Request&);
|
||||||
RequestResult GetSceneItemIndex(const Request&);
|
RequestResult GetSceneItemIndex(const Request&);
|
||||||
RequestResult SetSceneItemIndex(const Request&);
|
RequestResult SetSceneItemIndex(const Request&);
|
||||||
RequestResult GetSceneItemBlendMode(const Request &);
|
|
||||||
RequestResult SetSceneItemBlendMode(const Request &);
|
|
||||||
RequestResult GetSceneItemPrivateSettings(const Request &);
|
|
||||||
RequestResult SetSceneItemPrivateSettings(const Request &);
|
|
||||||
|
|
||||||
// Outputs
|
|
||||||
RequestResult GetVirtualCamStatus(const Request &);
|
|
||||||
RequestResult ToggleVirtualCam(const Request &);
|
|
||||||
RequestResult StartVirtualCam(const Request &);
|
|
||||||
RequestResult StopVirtualCam(const Request &);
|
|
||||||
RequestResult GetReplayBufferStatus(const Request &);
|
|
||||||
RequestResult ToggleReplayBuffer(const Request &);
|
|
||||||
RequestResult StartReplayBuffer(const Request &);
|
|
||||||
RequestResult StopReplayBuffer(const Request &);
|
|
||||||
RequestResult SaveReplayBuffer(const Request &);
|
|
||||||
RequestResult GetLastReplayBufferReplay(const Request &);
|
|
||||||
RequestResult GetOutputList(const Request &);
|
|
||||||
RequestResult GetOutputStatus(const Request &);
|
|
||||||
RequestResult ToggleOutput(const Request &);
|
|
||||||
RequestResult StartOutput(const Request &);
|
|
||||||
RequestResult StopOutput(const Request &);
|
|
||||||
RequestResult GetOutputSettings(const Request &);
|
|
||||||
RequestResult SetOutputSettings(const Request &);
|
|
||||||
|
|
||||||
// Stream
|
// Stream
|
||||||
RequestResult GetStreamStatus(const Request&);
|
RequestResult GetStreamStatus(const Request&);
|
||||||
RequestResult ToggleStream(const Request&);
|
RequestResult ToggleStream(const Request&);
|
||||||
RequestResult StartStream(const Request&);
|
RequestResult StartStream(const Request&);
|
||||||
RequestResult StopStream(const Request&);
|
RequestResult StopStream(const Request&);
|
||||||
RequestResult SendStreamCaption(const Request &);
|
|
||||||
|
|
||||||
// Record
|
// Record
|
||||||
RequestResult GetRecordStatus(const Request&);
|
RequestResult GetRecordStatus(const Request&);
|
||||||
@ -193,6 +145,7 @@ private:
|
|||||||
RequestResult ToggleRecordPause(const Request&);
|
RequestResult ToggleRecordPause(const Request&);
|
||||||
RequestResult PauseRecord(const Request&);
|
RequestResult PauseRecord(const Request&);
|
||||||
RequestResult ResumeRecord(const Request&);
|
RequestResult ResumeRecord(const Request&);
|
||||||
|
RequestResult GetRecordDirectory(const Request&);
|
||||||
|
|
||||||
// Media Inputs
|
// Media Inputs
|
||||||
RequestResult GetMediaInputStatus(const Request&);
|
RequestResult GetMediaInputStatus(const Request&);
|
||||||
@ -203,13 +156,7 @@ private:
|
|||||||
// Ui
|
// Ui
|
||||||
RequestResult GetStudioModeEnabled(const Request&);
|
RequestResult GetStudioModeEnabled(const Request&);
|
||||||
RequestResult SetStudioModeEnabled(const Request&);
|
RequestResult SetStudioModeEnabled(const Request&);
|
||||||
RequestResult OpenInputPropertiesDialog(const Request &);
|
|
||||||
RequestResult OpenInputFiltersDialog(const Request &);
|
|
||||||
RequestResult OpenInputInteractDialog(const Request &);
|
|
||||||
RequestResult GetMonitorList(const Request &);
|
|
||||||
RequestResult OpenVideoMixProjector(const Request &);
|
|
||||||
RequestResult OpenSourceProjector(const Request &);
|
|
||||||
|
|
||||||
SessionPtr _session;
|
SessionPtr _session;
|
||||||
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
static const std::map<std::string, RequestMethodHandler> _handlerMap;
|
||||||
};
|
};
|
||||||
|
@ -53,8 +53,7 @@ RequestResult RequestHandler::GetPersistentData(const Request &request)
|
|||||||
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
||||||
persistentDataPath += "/obsWebSocketPersistentData.json";
|
persistentDataPath += "/obsWebSocketPersistentData.json";
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
return RequestResult::Error(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
|
||||||
"You have specified an invalid persistent data realm.");
|
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
json persistentData;
|
json persistentData;
|
||||||
@ -84,8 +83,7 @@ RequestResult RequestHandler::SetPersistentData(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) &&
|
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) && request.ValidateBasic("slotValue", statusCode, comment)))
|
||||||
request.ValidateBasic("slotValue", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string realm = request.RequestData["realm"];
|
std::string realm = request.RequestData["realm"];
|
||||||
@ -98,15 +96,13 @@ RequestResult RequestHandler::SetPersistentData(const Request &request)
|
|||||||
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
||||||
persistentDataPath += "/obsWebSocketPersistentData.json";
|
persistentDataPath += "/obsWebSocketPersistentData.json";
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
return RequestResult::Error(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
|
||||||
"You have specified an invalid persistent data realm.");
|
|
||||||
|
|
||||||
json persistentData = json::object();
|
json persistentData = json::object();
|
||||||
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
|
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
|
||||||
persistentData[slotName] = slotValue;
|
persistentData[slotName] = slotValue;
|
||||||
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
|
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to write persistent data. No permissions?");
|
||||||
"Unable to write persistent data. No permissions?");
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -162,10 +158,9 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request &request)
|
|||||||
std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentSceneCollectionName != sceneCollectionName) {
|
if (currentSceneCollectionName != sceneCollectionName) {
|
||||||
obs_queue_task(
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
OBS_TASK_UI,
|
obs_frontend_set_current_scene_collection(reinterpret_cast<const char*>(param));
|
||||||
[](void *param) { obs_frontend_set_current_scene_collection(static_cast<const char *>(param)); },
|
}, (void*)sceneCollectionName.c_str(), true);
|
||||||
(void *)sceneCollectionName.c_str(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -198,10 +193,9 @@ RequestResult RequestHandler::CreateSceneCollection(const Request &request)
|
|||||||
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
|
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
||||||
|
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
bool success = false;
|
bool success = false;
|
||||||
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success),
|
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
||||||
Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
|
||||||
|
|
||||||
@ -257,9 +251,9 @@ RequestResult RequestHandler::SetCurrentProfile(const Request &request)
|
|||||||
std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
|
std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentProfileName != profileName) {
|
if (currentProfileName != profileName) {
|
||||||
obs_queue_task(
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
OBS_TASK_UI, [](void *param) { obs_frontend_set_current_profile(static_cast<const char *>(param)); },
|
obs_frontend_set_current_profile(reinterpret_cast<const char*>(param));
|
||||||
(void *)profileName.c_str(), true);
|
}, (void*)profileName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -290,9 +284,8 @@ RequestResult RequestHandler::CreateProfile(const Request &request)
|
|||||||
if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end())
|
if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
||||||
|
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
Q_ARG(QString, QString::fromStdString(profileName)));
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -325,9 +318,8 @@ RequestResult RequestHandler::RemoveProfile(const Request &request)
|
|||||||
if (profiles.size() < 2)
|
if (profiles.size() < 2)
|
||||||
return RequestResult::Error(RequestStatus::NotEnoughResources);
|
return RequestResult::Error(RequestStatus::NotEnoughResources);
|
||||||
|
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
Q_ARG(QString, QString::fromStdString(profileName)));
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -352,8 +344,7 @@ RequestResult RequestHandler::GetProfileParameter(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
|
if (!(request.ValidateString("parameterCategory", statusCode, comment) && request.ValidateString("parameterName", statusCode, comment)))
|
||||||
request.ValidateString("parameterName", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string parameterCategory = request.RequestData["parameterCategory"];
|
std::string parameterCategory = request.RequestData["parameterCategory"];
|
||||||
@ -367,8 +358,7 @@ RequestResult RequestHandler::GetProfileParameter(const Request &request)
|
|||||||
json responseData;
|
json responseData;
|
||||||
if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
||||||
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
||||||
responseData["defaultParameterValue"] =
|
responseData["defaultParameterValue"] = config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
||||||
config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
|
||||||
} else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
} else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
||||||
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
||||||
responseData["defaultParameterValue"] = nullptr;
|
responseData["defaultParameterValue"] = nullptr;
|
||||||
@ -410,8 +400,7 @@ RequestResult RequestHandler::SetProfileParameter(const Request &request)
|
|||||||
// Using check helpers here would just make the logic more complicated
|
// Using check helpers here would just make the logic more complicated
|
||||||
if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
|
if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
|
||||||
if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
|
if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
return RequestResult::Error(RequestStatus::ResourceNotFound, "There are no existing instances of that profile parameter.");
|
||||||
"There are no existing instances of that profile parameter.");
|
|
||||||
} else if (request.RequestData["parameterValue"].is_string()) {
|
} else if (request.RequestData["parameterValue"].is_string()) {
|
||||||
std::string parameterValue = request.RequestData["parameterValue"];
|
std::string parameterValue = request.RequestData["parameterValue"];
|
||||||
config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str());
|
config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str());
|
||||||
@ -419,8 +408,6 @@ RequestResult RequestHandler::SetProfileParameter(const Request &request)
|
|||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string.");
|
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
config_save(profile);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,24 +469,20 @@ RequestResult RequestHandler::GetVideoSettings(const Request &)
|
|||||||
RequestResult RequestHandler::SetVideoSettings(const Request& request)
|
RequestResult RequestHandler::SetVideoSettings(const Request& request)
|
||||||
{
|
{
|
||||||
if (obs_video_active())
|
if (obs_video_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning,
|
return RequestResult::Error(RequestStatus::OutputRunning, "Video settings cannot be changed while an output is active.");
|
||||||
"Video settings cannot be changed while an output is active.");
|
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
|
RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
|
bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
|
||||||
if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) &&
|
if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) && request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
|
||||||
request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
|
bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
|
||||||
if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) &&
|
if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
|
||||||
request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
|
bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
|
||||||
if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) &&
|
if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
|
||||||
request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
config_t *config = obs_frontend_get_profile_config();
|
config_t *config = obs_frontend_get_profile_config();
|
||||||
@ -572,21 +555,18 @@ RequestResult RequestHandler::GetStreamServiceSettings(const Request &)
|
|||||||
RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
|
RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
|
||||||
{
|
{
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning,
|
return RequestResult::Error(RequestStatus::OutputRunning, "You cannot change stream service settings while streaming.");
|
||||||
"You cannot change stream service settings while streaming.");
|
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("streamServiceType", statusCode, comment) &&
|
if (!(request.ValidateString("streamServiceType", statusCode, comment) && request.ValidateObject("streamServiceSettings", statusCode, comment)))
|
||||||
request.ValidateObject("streamServiceSettings", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSService currentStreamService = obs_frontend_get_streaming_service();
|
OBSService currentStreamService = obs_frontend_get_streaming_service();
|
||||||
|
|
||||||
std::string streamServiceType = obs_service_get_type(currentStreamService);
|
std::string streamServiceType = obs_service_get_type(currentStreamService);
|
||||||
std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
|
std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
|
||||||
OBSDataAutoRelease requestedStreamServiceSettings =
|
OBSDataAutoRelease requestedStreamServiceSettings = Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
|
||||||
Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
|
|
||||||
|
|
||||||
// Don't create a new service if the current service is the same type.
|
// Don't create a new service if the current service is the same type.
|
||||||
if (streamServiceType == requestedStreamServiceType) {
|
if (streamServiceType == requestedStreamServiceType) {
|
||||||
@ -600,13 +580,10 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
|
|||||||
obs_service_update(currentStreamService, newStreamServiceSettings);
|
obs_service_update(currentStreamService, newStreamServiceSettings);
|
||||||
} else {
|
} else {
|
||||||
// TODO: This leaks memory. I have no idea why.
|
// TODO: This leaks memory. I have no idea why.
|
||||||
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service",
|
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service", requestedStreamServiceSettings, nullptr);
|
||||||
requestedStreamServiceSettings, nullptr);
|
|
||||||
// TODO: Check service type here, instead of relying on service creation to fail.
|
// TODO: Check service type here, instead of relying on service creation to fail.
|
||||||
if (!newStreamService)
|
if (!newStreamService)
|
||||||
return RequestResult::Error(
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
|
||||||
RequestStatus::ResourceCreationFailed,
|
|
||||||
"Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
|
|
||||||
|
|
||||||
obs_frontend_set_streaming_service(newStreamService);
|
obs_frontend_set_streaming_service(newStreamService);
|
||||||
}
|
}
|
||||||
@ -615,23 +592,3 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current directory that the record output is set to.
|
|
||||||
*
|
|
||||||
* @responseField recordDirectory | String | Output directory
|
|
||||||
*
|
|
||||||
* @requestType GetRecordDirectory
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category rconfig
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetRecordDirectory(const Request &)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
@ -18,322 +18,3 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an array of all of a source's filters.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source
|
|
||||||
*
|
|
||||||
* @responseField filters | Array<Object> | Array of filters
|
|
||||||
*
|
|
||||||
* @requestType GetSourceFilterList
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetSourceFilterList(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
|
||||||
if (!source)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the default settings for a filter kind.
|
|
||||||
*
|
|
||||||
* @requestField filterKind | String | Filter kind to get the default settings for
|
|
||||||
*
|
|
||||||
* @responseField defaultFilterSettings | Object | Object of default settings for the filter kind
|
|
||||||
*
|
|
||||||
* @requestType GetSourceFilterDefaultSettings
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
if (!request.ValidateString("filterKind", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
std::string filterKind = request.RequestData["filterKind"];
|
|
||||||
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
|
|
||||||
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidFilterKind);
|
|
||||||
|
|
||||||
OBSDataAutoRelease defaultSettings = obs_get_source_defaults(filterKind.c_str());
|
|
||||||
if (!defaultSettings)
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidFilterKind);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true);
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new filter, adding it to the specified source.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source to add the filter to
|
|
||||||
* @requestField filterName | String | Name of the new filter to be created
|
|
||||||
* @requestField filterKind | String | The kind of filter to be created
|
|
||||||
* @requestField ?filterSettings | Object | Settings object to initialize the filter with | Default settings used
|
|
||||||
*
|
|
||||||
* @requestType CreateSourceFilter
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::CreateSourceFilter(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
|
||||||
if (!(source && request.ValidateString("filterName", statusCode, comment) &&
|
|
||||||
request.ValidateString("filterKind", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
std::string filterName = request.RequestData["filterName"];
|
|
||||||
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(source, filterName.c_str());
|
|
||||||
if (existingFilter)
|
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that name.");
|
|
||||||
|
|
||||||
std::string filterKind = request.RequestData["filterKind"];
|
|
||||||
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
|
|
||||||
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
|
|
||||||
return RequestResult::Error(
|
|
||||||
RequestStatus::InvalidFilterKind,
|
|
||||||
"Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
|
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = nullptr;
|
|
||||||
if (request.Contains("filterSettings")) {
|
|
||||||
if (!request.ValidateOptionalObject("filterSettings", statusCode, comment, true))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
filterSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings);
|
|
||||||
|
|
||||||
if (!filter)
|
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed.");
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a filter from a source.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source the filter is on
|
|
||||||
* @requestField filterName | String | Name of the filter to remove
|
|
||||||
*
|
|
||||||
* @requestType RemoveSourceFilter
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::RemoveSourceFilter(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
|
||||||
if (!pair.filter)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
obs_source_filter_remove(pair.source, pair.filter);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the name of a source filter (rename).
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source the filter is on
|
|
||||||
* @requestField filterName | String | Current name of the filter
|
|
||||||
* @requestField newFilterName | String | New name for the filter
|
|
||||||
*
|
|
||||||
* @requestType SetSourceFilterName
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetSourceFilterName(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
|
||||||
if (!pair.filter || !request.ValidateString("newFilterName", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
std::string newFilterName = request.RequestData["newFilterName"];
|
|
||||||
|
|
||||||
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(pair.source, newFilterName.c_str());
|
|
||||||
if (existingFilter)
|
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that new name.");
|
|
||||||
|
|
||||||
obs_source_set_name(pair.filter, newFilterName.c_str());
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the info for a specific source filter.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source
|
|
||||||
* @requestField filterName | String | Name of the filter
|
|
||||||
*
|
|
||||||
* @responseField filterEnabled | Boolean | Whether the filter is enabled
|
|
||||||
* @responseField filterIndex | Number | Index of the filter in the list, beginning at 0
|
|
||||||
* @responseField filterKind | String | The kind of filter
|
|
||||||
* @responseField filterSettings | Object | Settings object associated with the filter
|
|
||||||
*
|
|
||||||
* @requestType GetSourceFilter
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetSourceFilter(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
|
||||||
if (!pair.filter)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["filterEnabled"] = obs_source_enabled(pair.filter);
|
|
||||||
responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(
|
|
||||||
pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
|
|
||||||
responseData["filterKind"] = obs_source_get_id(pair.filter);
|
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
|
|
||||||
responseData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the index position of a filter on a source.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source the filter is on
|
|
||||||
* @requestField filterName | String | Name of the filter
|
|
||||||
* @requestField filterIndex | Number | New index position of the filter | >= 0
|
|
||||||
*
|
|
||||||
* @requestType SetSourceFilterIndex
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetSourceFilterIndex(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
|
||||||
if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode, comment, 0, 8192)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
int filterIndex = request.RequestData["filterIndex"];
|
|
||||||
|
|
||||||
Utils::Obs::ActionHelper::SetSourceFilterIndex(pair.source, pair.filter, filterIndex);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the settings of a source filter.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source the filter is on
|
|
||||||
* @requestField filterName | String | Name of the filter to set the settings of
|
|
||||||
* @requestField filterSettings | Object | Object of settings to apply
|
|
||||||
* @requestField ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | true
|
|
||||||
*
|
|
||||||
* @requestType SetSourceFilterSettings
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetSourceFilterSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
|
||||||
if (!(pair.filter && request.ValidateObject("filterSettings", statusCode, comment, true)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
// Almost identical to SetInputSettings
|
|
||||||
|
|
||||||
bool overlay = true;
|
|
||||||
if (request.Contains("overlay")) {
|
|
||||||
if (!request.ValidateOptionalBoolean("overlay", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
overlay = request.RequestData["overlay"];
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
|
|
||||||
if (!newSettings)
|
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
|
||||||
"An internal data conversion operation failed. Please report this!");
|
|
||||||
|
|
||||||
if (overlay)
|
|
||||||
obs_source_update(pair.filter, newSettings);
|
|
||||||
else
|
|
||||||
obs_source_reset_settings(pair.filter, newSettings);
|
|
||||||
|
|
||||||
obs_source_update_properties(pair.filter);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the enable state of a source filter.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source the filter is on
|
|
||||||
* @requestField filterName | String | Name of the filter
|
|
||||||
* @requestField filterEnabled | Boolean | New enable state of the filter
|
|
||||||
*
|
|
||||||
* @requestType SetSourceFilterEnabled
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category filters
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetSourceFilterEnabled(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
|
|
||||||
if (!(pair.filter && request.ValidateBoolean("filterEnabled", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
bool filterEnabled = request.RequestData["filterEnabled"];
|
|
||||||
|
|
||||||
obs_source_set_enabled(pair.filter, filterEnabled);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -18,7 +18,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QImageWriter>
|
#include <QImageWriter>
|
||||||
#include <QSysInfo>
|
|
||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
#include "../websocketserver/WebSocketServer.h"
|
#include "../websocketserver/WebSocketServer.h"
|
||||||
@ -26,6 +25,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../WebSocketApi.h"
|
#include "../WebSocketApi.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets data about the current plugin and RPC version.
|
* Gets data about the current plugin and RPC version.
|
||||||
*
|
*
|
||||||
@ -34,8 +34,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @responseField rpcVersion | Number | Current latest obs-websocket RPC version
|
* @responseField rpcVersion | Number | Current latest obs-websocket RPC version
|
||||||
* @responseField availableRequests | Array<String> | Array of available RPC requests for the currently negotiated RPC version
|
* @responseField availableRequests | Array<String> | Array of available RPC requests for the currently negotiated RPC version
|
||||||
* @responseField supportedImageFormats | Array<String> | Image formats available in `GetSourceScreenshot` and `SaveSourceScreenshot` requests.
|
* @responseField supportedImageFormats | Array<String> | Image formats available in `GetSourceScreenshot` and `SaveSourceScreenshot` requests.
|
||||||
* @responseField platform | String | Name of the platform. Usually `windows`, `macos`, or `ubuntu` (linux flavor). Not guaranteed to be any of those
|
|
||||||
* @responseField platformDescription | String | Description of the platform, like `Windows 10 (10.0)`
|
|
||||||
*
|
*
|
||||||
* @requestType GetVersion
|
* @requestType GetVersion
|
||||||
* @complexity 1
|
* @complexity 1
|
||||||
@ -59,9 +57,6 @@ RequestResult RequestHandler::GetVersion(const Request &)
|
|||||||
}
|
}
|
||||||
responseData["supportedImageFormats"] = supportedImageFormats;
|
responseData["supportedImageFormats"] = supportedImageFormats;
|
||||||
|
|
||||||
responseData["platform"] = QSysInfo::productType().toStdString();
|
|
||||||
responseData["platformDescription"] = QSysInfo::prettyProductName().toStdString();
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,13 +86,8 @@ RequestResult RequestHandler::GetStats(const Request &)
|
|||||||
{
|
{
|
||||||
json responseData = Utils::Obs::ObjectHelper::GetStats();
|
json responseData = Utils::Obs::ObjectHelper::GetStats();
|
||||||
|
|
||||||
if (_session) {
|
|
||||||
responseData["webSocketSessionIncomingMessages"] = _session->IncomingMessages();
|
responseData["webSocketSessionIncomingMessages"] = _session->IncomingMessages();
|
||||||
responseData["webSocketSessionOutgoingMessages"] = _session->OutgoingMessages();
|
responseData["webSocketSessionOutgoingMessages"] = _session->OutgoingMessages();
|
||||||
} else {
|
|
||||||
responseData["webSocketSessionIncomingMessages"] = nullptr;
|
|
||||||
responseData["webSocketSessionOutgoingMessages"] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -140,8 +130,6 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request &request)
|
|||||||
* @requestField requestType | String | The request type to call
|
* @requestField requestType | String | The request type to call
|
||||||
* @requestField ?requestData | Object | Object containing appropriate request data | {}
|
* @requestField ?requestData | Object | Object containing appropriate request data | {}
|
||||||
*
|
*
|
||||||
* @responseField vendorName | String | Echoed of `vendorName`
|
|
||||||
* @responseField requestType | String | Echoed of `requestType`
|
|
||||||
* @responseField responseData | Object | Object containing appropriate response data. {} if request does not provide any response data
|
* @responseField responseData | Object | Object containing appropriate response data. {} if request does not provide any response data
|
||||||
*
|
*
|
||||||
* @requestType CallVendorRequest
|
* @requestType CallVendorRequest
|
||||||
@ -155,8 +143,7 @@ RequestResult RequestHandler::CallVendorRequest(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!request.ValidateString("vendorName", statusCode, comment) ||
|
if (!request.ValidateString("vendorName", statusCode, comment) || !request.ValidateString("requestType", statusCode, comment))
|
||||||
!request.ValidateString("requestType", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string vendorName = request.RequestData["vendorName"];
|
std::string vendorName = request.RequestData["vendorName"];
|
||||||
@ -174,8 +161,7 @@ RequestResult RequestHandler::CallVendorRequest(const Request &request)
|
|||||||
|
|
||||||
auto webSocketApi = GetWebSocketApi();
|
auto webSocketApi = GetWebSocketApi();
|
||||||
if (!webSocketApi)
|
if (!webSocketApi)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to call request due to internal error.");
|
||||||
"Unable to call request due to internal error.");
|
|
||||||
|
|
||||||
auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
|
auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
@ -189,8 +175,6 @@ RequestResult RequestHandler::CallVendorRequest(const Request &request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["vendorName"] = vendorName;
|
|
||||||
responseData["requestType"] = requestType;
|
|
||||||
responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
|
responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
@ -282,23 +266,19 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request &request)
|
|||||||
|
|
||||||
const json keyModifiersJson = request.RequestData["keyModifiers"];
|
const json keyModifiersJson = request.RequestData["keyModifiers"];
|
||||||
uint32_t keyModifiers = 0;
|
uint32_t keyModifiers = 0;
|
||||||
if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() &&
|
if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() && keyModifiersJson["shift"].get<bool>())
|
||||||
keyModifiersJson["shift"].get<bool>())
|
|
||||||
keyModifiers |= INTERACT_SHIFT_KEY;
|
keyModifiers |= INTERACT_SHIFT_KEY;
|
||||||
if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() &&
|
if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() && keyModifiersJson["control"].get<bool>())
|
||||||
keyModifiersJson["control"].get<bool>())
|
|
||||||
keyModifiers |= INTERACT_CONTROL_KEY;
|
keyModifiers |= INTERACT_CONTROL_KEY;
|
||||||
if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
|
if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
|
||||||
keyModifiers |= INTERACT_ALT_KEY;
|
keyModifiers |= INTERACT_ALT_KEY;
|
||||||
if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() &&
|
if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() && keyModifiersJson["command"].get<bool>())
|
||||||
keyModifiersJson["command"].get<bool>())
|
|
||||||
keyModifiers |= INTERACT_COMMAND_KEY;
|
keyModifiers |= INTERACT_COMMAND_KEY;
|
||||||
combo.modifiers = keyModifiers;
|
combo.modifiers = keyModifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
|
if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
|
||||||
return RequestResult::Error(RequestStatus::CannotAct,
|
return RequestResult::Error(RequestStatus::CannotAct, "Your provided request fields cannot be used to trigger a hotkey.");
|
||||||
"Your provided request fields cannot be used to trigger a hotkey.");
|
|
||||||
|
|
||||||
// Apparently things break when you don't start by setting the combo to false
|
// Apparently things break when you don't start by setting the combo to false
|
||||||
obs_hotkey_inject_event(combo, false);
|
obs_hotkey_inject_event(combo, false);
|
||||||
|
@ -83,43 +83,6 @@ RequestResult RequestHandler::GetInputKindList(const Request &request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the names of all special inputs.
|
|
||||||
*
|
|
||||||
* @responseField desktop1 | String | Name of the Desktop Audio input
|
|
||||||
* @responseField desktop2 | String | Name of the Desktop Audio 2 input
|
|
||||||
* @responseField mic1 | String | Name of the Mic/Auxiliary Audio input
|
|
||||||
* @responseField mic2 | String | Name of the Mic/Auxiliary Audio 2 input
|
|
||||||
* @responseField mic3 | String | Name of the Mic/Auxiliary Audio 3 input
|
|
||||||
* @responseField mic4 | String | Name of the Mic/Auxiliary Audio 4 input
|
|
||||||
*
|
|
||||||
* @requestType GetSpecialInputs
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category inputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetSpecialInputs(const Request &)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
|
|
||||||
std::vector<std::string> channels = {"desktop1", "desktop2", "mic1", "mic2", "mic3", "mic4"};
|
|
||||||
|
|
||||||
size_t channelId = 1;
|
|
||||||
for (auto &channel : channels) {
|
|
||||||
OBSSourceAutoRelease input = obs_get_output_source(channelId);
|
|
||||||
if (!input)
|
|
||||||
responseData[channel] = nullptr;
|
|
||||||
else
|
|
||||||
responseData[channel] = obs_source_get_name(input);
|
|
||||||
|
|
||||||
channelId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new input, adding it as a scene item to the specified scene.
|
* Creates a new input, adding it as a scene item to the specified scene.
|
||||||
*
|
*
|
||||||
@ -143,8 +106,7 @@ RequestResult RequestHandler::CreateInput(const Request &request)
|
|||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
|
||||||
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) &&
|
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) && request.ValidateString("inputKind", statusCode, comment)))
|
||||||
request.ValidateString("inputKind", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string inputName = request.RequestData["inputName"];
|
std::string inputName = request.RequestData["inputName"];
|
||||||
@ -155,9 +117,7 @@ RequestResult RequestHandler::CreateInput(const Request &request)
|
|||||||
std::string inputKind = request.RequestData["inputKind"];
|
std::string inputKind = request.RequestData["inputKind"];
|
||||||
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
|
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
|
||||||
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
||||||
return RequestResult::Error(
|
return RequestResult::Error(RequestStatus::InvalidInputKind, "Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
|
||||||
RequestStatus::InvalidInputKind,
|
|
||||||
"Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
|
|
||||||
|
|
||||||
OBSDataAutoRelease inputSettings = nullptr;
|
OBSDataAutoRelease inputSettings = nullptr;
|
||||||
if (request.Contains("inputSettings")) {
|
if (request.Contains("inputSettings")) {
|
||||||
@ -178,8 +138,7 @@ RequestResult RequestHandler::CreateInput(const Request &request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the input and add it as a scene item to the destination scene
|
// Create the input and add it as a scene item to the destination scene
|
||||||
OBSSceneItemAutoRelease sceneItem =
|
OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
|
||||||
Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
|
|
||||||
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
|
||||||
@ -244,8 +203,7 @@ RequestResult RequestHandler::SetInputName(const Request &request)
|
|||||||
|
|
||||||
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str());
|
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str());
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new input name.");
|
||||||
"A source already exists by that new input name.");
|
|
||||||
|
|
||||||
obs_source_set_name(input, newInputName.c_str());
|
obs_source_set_name(input, newInputName.c_str());
|
||||||
|
|
||||||
@ -274,9 +232,6 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request &request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string inputKind = request.RequestData["inputKind"];
|
std::string inputKind = request.RequestData["inputKind"];
|
||||||
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
|
|
||||||
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidInputKind);
|
|
||||||
|
|
||||||
OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str());
|
OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str());
|
||||||
if (!defaultSettings)
|
if (!defaultSettings)
|
||||||
@ -354,8 +309,7 @@ RequestResult RequestHandler::SetInputSettings(const Request &request)
|
|||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
// This should never happen
|
// This should never happen
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
||||||
"An internal data conversion operation failed. Please report this!");
|
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
// Applies the new settings on top of the existing user settings
|
// Applies the new settings on top of the existing user settings
|
||||||
@ -392,9 +346,6 @@ RequestResult RequestHandler::GetInputMute(const Request &request)
|
|||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["inputMuted"] = obs_source_muted(input);
|
responseData["inputMuted"] = obs_source_muted(input);
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
@ -421,9 +372,6 @@ RequestResult RequestHandler::SetInputMute(const Request &request)
|
|||||||
if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
|
if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
obs_source_set_muted(input, request.RequestData["inputMuted"]);
|
obs_source_set_muted(input, request.RequestData["inputMuted"]);
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -451,9 +399,6 @@ RequestResult RequestHandler::ToggleInputMute(const Request &request)
|
|||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
bool inputMuted = !obs_source_muted(input);
|
bool inputMuted = !obs_source_muted(input);
|
||||||
obs_source_set_muted(input, inputMuted);
|
obs_source_set_muted(input, inputMuted);
|
||||||
|
|
||||||
@ -485,9 +430,6 @@ RequestResult RequestHandler::GetInputVolume(const Request &request)
|
|||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
float inputVolumeMul = obs_source_get_volume(input);
|
float inputVolumeMul = obs_source_get_volume(input);
|
||||||
float inputVolumeDb = obs_mul_to_db(inputVolumeMul);
|
float inputVolumeDb = obs_mul_to_db(inputVolumeMul);
|
||||||
if (inputVolumeDb == -INFINITY)
|
if (inputVolumeDb == -INFINITY)
|
||||||
@ -504,7 +446,7 @@ RequestResult RequestHandler::GetInputVolume(const Request &request)
|
|||||||
*
|
*
|
||||||
* @requestField inputName | String | Name of the input to set the volume of
|
* @requestField inputName | String | Name of the input to set the volume of
|
||||||
* @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified
|
* @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified
|
||||||
* @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= 26 | `inputVolumeMul` should be specified
|
* @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= -26 | `inputVolumeMul` should be specified
|
||||||
*
|
*
|
||||||
* @requestType SetInputVolume
|
* @requestType SetInputVolume
|
||||||
* @complexity 3
|
* @complexity 3
|
||||||
@ -521,9 +463,6 @@ RequestResult RequestHandler::SetInputVolume(const Request &request)
|
|||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
bool hasMul = request.Contains("inputVolumeMul");
|
bool hasMul = request.Contains("inputVolumeMul");
|
||||||
if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
|
if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
@ -549,67 +488,6 @@ RequestResult RequestHandler::SetInputVolume(const Request &request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the audio balance of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input to get the audio balance of
|
|
||||||
*
|
|
||||||
* @responseField inputAudioBalance | Number | Audio balance value from 0.0-1.0
|
|
||||||
*
|
|
||||||
* @requestType GetInputAudioBalance
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category inputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetInputAudioBalance(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!input)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["inputAudioBalance"] = obs_source_get_balance_value(input);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the audio balance of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input to set the audio balance of
|
|
||||||
* @requestField inputAudioBalance | Number | New audio balance value | >= 0.0, <= 1.0
|
|
||||||
*
|
|
||||||
* @requestType SetInputAudioBalance
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category inputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetInputAudioBalance(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
float inputAudioBalance = request.RequestData["inputAudioBalance"];
|
|
||||||
obs_source_set_balance_value(input, inputAudioBalance);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the audio sync offset of an input.
|
* Gets the audio sync offset of an input.
|
||||||
*
|
*
|
||||||
@ -634,9 +512,6 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request)
|
|||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
// Offset is stored in nanoseconds in OBS.
|
// Offset is stored in nanoseconds in OBS.
|
||||||
responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
|
responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
|
||||||
@ -665,9 +540,6 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
|
|||||||
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
|
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
int64_t syncOffset = request.RequestData["inputAudioSyncOffset"];
|
int64_t syncOffset = request.RequestData["inputAudioSyncOffset"];
|
||||||
obs_source_set_sync_offset(input, syncOffset * 1000000);
|
obs_source_set_sync_offset(input, syncOffset * 1000000);
|
||||||
|
|
||||||
@ -678,7 +550,6 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
|
|||||||
* Gets the audio monitor type of an input.
|
* Gets the audio monitor type of an input.
|
||||||
*
|
*
|
||||||
* The available audio monitor types are:
|
* The available audio monitor types are:
|
||||||
*
|
|
||||||
* - `OBS_MONITORING_TYPE_NONE`
|
* - `OBS_MONITORING_TYPE_NONE`
|
||||||
* - `OBS_MONITORING_TYPE_MONITOR_ONLY`
|
* - `OBS_MONITORING_TYPE_MONITOR_ONLY`
|
||||||
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
|
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
|
||||||
@ -702,11 +573,8 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request)
|
|||||||
if (!input)
|
if (!input)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["monitorType"] = obs_source_get_monitoring_type(input);
|
responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -732,13 +600,6 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
|
|||||||
if (!(input && request.ValidateString("monitorType", statusCode, comment)))
|
if (!(input && request.ValidateString("monitorType", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
if (!obs_audio_monitoring_available())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
|
||||||
"Audio monitoring is not available on this platform.");
|
|
||||||
|
|
||||||
enum obs_monitoring_type monitorType;
|
enum obs_monitoring_type monitorType;
|
||||||
std::string monitorTypeString = request.RequestData["monitorType"];
|
std::string monitorTypeString = request.RequestData["monitorType"];
|
||||||
if (monitorTypeString == "OBS_MONITORING_TYPE_NONE")
|
if (monitorTypeString == "OBS_MONITORING_TYPE_NONE")
|
||||||
@ -748,104 +609,13 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
|
|||||||
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
|
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
|
||||||
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
|
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
return RequestResult::Error(RequestStatus::InvalidRequestField, std::string("Unknown monitor type: ") + monitorTypeString);
|
||||||
std::string("Unknown monitor type: ") + monitorTypeString);
|
|
||||||
|
|
||||||
obs_source_set_monitoring_type(input, monitorType);
|
obs_source_set_monitoring_type(input, monitorType);
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the enable state of all audio tracks of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input
|
|
||||||
*
|
|
||||||
* @responseField inputAudioTracks | Object | Object of audio tracks and associated enable states
|
|
||||||
*
|
|
||||||
* @requestType GetInputAudioTracks
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category inputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetInputAudioTracks(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!input)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
long long tracks = obs_source_get_audio_mixers(input);
|
|
||||||
|
|
||||||
json inputAudioTracks;
|
|
||||||
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
|
|
||||||
inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["inputAudioTracks"] = inputAudioTracks;
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the enable state of audio tracks of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input
|
|
||||||
* @requestField inputAudioTracks | Object | Track settings to apply
|
|
||||||
*
|
|
||||||
* @requestType SetInputAudioTracks
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category inputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
|
||||||
|
|
||||||
json inputAudioTracks = request.RequestData["inputAudioTracks"];
|
|
||||||
|
|
||||||
long long mixers = obs_source_get_audio_mixers(input);
|
|
||||||
|
|
||||||
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
|
|
||||||
std::string track = std::to_string(i + 1);
|
|
||||||
|
|
||||||
if (!Utils::Json::Contains(inputAudioTracks, track))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!inputAudioTracks[track].is_boolean())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType,
|
|
||||||
"The value of one of your tracks is not a boolean.");
|
|
||||||
|
|
||||||
bool enabled = inputAudioTracks[track];
|
|
||||||
|
|
||||||
if (enabled)
|
|
||||||
mixers |= (1 << i);
|
|
||||||
else
|
|
||||||
mixers &= ~(1 << i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decided that checking if tracks have actually changed is unnecessary
|
|
||||||
obs_source_set_audio_mixers(input, mixers);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the items of a list property from an input's properties.
|
* Gets the items of a list property from an input's properties.
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,6 @@ bool IsMediaTimeValid(obs_source_t *input)
|
|||||||
* Gets the status of a media input.
|
* Gets the status of a media input.
|
||||||
*
|
*
|
||||||
* Media States:
|
* Media States:
|
||||||
*
|
|
||||||
* - `OBS_MEDIA_STATE_NONE`
|
* - `OBS_MEDIA_STATE_NONE`
|
||||||
* - `OBS_MEDIA_STATE_PLAYING`
|
* - `OBS_MEDIA_STATE_PLAYING`
|
||||||
* - `OBS_MEDIA_STATE_OPENING`
|
* - `OBS_MEDIA_STATE_OPENING`
|
||||||
@ -61,8 +60,7 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request &request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["mediaState"] = obs_source_media_get_state(input);
|
responseData["mediaState"] = Utils::Obs::StringHelper::GetMediaInputState(input);
|
||||||
;
|
|
||||||
|
|
||||||
if (IsMediaTimeValid(input)) {
|
if (IsMediaTimeValid(input)) {
|
||||||
responseData["mediaDuration"] = obs_source_media_get_duration(input);
|
responseData["mediaDuration"] = obs_source_media_get_duration(input);
|
||||||
@ -99,8 +97,7 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request &request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!IsMediaTimeValid(input))
|
if (!IsMediaTimeValid(input))
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
|
||||||
"The media input must be playing or paused in order to set the cursor position.");
|
|
||||||
|
|
||||||
int64_t mediaCursor = request.RequestData["mediaCursor"];
|
int64_t mediaCursor = request.RequestData["mediaCursor"];
|
||||||
|
|
||||||
@ -134,8 +131,7 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request &request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!IsMediaTimeValid(input))
|
if (!IsMediaTimeValid(input))
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
|
||||||
"The media input must be playing or paused in order to set the cursor position.");
|
|
||||||
|
|
||||||
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
|
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
|
||||||
int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
|
int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
|
||||||
@ -169,13 +165,13 @@ RequestResult RequestHandler::TriggerMediaInputAction(const Request &request)
|
|||||||
if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
|
if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
enum ObsMediaInputAction mediaAction = request.RequestData["mediaAction"];
|
std::string mediaActionString = request.RequestData["mediaAction"];
|
||||||
|
auto mediaAction = Utils::Obs::EnumHelper::GetMediaInputAction(mediaActionString);
|
||||||
|
|
||||||
switch (mediaAction) {
|
switch (mediaAction) {
|
||||||
default:
|
default:
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
return RequestResult::Error(RequestStatus::InvalidRequestField, "You have specified an invalid media input action.");
|
||||||
"You have specified an invalid media input action.");
|
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
|
||||||
// Shoutout to whoever implemented this API call like this
|
// Shoutout to whoever implemented this API call like this
|
||||||
obs_source_media_play_pause(input, false);
|
obs_source_media_play_pause(input, false);
|
||||||
|
@ -1,488 +0,0 @@
|
|||||||
/*
|
|
||||||
obs-websocket
|
|
||||||
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
|
|
||||||
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "RequestHandler.h"
|
|
||||||
|
|
||||||
static bool VirtualCamAvailable()
|
|
||||||
{
|
|
||||||
OBSDataAutoRelease privateData = obs_get_private_data();
|
|
||||||
if (!privateData)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return obs_data_get_bool(privateData, "vcamEnabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ReplayBufferAvailable()
|
|
||||||
{
|
|
||||||
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
|
|
||||||
return output != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the status of the virtualcam output.
|
|
||||||
*
|
|
||||||
* @responseField outputActive | Boolean | Whether the output is active
|
|
||||||
*
|
|
||||||
* @requestType GetVirtualCamStatus
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category outputs
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetVirtualCamStatus(const Request &)
|
|
||||||
{
|
|
||||||
if (!VirtualCamAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputActive"] = obs_frontend_virtualcam_active();
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the state of the virtualcam output.
|
|
||||||
*
|
|
||||||
* @responseField outputActive | Boolean | Whether the output is active
|
|
||||||
*
|
|
||||||
* @requestType ToggleVirtualCam
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category outputs
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::ToggleVirtualCam(const Request &)
|
|
||||||
{
|
|
||||||
if (!VirtualCamAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
|
||||||
|
|
||||||
bool outputActive = obs_frontend_virtualcam_active();
|
|
||||||
|
|
||||||
if (outputActive)
|
|
||||||
obs_frontend_stop_virtualcam();
|
|
||||||
else
|
|
||||||
obs_frontend_start_virtualcam();
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputActive"] = !outputActive;
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the virtualcam output.
|
|
||||||
*
|
|
||||||
* @requestType StartVirtualCam
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::StartVirtualCam(const Request &)
|
|
||||||
{
|
|
||||||
if (!VirtualCamAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
|
||||||
|
|
||||||
if (obs_frontend_virtualcam_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
|
||||||
|
|
||||||
obs_frontend_start_virtualcam();
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the virtualcam output.
|
|
||||||
*
|
|
||||||
* @requestType StopVirtualCam
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::StopVirtualCam(const Request &)
|
|
||||||
{
|
|
||||||
if (!VirtualCamAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
|
|
||||||
|
|
||||||
if (!obs_frontend_virtualcam_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
|
||||||
|
|
||||||
obs_frontend_stop_virtualcam();
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the status of the replay buffer output.
|
|
||||||
*
|
|
||||||
* @responseField outputActive | Boolean | Whether the output is active
|
|
||||||
*
|
|
||||||
* @requestType GetReplayBufferStatus
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category outputs
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetReplayBufferStatus(const Request &)
|
|
||||||
{
|
|
||||||
if (!ReplayBufferAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputActive"] = obs_frontend_replay_buffer_active();
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the state of the replay buffer output.
|
|
||||||
*
|
|
||||||
* @responseField outputActive | Boolean | Whether the output is active
|
|
||||||
*
|
|
||||||
* @requestType ToggleReplayBuffer
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category outputs
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::ToggleReplayBuffer(const Request &)
|
|
||||||
{
|
|
||||||
if (!ReplayBufferAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
|
||||||
|
|
||||||
bool outputActive = obs_frontend_replay_buffer_active();
|
|
||||||
|
|
||||||
if (outputActive)
|
|
||||||
obs_frontend_replay_buffer_stop();
|
|
||||||
else
|
|
||||||
obs_frontend_replay_buffer_start();
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputActive"] = !outputActive;
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the replay buffer output.
|
|
||||||
*
|
|
||||||
* @requestType StartReplayBuffer
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::StartReplayBuffer(const Request &)
|
|
||||||
{
|
|
||||||
if (!ReplayBufferAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
|
||||||
|
|
||||||
if (obs_frontend_replay_buffer_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
|
||||||
|
|
||||||
obs_frontend_replay_buffer_start();
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the replay buffer output.
|
|
||||||
*
|
|
||||||
* @requestType StopReplayBuffer
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::StopReplayBuffer(const Request &)
|
|
||||||
{
|
|
||||||
if (!ReplayBufferAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
|
||||||
|
|
||||||
if (!obs_frontend_replay_buffer_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
|
||||||
|
|
||||||
obs_frontend_replay_buffer_stop();
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the contents of the replay buffer output.
|
|
||||||
*
|
|
||||||
* @requestType SaveReplayBuffer
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SaveReplayBuffer(const Request &)
|
|
||||||
{
|
|
||||||
if (!ReplayBufferAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
|
||||||
|
|
||||||
if (!obs_frontend_replay_buffer_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
|
||||||
|
|
||||||
obs_frontend_replay_buffer_save();
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the filename of the last replay buffer save file.
|
|
||||||
*
|
|
||||||
* @responseField savedReplayPath | String | File path
|
|
||||||
*
|
|
||||||
* @requestType GetLastReplayBufferReplay
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetLastReplayBufferReplay(const Request &)
|
|
||||||
{
|
|
||||||
if (!ReplayBufferAvailable())
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
|
|
||||||
|
|
||||||
if (!obs_frontend_replay_buffer_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the list of available outputs.
|
|
||||||
*
|
|
||||||
* @requestType GetOutputList
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetOutputList(const Request &)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
responseData["outputs"] = Utils::Obs::ArrayHelper::GetOutputList();
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the status of an output.
|
|
||||||
*
|
|
||||||
* @requestField outputName | String | Output name
|
|
||||||
*
|
|
||||||
* @responseField outputActive | Boolean | Whether the output is active
|
|
||||||
* @responseField outputReconnecting | Boolean | Whether the output is reconnecting
|
|
||||||
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
|
||||||
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
|
||||||
* @responseField outputCongestion | Number | Congestion of the output
|
|
||||||
* @responseField outputBytes | Number | Number of bytes sent by the output
|
|
||||||
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
|
|
||||||
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
|
|
||||||
*
|
|
||||||
* @requestType GetOutputStatus
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetOutputStatus(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
|
||||||
if (!output)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(output);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputActive"] = obs_output_active(output);
|
|
||||||
responseData["outputReconnecting"] = obs_output_reconnecting(output);
|
|
||||||
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
|
||||||
responseData["outputDuration"] = outputDuration;
|
|
||||||
responseData["outputCongestion"] = obs_output_get_congestion(output);
|
|
||||||
responseData["outputBytes"] = obs_output_get_total_bytes(output);
|
|
||||||
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(output);
|
|
||||||
responseData["outputTotalFrames"] = obs_output_get_total_frames(output);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the status of an output.
|
|
||||||
*
|
|
||||||
* @requestField outputName | String | Output name
|
|
||||||
*
|
|
||||||
* @responseField outputActive | Boolean | Whether the output is active
|
|
||||||
*
|
|
||||||
* @requestType ToggleOutput
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::ToggleOutput(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
|
||||||
if (!output)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
bool outputActive = obs_output_active(output);
|
|
||||||
if (outputActive)
|
|
||||||
obs_output_stop(output);
|
|
||||||
else
|
|
||||||
obs_output_start(output);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputActive"] = !outputActive;
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts an output.
|
|
||||||
*
|
|
||||||
* @requestField outputName | String | Output name
|
|
||||||
*
|
|
||||||
* @requestType StartOutput
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::StartOutput(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
|
||||||
if (!output)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (obs_output_active(output))
|
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning);
|
|
||||||
|
|
||||||
obs_output_start(output);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops an output.
|
|
||||||
*
|
|
||||||
* @requestField outputName | String | Output name
|
|
||||||
*
|
|
||||||
* @requestType StopOutput
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::StopOutput(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
|
||||||
if (!output)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!obs_output_active(output))
|
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
|
||||||
|
|
||||||
obs_output_stop(output);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the settings of an output.
|
|
||||||
*
|
|
||||||
* @requestField outputName | String | Output name
|
|
||||||
*
|
|
||||||
* @responseField outputSettings | Object | Output settings
|
|
||||||
*
|
|
||||||
* @requestType GetOutputSettings
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetOutputSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
|
||||||
if (!output)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease outputSettings = obs_output_get_settings(output);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["outputSettings"] = Utils::Json::ObsDataToJson(outputSettings);
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the settings of an output.
|
|
||||||
*
|
|
||||||
* @requestField outputName | String | Output name
|
|
||||||
* @requestField outputSettings | Object | Output settings
|
|
||||||
*
|
|
||||||
* @requestType SetOutputSettings
|
|
||||||
* @complexity 4
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category outputs
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetOutputSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
|
||||||
if (!(output && request.ValidateObject("outputSettings", statusCode, comment, true)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["outputSettings"]);
|
|
||||||
if (!newSettings)
|
|
||||||
// This should never happen
|
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
|
||||||
"An internal data conversion operation failed. Please report this!");
|
|
||||||
|
|
||||||
obs_output_update(output, newSettings);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
@ -99,8 +99,6 @@ RequestResult RequestHandler::StartRecord(const Request &)
|
|||||||
/**
|
/**
|
||||||
* Stops the record output.
|
* Stops the record output.
|
||||||
*
|
*
|
||||||
* @responseField outputPath | String | File name for the saved recording
|
|
||||||
*
|
|
||||||
* @requestType StopRecord
|
* @requestType StopRecord
|
||||||
* @complexity 1
|
* @complexity 1
|
||||||
* @rpcVersion -1
|
* @rpcVersion -1
|
||||||
@ -116,10 +114,7 @@ RequestResult RequestHandler::StopRecord(const Request &)
|
|||||||
// TODO: Call signal directly to perform blocking wait
|
// TODO: Call signal directly to perform blocking wait
|
||||||
obs_frontend_recording_stop();
|
obs_frontend_recording_stop();
|
||||||
|
|
||||||
json responseData;
|
return RequestResult::Success();
|
||||||
responseData["outputPath"] = Utils::Obs::StringHelper::GetLastRecordFileName();
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,3 +182,23 @@ RequestResult RequestHandler::ResumeRecord(const Request &)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current directory that the record output is set to.
|
||||||
|
*
|
||||||
|
* @responseField recordDirectory | String | Output directory
|
||||||
|
*
|
||||||
|
* @requestType GetRecordDirectory
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetRecordDirectory(const Request&)
|
||||||
|
{
|
||||||
|
json responseData;
|
||||||
|
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
||||||
|
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
@ -88,7 +88,6 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request &request)
|
|||||||
*
|
*
|
||||||
* @requestField sceneName | String | Name of the scene or group to search in
|
* @requestField sceneName | String | Name of the scene or group to search in
|
||||||
* @requestField sourceName | String | Name of the source to find
|
* @requestField sourceName | String | Name of the source to find
|
||||||
* @requestField ?searchOffset | Number | Number of matches to skip during search. >= 0 means first forward. -1 means last (top) item | >= -1 | 0
|
|
||||||
*
|
*
|
||||||
* @responseField sceneItemId | Number | Numeric ID of the scene item
|
* @responseField sceneItemId | Number | Numeric ID of the scene item
|
||||||
*
|
*
|
||||||
@ -103,24 +102,15 @@ RequestResult RequestHandler::GetSceneItemId(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneAutoRelease scene =
|
OBSSceneAutoRelease scene = request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
|
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string sourceName = request.RequestData["sourceName"];
|
std::string sourceName = request.RequestData["sourceName"];
|
||||||
|
|
||||||
int offset = 0;
|
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName);
|
||||||
if (request.Contains("searchOffset")) {
|
|
||||||
if (!request.ValidateOptionalNumber("searchOffset", statusCode, comment, -1))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
offset = request.RequestData["searchOffset"];
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset);
|
|
||||||
if (!item)
|
if (!item)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name.");
|
||||||
"No scene items were found in the specified scene by that name or offset.");
|
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemId"] = obs_sceneitem_get_id(item);
|
responseData["sceneItemId"] = obs_sceneitem_get_id(item);
|
||||||
@ -242,10 +232,8 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
|
|||||||
if (!destinationScene)
|
if (!destinationScene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
} else {
|
} else {
|
||||||
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
|
destinationScene = obs_sceneitem_get_scene(sceneItem);
|
||||||
if (!destinationScene)
|
obs_scene_addref(destinationScene);
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
|
||||||
"Internal error: Failed to get ref for scene of scene item.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
|
if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
|
||||||
@ -262,8 +250,7 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
|
|||||||
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
|
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
|
||||||
|
|
||||||
// Create the new item
|
// Create the new item
|
||||||
OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(
|
OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
|
||||||
sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
|
|
||||||
obs_scene_release(destinationScene);
|
obs_scene_release(destinationScene);
|
||||||
if (!newSceneItem)
|
if (!newSceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
|
||||||
@ -295,8 +282,7 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -324,8 +310,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
|
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -369,8 +354,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
|||||||
float scaleX = r.RequestData["scaleX"];
|
float scaleX = r.RequestData["scaleX"];
|
||||||
float finalWidth = scaleX * sourceWidth;
|
float finalWidth = scaleX * sourceWidth;
|
||||||
if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
|
if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
|
||||||
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
|
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleX is too small or large for the current source resolution.");
|
||||||
"The field `scaleX` is too small or large for the current source resolution.");
|
|
||||||
sceneItemTransform.scale.x = scaleX;
|
sceneItemTransform.scale.x = scaleX;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
@ -380,8 +364,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
|||||||
float scaleY = r.RequestData["scaleY"];
|
float scaleY = r.RequestData["scaleY"];
|
||||||
float finalHeight = scaleY * sourceHeight;
|
float finalHeight = scaleY * sourceHeight;
|
||||||
if (!(finalHeight > -90001.0 && finalHeight < 90001.0))
|
if (!(finalHeight > -90001.0 && finalHeight < 90001.0))
|
||||||
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
|
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleY is too small or large for the current source resolution.");
|
||||||
"The field `scaleY` is too small or large for the current source resolution.");
|
|
||||||
sceneItemTransform.scale.y = scaleY;
|
sceneItemTransform.scale.y = scaleY;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
@ -396,10 +379,10 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
|
|||||||
if (r.Contains("boundsType")) {
|
if (r.Contains("boundsType")) {
|
||||||
if (!r.ValidateOptionalString("boundsType", statusCode, comment))
|
if (!r.ValidateOptionalString("boundsType", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
enum obs_bounds_type boundsType = r.RequestData["boundsType"];
|
std::string boundsTypeString = r.RequestData["boundsType"];
|
||||||
if (boundsType == OBS_BOUNDS_NONE && r.RequestData["boundsType"] != "OBS_BOUNDS_NONE")
|
enum obs_bounds_type boundsType = Utils::Obs::EnumHelper::GetSceneItemBoundsType(boundsTypeString);
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
if (boundsType == OBS_BOUNDS_NONE && boundsTypeString != "OBS_BOUNDS_NONE")
|
||||||
"The field `boundsType` has an invalid value.");
|
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field boundsType has an invalid value.");
|
||||||
sceneItemTransform.bounds_type = boundsType;
|
sceneItemTransform.bounds_type = boundsType;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
@ -482,8 +465,7 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -513,8 +495,7 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
|
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -546,8 +527,7 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -577,8 +557,7 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
|
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -612,8 +591,7 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -643,8 +621,7 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
|
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -654,120 +631,3 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the blend mode of a scene item.
|
|
||||||
*
|
|
||||||
* Blend modes:
|
|
||||||
*
|
|
||||||
* - `OBS_BLEND_NORMAL`
|
|
||||||
* - `OBS_BLEND_ADDITIVE`
|
|
||||||
* - `OBS_BLEND_SUBTRACT`
|
|
||||||
* - `OBS_BLEND_SCREEN`
|
|
||||||
* - `OBS_BLEND_MULTIPLY`
|
|
||||||
* - `OBS_BLEND_LIGHTEN`
|
|
||||||
* - `OBS_BLEND_DARKEN`
|
|
||||||
*
|
|
||||||
* Scenes and Groups
|
|
||||||
*
|
|
||||||
* @requestField sceneName | String | Name of the scene the item is in
|
|
||||||
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
|
|
||||||
*
|
|
||||||
* @responseField sceneItemBlendMode | String | Current blend mode
|
|
||||||
*
|
|
||||||
* @requestType GetSceneItemBlendMode
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category scene items
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetSceneItemBlendMode(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["sceneItemBlendMode"] = blendMode;
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the blend mode of a scene item.
|
|
||||||
*
|
|
||||||
* Scenes and Groups
|
|
||||||
*
|
|
||||||
* @requestField sceneName | String | Name of the scene the item is in
|
|
||||||
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
|
|
||||||
* @requestField sceneItemBlendMode | String | New blend mode
|
|
||||||
*
|
|
||||||
* @requestType SetSceneItemBlendMode
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category scene items
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetSceneItemBlendMode(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
enum obs_blending_type blendMode = request.RequestData["sceneItemBlendMode"];
|
|
||||||
if (blendMode == OBS_BLEND_NORMAL && request.RequestData["sceneItemBlendMode"] != "OBS_BLEND_NORMAL")
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
|
||||||
"The field sceneItemBlendMode has an invalid value.");
|
|
||||||
|
|
||||||
obs_sceneitem_set_blending_mode(sceneItem, blendMode);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally undocumented
|
|
||||||
RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["sceneItemSettings"] = Utils::Json::ObsDataToJson(privateSettings);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally undocumented
|
|
||||||
RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
|
||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
|
||||||
if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_sceneitem_get_private_settings(sceneItem);
|
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sceneItemSettings"]);
|
|
||||||
|
|
||||||
// Always overlays to prevent destroying internal source unintentionally
|
|
||||||
obs_data_apply(privateSettings, newSettings);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -24,7 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*
|
*
|
||||||
* @responseField currentProgramSceneName | String | Current program scene
|
* @responseField currentProgramSceneName | String | Current program scene
|
||||||
* @responseField currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode
|
* @responseField currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode
|
||||||
* @responseField scenes | Array<Object> | Array of scenes
|
* @responseField scenes | Array<Object> | Array of scenes in OBS
|
||||||
*
|
*
|
||||||
* @requestType GetSceneList
|
* @requestType GetSceneList
|
||||||
* @complexity 2
|
* @complexity 2
|
||||||
@ -54,29 +54,6 @@ RequestResult RequestHandler::GetSceneList(const Request &)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an array of all groups in OBS.
|
|
||||||
*
|
|
||||||
* Groups in OBS are actually scenes, but renamed and modified. In obs-websocket, we treat them as scenes where we can.
|
|
||||||
*
|
|
||||||
* @responseField groups | Array<String> | Array of group names
|
|
||||||
*
|
|
||||||
* @requestType GetGroupList
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category scenes
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetGroupList(const Request &)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
|
|
||||||
responseData["groups"] = Utils::Obs::ArrayHelper::GetGroupList();
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current program scene.
|
* Gets the current program scene.
|
||||||
*
|
*
|
||||||
@ -235,8 +212,7 @@ RequestResult RequestHandler::RemoveScene(const Request &request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (Utils::Obs::NumberHelper::GetSceneCount() < 2)
|
if (Utils::Obs::NumberHelper::GetSceneCount() < 2)
|
||||||
return RequestResult::Error(RequestStatus::NotEnoughResources,
|
return RequestResult::Error(RequestStatus::NotEnoughResources, "You cannot remove the last scene in the collection.");
|
||||||
"You cannot remove the last scene in the collection.");
|
|
||||||
|
|
||||||
obs_source_remove(scene);
|
obs_source_remove(scene);
|
||||||
|
|
||||||
@ -268,114 +244,9 @@ RequestResult RequestHandler::SetSceneName(const Request &request)
|
|||||||
|
|
||||||
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str());
|
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str());
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new scene name.");
|
||||||
"A source already exists by that new scene name.");
|
|
||||||
|
|
||||||
obs_source_set_name(scene, newSceneName.c_str());
|
obs_source_set_name(scene, newSceneName.c_str());
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the scene transition overridden for a scene.
|
|
||||||
*
|
|
||||||
* @requestField sceneName | String | Name of the scene
|
|
||||||
*
|
|
||||||
* @responseField transitionName | String | Name of the overridden scene transition, else `null`
|
|
||||||
* @responseField transitionDuration | Number | Duration of the overridden scene transition, else `null`
|
|
||||||
*
|
|
||||||
* @requestType GetSceneSceneTransitionOverride
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category scenes
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
|
||||||
if (!scene)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
const char *transitionName = obs_data_get_string(privateSettings, "transition");
|
|
||||||
if (transitionName && strlen(transitionName))
|
|
||||||
responseData["transitionName"] = transitionName;
|
|
||||||
else
|
|
||||||
responseData["transitionName"] = nullptr;
|
|
||||||
|
|
||||||
if (obs_data_has_user_value(privateSettings, "transition_duration"))
|
|
||||||
responseData["transitionDuration"] = obs_data_get_int(privateSettings, "transition_duration");
|
|
||||||
else
|
|
||||||
responseData["transitionDuration"] = nullptr;
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the scene transition overridden for a scene.
|
|
||||||
*
|
|
||||||
* @requestField sceneName | String | Name of the scene
|
|
||||||
* @requestField ?transitionName | String | Name of the scene transition to use as override. Specify `null` to remove | Unchanged
|
|
||||||
* @requestField ?transitionDuration | Number | Duration to use for any overridden transition. Specify `null` to remove | >= 50, <= 20000 | Unchanged
|
|
||||||
*
|
|
||||||
* @requestType SetSceneSceneTransitionOverride
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category scenes
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
|
|
||||||
if (!scene)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
|
|
||||||
|
|
||||||
bool hasName = request.RequestData.contains("transitionName");
|
|
||||||
if (hasName && !request.RequestData["transitionName"].is_null()) {
|
|
||||||
if (!request.ValidateOptionalString("transitionName", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
OBSSourceAutoRelease transition =
|
|
||||||
Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
|
|
||||||
if (!transition)
|
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasDuration = request.RequestData.contains("transitionDuration");
|
|
||||||
if (hasDuration && !request.RequestData["transitionDuration"].is_null()) {
|
|
||||||
if (!request.ValidateOptionalNumber("transitionDuration", statusCode, comment, 50, 20000))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasName && !hasDuration)
|
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestField,
|
|
||||||
"Your request data must include either `transitionName` or `transitionDuration`.");
|
|
||||||
|
|
||||||
if (hasName) {
|
|
||||||
if (request.RequestData["transitionName"].is_null()) {
|
|
||||||
obs_data_erase(privateSettings, "transition");
|
|
||||||
} else {
|
|
||||||
std::string transitionName = request.RequestData["transitionName"];
|
|
||||||
obs_data_set_string(privateSettings, "transition", transitionName.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasDuration) {
|
|
||||||
if (request.RequestData["transitionDuration"].is_null()) {
|
|
||||||
obs_data_erase(privateSettings, "transition_duration");
|
|
||||||
} else {
|
|
||||||
obs_data_set_int(privateSettings, "transition_duration", request.RequestData["transitionDuration"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -180,8 +180,7 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request &request)
|
|||||||
std::string imageFormat = request.RequestData["imageFormat"];
|
std::string imageFormat = request.RequestData["imageFormat"];
|
||||||
|
|
||||||
if (!IsImageFormatValid(imageFormat))
|
if (!IsImageFormatValid(imageFormat))
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
|
||||||
"Your specified image format is invalid or not supported by this system.");
|
|
||||||
|
|
||||||
uint32_t requestedWidth{0};
|
uint32_t requestedWidth{0};
|
||||||
uint32_t requestedHeight{0};
|
uint32_t requestedHeight{0};
|
||||||
@ -259,8 +258,7 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
|
|||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
||||||
if (!(source && request.ValidateString("imageFormat", statusCode, comment) &&
|
if (!(source && request.ValidateString("imageFormat", statusCode, comment) && request.ValidateString("imageFilePath", statusCode, comment)))
|
||||||
request.ValidateString("imageFilePath", statusCode, comment)))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
||||||
@ -270,8 +268,7 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
|
|||||||
std::string imageFilePath = request.RequestData["imageFilePath"];
|
std::string imageFilePath = request.RequestData["imageFilePath"];
|
||||||
|
|
||||||
if (!IsImageFormatValid(imageFormat))
|
if (!IsImageFormatValid(imageFormat))
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
|
||||||
"Your specified image format is invalid or not supported by this system.");
|
|
||||||
|
|
||||||
QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
|
QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
|
||||||
if (!filePathInfo.absoluteDir().exists())
|
if (!filePathInfo.absoluteDir().exists())
|
||||||
@ -315,39 +312,3 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally undocumented
|
|
||||||
RequestResult RequestHandler::GetSourcePrivateSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
|
||||||
if (!source)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["sourceSettings"] = Utils::Json::ObsDataToJson(privateSettings);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally undocumented
|
|
||||||
RequestResult RequestHandler::SetSourcePrivateSettings(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
|
||||||
if (!source || !request.ValidateObject("sourceSettings", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
|
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sourceSettings"]);
|
|
||||||
|
|
||||||
// Always overlays to prevent destroying internal source data unintentionally
|
|
||||||
obs_data_apply(privateSettings, newSettings);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
|
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
|
||||||
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
||||||
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
||||||
* @responseField outputCongestion | Number | Congestion of the output
|
|
||||||
* @responseField outputBytes | Number | Number of bytes sent by the output
|
* @responseField outputBytes | Number | Number of bytes sent by the output
|
||||||
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
|
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
|
||||||
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
|
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
|
||||||
@ -49,7 +48,6 @@ RequestResult RequestHandler::GetStreamStatus(const Request &)
|
|||||||
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
|
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
|
||||||
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
||||||
responseData["outputDuration"] = outputDuration;
|
responseData["outputDuration"] = outputDuration;
|
||||||
responseData["outputCongestion"] = obs_output_get_congestion(streamOutput);
|
|
||||||
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
|
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
|
||||||
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
|
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
|
||||||
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
|
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
|
||||||
@ -124,35 +122,3 @@ RequestResult RequestHandler::StopStream(const Request &)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends CEA-608 caption text over the stream output.
|
|
||||||
*
|
|
||||||
* @requestField captionText | String | Caption text
|
|
||||||
*
|
|
||||||
* @requestType SendStreamCaption
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category stream
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SendStreamCaption(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
if (!request.ValidateString("captionText", statusCode, comment, true))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!obs_frontend_streaming_active())
|
|
||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
|
||||||
|
|
||||||
std::string captionText = request.RequestData["captionText"];
|
|
||||||
|
|
||||||
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
|
||||||
|
|
||||||
// 0.0 means no delay until the next caption can be sent
|
|
||||||
obs_output_output_caption_text2(output, captionText.c_str(), 0.0);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -17,8 +17,6 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,8 +93,7 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request &)
|
|||||||
{
|
{
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
"OBS does not currently have a scene transition set."); // This should not happen!
|
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["transitionName"] = obs_source_get_name(transition);
|
responseData["transitionName"] = obs_source_get_name(transition);
|
||||||
@ -202,12 +199,10 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request &r
|
|||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
"OBS does not currently have a scene transition set."); // This should not happen!
|
|
||||||
|
|
||||||
if (!obs_source_configurable(transition))
|
if (!obs_source_configurable(transition))
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotConfigurable,
|
return RequestResult::Error(RequestStatus::ResourceNotConfigurable, "The current transition does not support custom settings.");
|
||||||
"The current transition does not support custom settings.");
|
|
||||||
|
|
||||||
bool overlay = true;
|
bool overlay = true;
|
||||||
if (request.Contains("overlay")) {
|
if (request.Contains("overlay")) {
|
||||||
@ -219,8 +214,7 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request &r
|
|||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
||||||
"An internal data conversion operation failed. Please report this!");
|
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
obs_source_update(transition, newSettings);
|
obs_source_update(transition, newSettings);
|
||||||
@ -232,33 +226,6 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request &r
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the cursor position of the current scene transition.
|
|
||||||
*
|
|
||||||
* Note: `transitionCursor` will return 1.0 when the transition is inactive.
|
|
||||||
*
|
|
||||||
* @responseField transitionCursor | Number | Cursor position, between 0.0 and 1.0
|
|
||||||
*
|
|
||||||
* @requestType GetCurrentSceneTransitionCursor
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request &)
|
|
||||||
{
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
|
||||||
if (!transition)
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
|
||||||
"OBS does not currently have a scene transition set."); // This should not happen!
|
|
||||||
|
|
||||||
json responseData;
|
|
||||||
responseData["transitionCursor"] = obs_transition_get_time(transition);
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the current scene transition. Same functionality as the `Transition` button in studio mode.
|
* Triggers the current scene transition. Same functionality as the `Transition` button in studio mode.
|
||||||
*
|
*
|
||||||
@ -280,49 +247,3 @@ RequestResult RequestHandler::TriggerStudioModeTransition(const Request &)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the position of the TBar.
|
|
||||||
*
|
|
||||||
* **Very important note**: This will be deprecated and replaced in a future version of obs-websocket.
|
|
||||||
*
|
|
||||||
* @requestField position | Number | New position | >= 0.0, <= 1.0
|
|
||||||
* @requestField ?release | Boolean | Whether to release the TBar. Only set `false` if you know that you will be sending another position update | `true`
|
|
||||||
*
|
|
||||||
* @requestType SetTBarPosition
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api requests
|
|
||||||
* @category transitions
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetTBarPosition(const Request &request)
|
|
||||||
{
|
|
||||||
if (!obs_frontend_preview_program_mode_active())
|
|
||||||
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
if (!request.ValidateNumber("position", statusCode, comment, 0.0, 1.0))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
bool release = true;
|
|
||||||
if (request.Contains("release")) {
|
|
||||||
if (!request.ValidateOptionalBoolean("release", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
|
||||||
if (!transition)
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
|
||||||
"OBS does not currently have a scene transition set."); // This should not happen!
|
|
||||||
|
|
||||||
float position = request.RequestData["position"];
|
|
||||||
|
|
||||||
obs_frontend_set_tbar_position((int)round(position * 1024.0));
|
|
||||||
|
|
||||||
if (release)
|
|
||||||
obs_frontend_release_tbar();
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -17,11 +17,6 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QScreen>
|
|
||||||
#include <QRect>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,238 +62,11 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request &request)
|
|||||||
// (Bad) Create a boolean then pass it as a reference to the task. Requires `wait` in obs_queue_task() to be true, else undefined behavior
|
// (Bad) Create a boolean then pass it as a reference to the task. Requires `wait` in obs_queue_task() to be true, else undefined behavior
|
||||||
bool studioModeEnabled = request.RequestData["studioModeEnabled"];
|
bool studioModeEnabled = request.RequestData["studioModeEnabled"];
|
||||||
// Queue the task inside of the UI thread to prevent race conditions
|
// Queue the task inside of the UI thread to prevent race conditions
|
||||||
obs_queue_task(
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
OBS_TASK_UI,
|
|
||||||
[](void *param) {
|
|
||||||
auto studioModeEnabled = (bool*)param;
|
auto studioModeEnabled = (bool*)param;
|
||||||
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
||||||
},
|
}, &studioModeEnabled, true);
|
||||||
&studioModeEnabled, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the properties dialog of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input to open the dialog of
|
|
||||||
*
|
|
||||||
* @requestType OpenInputPropertiesDialog
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category ui
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::OpenInputPropertiesDialog(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!input)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
obs_frontend_open_source_properties(input);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the filters dialog of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input to open the dialog of
|
|
||||||
*
|
|
||||||
* @requestType OpenInputFiltersDialog
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category ui
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::OpenInputFiltersDialog(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!input)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
obs_frontend_open_source_filters(input);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the interact dialog of an input.
|
|
||||||
*
|
|
||||||
* @requestField inputName | String | Name of the input to open the dialog of
|
|
||||||
*
|
|
||||||
* @requestType OpenInputInteractDialog
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category ui
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::OpenInputInteractDialog(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
|
||||||
if (!input)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_INTERACTION))
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
|
||||||
"The specified input does not support interaction.");
|
|
||||||
|
|
||||||
obs_frontend_open_source_interaction(input);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a list of connected monitors and information about them.
|
|
||||||
*
|
|
||||||
* @responseField monitors | Array<Object> | a list of detected monitors with some information
|
|
||||||
*
|
|
||||||
* @requestType GetMonitorList
|
|
||||||
* @complexity 2
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category ui
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetMonitorList(const Request &)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
std::vector<json> monitorsData;
|
|
||||||
QList<QScreen *> screensList = QGuiApplication::screens();
|
|
||||||
for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++) {
|
|
||||||
json screenData;
|
|
||||||
QScreen const *screen = screensList[screenIndex];
|
|
||||||
std::stringstream nameAndIndex;
|
|
||||||
nameAndIndex << screen->name().toStdString();
|
|
||||||
nameAndIndex << '(' << screenIndex << ')';
|
|
||||||
screenData["monitorName"] = nameAndIndex.str();
|
|
||||||
screenData["monitorIndex"] = screenIndex;
|
|
||||||
const QRect screenGeometry = screen->geometry();
|
|
||||||
screenData["monitorWidth"] = screenGeometry.width();
|
|
||||||
screenData["monitorHeight"] = screenGeometry.height();
|
|
||||||
screenData["monitorPositionX"] = screenGeometry.x();
|
|
||||||
screenData["monitorPositionY"] = screenGeometry.y();
|
|
||||||
monitorsData.push_back(screenData);
|
|
||||||
}
|
|
||||||
responseData["monitors"] = monitorsData;
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a projector for a specific output video mix.
|
|
||||||
*
|
|
||||||
* Mix types:
|
|
||||||
*
|
|
||||||
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW`
|
|
||||||
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM`
|
|
||||||
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW`
|
|
||||||
*
|
|
||||||
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
|
|
||||||
*
|
|
||||||
* @requestField videoMixType | String | Type of mix to open
|
|
||||||
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
|
|
||||||
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
|
|
||||||
*
|
|
||||||
* @requestType OpenVideoMixProjector
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category ui
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
if (!request.ValidateString("videoMixType", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
std::string videoMixType = request.RequestData["videoMixType"];
|
|
||||||
const char *projectorType;
|
|
||||||
if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW")
|
|
||||||
projectorType = "Preview";
|
|
||||||
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM")
|
|
||||||
projectorType = "Program";
|
|
||||||
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")
|
|
||||||
projectorType = "Multiview";
|
|
||||||
else
|
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
|
||||||
"The field `videoMixType` has an invalid enum value.");
|
|
||||||
|
|
||||||
int monitorIndex = -1;
|
|
||||||
if (request.Contains("monitorIndex")) {
|
|
||||||
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
monitorIndex = request.RequestData["monitorIndex"];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string projectorGeometry;
|
|
||||||
if (request.Contains("projectorGeometry")) {
|
|
||||||
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
if (monitorIndex != -1)
|
|
||||||
return RequestResult::Error(RequestStatus::TooManyRequestFields,
|
|
||||||
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
|
|
||||||
projectorGeometry = request.RequestData["projectorGeometry"];
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_open_projector(projectorType, monitorIndex, projectorGeometry.c_str(), nullptr);
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a projector for a source.
|
|
||||||
*
|
|
||||||
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
|
|
||||||
*
|
|
||||||
* @requestField sourceName | String | Name of the source to open a projector for
|
|
||||||
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
|
|
||||||
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
|
|
||||||
*
|
|
||||||
* @requestType OpenSourceProjector
|
|
||||||
* @complexity 3
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category ui
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::OpenSourceProjector(const Request &request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
|
||||||
if (!source)
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
int monitorIndex = -1;
|
|
||||||
if (request.Contains("monitorIndex")) {
|
|
||||||
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
monitorIndex = request.RequestData["monitorIndex"];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string projectorGeometry;
|
|
||||||
if (request.Contains("projectorGeometry")) {
|
|
||||||
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
if (monitorIndex != -1)
|
|
||||||
return RequestResult::Error(RequestStatus::TooManyRequestFields,
|
|
||||||
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
|
|
||||||
projectorGeometry = request.RequestData["projectorGeometry"];
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_open_projector("Source", monitorIndex, projectorGeometry.c_str(), obs_source_get_name(source));
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
@ -29,9 +29,8 @@ json GetDefaultJsonObject(const json &requestData)
|
|||||||
return requestData;
|
return requestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
Request::Request(const std::string &requestType, const json &requestData,
|
Request::Request(const std::string &requestType, const json &requestData, const RequestBatchExecutionType::RequestBatchExecutionType executionType) :
|
||||||
const RequestBatchExecutionType::RequestBatchExecutionType executionType)
|
RequestType(requestType),
|
||||||
: RequestType(requestType),
|
|
||||||
HasRequestData(requestData.is_object()),
|
HasRequestData(requestData.is_object()),
|
||||||
RequestData(GetDefaultJsonObject(requestData)),
|
RequestData(GetDefaultJsonObject(requestData)),
|
||||||
ExecutionType(executionType)
|
ExecutionType(executionType)
|
||||||
@ -60,8 +59,7 @@ bool Request::ValidateBasic(const std::string &keyName, RequestStatus::RequestSt
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
|
||||||
const double minValue, const double maxValue) const
|
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_number()) {
|
if (!RequestData[keyName].is_number()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -72,22 +70,19 @@ bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::
|
|||||||
double value = RequestData[keyName];
|
double value = RequestData[keyName];
|
||||||
if (value < minValue) {
|
if (value < minValue) {
|
||||||
statusCode = RequestStatus::RequestFieldOutOfRange;
|
statusCode = RequestStatus::RequestFieldOutOfRange;
|
||||||
comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) +
|
comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) + "`";
|
||||||
"`";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (value > maxValue) {
|
if (value > maxValue) {
|
||||||
statusCode = RequestStatus::RequestFieldOutOfRange;
|
statusCode = RequestStatus::RequestFieldOutOfRange;
|
||||||
comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) +
|
comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) + "`";
|
||||||
"`";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
|
||||||
const double minValue, const double maxValue) const
|
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -98,8 +93,7 @@ bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
||||||
const bool allowEmpty) const
|
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_string()) {
|
if (!RequestData[keyName].is_string()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -116,8 +110,7 @@ bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
||||||
const bool allowEmpty) const
|
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -128,8 +121,7 @@ bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
||||||
std::string &comment) const
|
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_boolean()) {
|
if (!RequestData[keyName].is_boolean()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -151,8 +143,7 @@ bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::Request
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
||||||
const bool allowEmpty) const
|
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_object()) {
|
if (!RequestData[keyName].is_object()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -169,8 +160,7 @@ bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
||||||
const bool allowEmpty) const
|
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -181,8 +171,7 @@ bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
||||||
const bool allowEmpty) const
|
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_array()) {
|
if (!RequestData[keyName].is_array()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -199,8 +188,7 @@ bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::R
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
||||||
const bool allowEmpty) const
|
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -211,8 +199,7 @@ bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestSt
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
||||||
std::string &comment) const
|
|
||||||
{
|
{
|
||||||
if (!ValidateString(keyName, statusCode, comment))
|
if (!ValidateString(keyName, statusCode, comment))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -229,8 +216,7 @@ obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
||||||
const ObsWebSocketSceneFilter filter) const
|
|
||||||
{
|
{
|
||||||
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -259,8 +245,7 @@ obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
||||||
const ObsWebSocketSceneFilter filter) const
|
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
|
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!sceneSource)
|
if (!sceneSource)
|
||||||
@ -279,19 +264,22 @@ obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::
|
|||||||
comment = "The specified source is not a scene. (Is group)";
|
comment = "The specified source is not a scene. (Is group)";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return obs_scene_get_ref(obs_group_from_source(sceneSource));
|
OBSScene ret = obs_group_from_source(sceneSource);
|
||||||
|
obs_scene_addref(ret);
|
||||||
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY) {
|
if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY) {
|
||||||
statusCode = RequestStatus::InvalidResourceType;
|
statusCode = RequestStatus::InvalidResourceType;
|
||||||
comment = "The specified source is not a group. (Is scene)";
|
comment = "The specified source is not a group. (Is scene)";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return obs_scene_get_ref(obs_scene_from_source(sceneSource));
|
OBSScene ret = obs_scene_from_source(sceneSource);
|
||||||
|
obs_scene_addref(ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
||||||
std::string &comment) const
|
|
||||||
{
|
{
|
||||||
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -307,32 +295,7 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
|
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
||||||
RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
|
||||||
{
|
|
||||||
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
|
|
||||||
if (!source)
|
|
||||||
return FilterPair{source, nullptr};
|
|
||||||
|
|
||||||
if (!ValidateString(filterKeyName, statusCode, comment))
|
|
||||||
return FilterPair{source, nullptr};
|
|
||||||
|
|
||||||
std::string filterName = RequestData[filterKeyName];
|
|
||||||
|
|
||||||
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
|
|
||||||
if (!filter) {
|
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
|
||||||
comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() +
|
|
||||||
"` with the name `" + filterName + "`.";
|
|
||||||
return FilterPair{source, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
return FilterPair{source, filter};
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
|
|
||||||
RequestStatus::RequestStatus &statusCode, std::string &comment,
|
|
||||||
const ObsWebSocketSceneFilter filter) const
|
|
||||||
{
|
{
|
||||||
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -346,29 +309,10 @@ obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, con
|
|||||||
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
|
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
|
||||||
if (!sceneItem) {
|
if (!sceneItem) {
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
statusCode = RequestStatus::ResourceNotFound;
|
||||||
comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() +
|
comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() + "` with the ID `" + std::to_string(sceneItemId) + "`.";
|
||||||
"` with the ID `" + std::to_string(sceneItemId) + "`.";
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_addref(sceneItem);
|
obs_sceneitem_addref(sceneItem);
|
||||||
return sceneItem;
|
return sceneItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_output_t *Request::ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
|
||||||
std::string &comment) const
|
|
||||||
{
|
|
||||||
if (!ValidateString(keyName, statusCode, comment))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
std::string outputName = RequestData[keyName];
|
|
||||||
|
|
||||||
obs_output_t *ret = obs_get_output_by_name(outputName.c_str());
|
|
||||||
if (!ret) {
|
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
|
||||||
comment = std::string("No output was found with the name `") + outputName + "`.";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
@ -29,56 +29,31 @@ enum ObsWebSocketSceneFilter {
|
|||||||
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP,
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We return filters as a pair because `obs_filter_get_parent()` is apparently volatile
|
struct Request
|
||||||
struct FilterPair {
|
{
|
||||||
OBSSourceAutoRelease source;
|
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
||||||
OBSSourceAutoRelease filter;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Request {
|
|
||||||
Request(const std::string &requestType, const json &requestData = nullptr,
|
|
||||||
const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
|
||||||
|
|
||||||
// Contains the key and is not null
|
// Contains the key and is not null
|
||||||
bool Contains(const std::string &keyName) const;
|
bool Contains(const std::string &keyName) const;
|
||||||
|
|
||||||
bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
||||||
const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
||||||
bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
||||||
const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
||||||
bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
const bool allowEmpty = false) const;
|
|
||||||
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
|
||||||
const bool allowEmpty = false) const;
|
|
||||||
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
|
||||||
std::string &comment) const;
|
|
||||||
bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
||||||
const bool allowEmpty = false) const;
|
bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
||||||
bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
||||||
const bool allowEmpty = false) const;
|
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
||||||
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
|
||||||
const bool allowEmpty = false) const;
|
|
||||||
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
|
||||||
const bool allowEmpty = false) const;
|
|
||||||
|
|
||||||
// All return values have incremented refcounts
|
// All return values have incremented refcounts
|
||||||
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
std::string &comment) const;
|
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
|
||||||
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
|
||||||
std::string &comment) const;
|
|
||||||
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
|
|
||||||
RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
|
||||||
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
|
|
||||||
RequestStatus::RequestStatus &statusCode, std::string &comment,
|
|
||||||
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
|
||||||
obs_output_t *ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
|
||||||
std::string &comment) const;
|
|
||||||
|
|
||||||
std::string RequestType;
|
std::string RequestType;
|
||||||
bool HasRequestData;
|
bool HasRequestData;
|
||||||
|
@ -18,9 +18,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestBatchRequest.h"
|
#include "RequestBatchRequest.h"
|
||||||
|
|
||||||
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData,
|
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables, const json &outputVariables) :
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
Request(requestType, requestData, executionType),
|
||||||
const json &inputVariables, const json &outputVariables)
|
InputVariables(inputVariables),
|
||||||
: Request(requestType, requestData, executionType), InputVariables(inputVariables), OutputVariables(outputVariables)
|
OutputVariables(outputVariables)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
|
|
||||||
struct RequestBatchRequest : Request {
|
struct RequestBatchRequest : Request {
|
||||||
RequestBatchRequest(const std::string &requestType, const json &requestData,
|
RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables = nullptr, const json &outputVariables = nullptr);
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
|
||||||
const json &inputVariables = nullptr, const json &outputVariables = nullptr);
|
|
||||||
|
|
||||||
json InputVariables;
|
json InputVariables;
|
||||||
json OutputVariables;
|
json OutputVariables;
|
||||||
|
@ -19,8 +19,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestResult.h"
|
#include "RequestResult.h"
|
||||||
|
|
||||||
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment)
|
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment) :
|
||||||
: StatusCode(statusCode), ResponseData(responseData), Comment(comment), SleepFrames(0)
|
StatusCode(statusCode),
|
||||||
|
ResponseData(responseData),
|
||||||
|
Comment(comment),
|
||||||
|
SleepFrames(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../types/RequestStatus.h"
|
#include "../types/RequestStatus.h"
|
||||||
#include "../../utils/Json.h"
|
#include "../../utils/Json.h"
|
||||||
|
|
||||||
struct RequestResult {
|
struct RequestResult
|
||||||
RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr,
|
{
|
||||||
std::string comment = "");
|
RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr, std::string comment = "");
|
||||||
static RequestResult Success(json responseData = nullptr);
|
static RequestResult Success(json responseData = nullptr);
|
||||||
static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = "");
|
static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = "");
|
||||||
RequestStatus::RequestStatus StatusCode;
|
RequestStatus::RequestStatus StatusCode;
|
||||||
|
@ -22,7 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace RequestBatchExecutionType {
|
namespace RequestBatchExecutionType {
|
||||||
enum RequestBatchExecutionType : int8_t {
|
enum RequestBatchExecutionType {
|
||||||
/**
|
/**
|
||||||
* Not a request batch.
|
* Not a request batch.
|
||||||
*
|
*
|
||||||
@ -77,5 +77,8 @@ namespace RequestBatchExecutionType {
|
|||||||
Parallel = 2,
|
Parallel = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(int8_t executionType) { return executionType >= None && executionType <= Parallel; }
|
inline bool IsValid(int executionType)
|
||||||
|
{
|
||||||
|
return executionType >= None && executionType <= Parallel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +263,7 @@ namespace RequestStatus {
|
|||||||
*/
|
*/
|
||||||
StudioModeNotActive = 506,
|
StudioModeNotActive = 506,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The resource was not found.
|
* The resource was not found.
|
||||||
*
|
*
|
||||||
@ -344,17 +345,6 @@ namespace RequestStatus {
|
|||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
ResourceNotConfigurable = 606,
|
ResourceNotConfigurable = 606,
|
||||||
/**
|
|
||||||
* The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind.
|
|
||||||
*
|
|
||||||
* @enumIdentifier InvalidFilterKind
|
|
||||||
* @enumValue 607
|
|
||||||
* @enumType RequestStatus
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @api enums
|
|
||||||
*/
|
|
||||||
InvalidFilterKind = 607,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creating the resource failed.
|
* Creating the resource failed.
|
||||||
|
@ -19,7 +19,10 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
|
||||||
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) : cb(std::move(func)) {}
|
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) :
|
||||||
|
cb(std::move(func))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Utils::Compat::StdFunctionRunnable::run()
|
void Utils::Compat::StdFunctionRunnable::run()
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,6 @@ namespace Utils {
|
|||||||
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
||||||
class StdFunctionRunnable : public QRunnable {
|
class StdFunctionRunnable : public QRunnable {
|
||||||
std::function<void()> cb;
|
std::function<void()> cb;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StdFunctionRunnable(std::function<void()> func);
|
StdFunctionRunnable(std::function<void()> func);
|
||||||
void run() override;
|
void run() override;
|
||||||
|
@ -63,7 +63,10 @@ bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string ch
|
|||||||
secretAndChallenge += QString::fromStdString(challenge);
|
secretAndChallenge += QString::fromStdString(challenge);
|
||||||
|
|
||||||
// Generate a SHA256 hash of secretAndChallenge
|
// Generate a SHA256 hash of secretAndChallenge
|
||||||
auto hash = QCryptographicHash::hash(secretAndChallenge.toUtf8(), QCryptographicHash::Algorithm::Sha256);
|
auto hash = QCryptographicHash::hash(
|
||||||
|
secretAndChallenge.toUtf8(),
|
||||||
|
QCryptographicHash::Algorithm::Sha256
|
||||||
|
);
|
||||||
|
|
||||||
// Encode the SHA256 hash to Base64
|
// Encode the SHA256 hash to Base64
|
||||||
std::string expectedAuthenticationString = hash.toBase64().toStdString();
|
std::string expectedAuthenticationString = hash.toBase64().toStdString();
|
||||||
|
@ -21,7 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
bool Utils::Json::JsonArrayIsValidObsArray(const json &j)
|
bool Utils::Json::JsonArrayIsValidObsArray(json j)
|
||||||
{
|
{
|
||||||
for (auto it : j) {
|
for (auto it : j) {
|
||||||
if (!it.is_object())
|
if (!it.is_object())
|
||||||
@ -168,7 +168,8 @@ json Utils::Json::ObsDataToJson(obs_data_t *d, bool includeDefault)
|
|||||||
case OBS_DATA_ARRAY:
|
case OBS_DATA_ARRAY:
|
||||||
set_json_array(&j, name, item, includeDefault);
|
set_json_array(&j, name, item, includeDefault);
|
||||||
break;
|
break;
|
||||||
default:;
|
default:
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +191,7 @@ bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool createNew)
|
bool Utils::Json::SetJsonFileContent(std::string fileName, json content, bool createNew)
|
||||||
{
|
{
|
||||||
std::string textContent = content.dump(2);
|
std::string textContent = content.dump(2);
|
||||||
return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);
|
return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);
|
||||||
|
@ -25,58 +25,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(obs_source_type, {
|
|
||||||
{OBS_SOURCE_TYPE_INPUT, "OBS_SOURCE_TYPE_INPUT"},
|
|
||||||
{OBS_SOURCE_TYPE_FILTER, "OBS_SOURCE_TYPE_FILTER"},
|
|
||||||
{OBS_SOURCE_TYPE_TRANSITION, "OBS_SOURCE_TYPE_TRANSITION"},
|
|
||||||
{OBS_SOURCE_TYPE_SCENE, "OBS_SOURCE_TYPE_SCENE"},
|
|
||||||
})
|
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(obs_monitoring_type,
|
|
||||||
{
|
|
||||||
{OBS_MONITORING_TYPE_NONE, "OBS_MONITORING_TYPE_NONE"},
|
|
||||||
{OBS_MONITORING_TYPE_MONITOR_ONLY, "OBS_MONITORING_TYPE_MONITOR_ONLY"},
|
|
||||||
{OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT, "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT"},
|
|
||||||
})
|
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(obs_media_state, {
|
|
||||||
{OBS_MEDIA_STATE_NONE, "OBS_MEDIA_STATE_NONE"},
|
|
||||||
{OBS_MEDIA_STATE_PLAYING, "OBS_MEDIA_STATE_PLAYING"},
|
|
||||||
{OBS_MEDIA_STATE_OPENING, "OBS_MEDIA_STATE_OPENING"},
|
|
||||||
{OBS_MEDIA_STATE_BUFFERING, "OBS_MEDIA_STATE_BUFFERING"},
|
|
||||||
{OBS_MEDIA_STATE_PAUSED, "OBS_MEDIA_STATE_PAUSED"},
|
|
||||||
{OBS_MEDIA_STATE_STOPPED, "OBS_MEDIA_STATE_STOPPED"},
|
|
||||||
{OBS_MEDIA_STATE_ENDED, "OBS_MEDIA_STATE_ENDED"},
|
|
||||||
{OBS_MEDIA_STATE_ERROR, "OBS_MEDIA_STATE_ERROR"},
|
|
||||||
})
|
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(obs_bounds_type, {
|
|
||||||
{OBS_BOUNDS_NONE, "OBS_BOUNDS_NONE"},
|
|
||||||
{OBS_BOUNDS_STRETCH, "OBS_BOUNDS_STRETCH"},
|
|
||||||
{OBS_BOUNDS_SCALE_INNER, "OBS_BOUNDS_SCALE_INNER"},
|
|
||||||
{OBS_BOUNDS_SCALE_OUTER, "OBS_BOUNDS_SCALE_OUTER"},
|
|
||||||
{OBS_BOUNDS_SCALE_TO_WIDTH, "OBS_BOUNDS_SCALE_TO_WIDTH"},
|
|
||||||
{OBS_BOUNDS_SCALE_TO_HEIGHT, "OBS_BOUNDS_SCALE_TO_HEIGHT"},
|
|
||||||
{OBS_BOUNDS_MAX_ONLY, "OBS_BOUNDS_MAX_ONLY"},
|
|
||||||
})
|
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(obs_blending_type, {
|
|
||||||
{OBS_BLEND_NORMAL, "OBS_BLEND_NORMAL"},
|
|
||||||
{OBS_BLEND_ADDITIVE, "OBS_BLEND_ADDITIVE"},
|
|
||||||
{OBS_BLEND_SUBTRACT, "OBS_BLEND_SUBTRACT"},
|
|
||||||
{OBS_BLEND_SCREEN, "OBS_BLEND_SCREEN"},
|
|
||||||
{OBS_BLEND_MULTIPLY, "OBS_BLEND_MULTIPLY"},
|
|
||||||
{OBS_BLEND_LIGHTEN, "OBS_BLEND_LIGHTEN"},
|
|
||||||
{OBS_BLEND_DARKEN, "OBS_BLEND_DARKEN"},
|
|
||||||
})
|
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Json {
|
namespace Json {
|
||||||
bool JsonArrayIsValidObsArray(const json &j);
|
bool JsonArrayIsValidObsArray(json j);
|
||||||
obs_data_t *JsonToObsData(json j);
|
obs_data_t *JsonToObsData(json j);
|
||||||
json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
|
json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
|
||||||
bool GetJsonFileContent(std::string fileName, json &content);
|
bool GetJsonFileContent(std::string fileName, json &content);
|
||||||
bool SetJsonFileContent(std::string fileName, const json &content, bool createNew = true);
|
bool SetJsonFileContent(std::string fileName, json content, bool createNew = true);
|
||||||
static inline bool Contains(const json &j, std::string key) { return j.contains(key) && !j[key].is_null(); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,11 +59,10 @@ using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_d
|
|||||||
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
|
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename T> T *GetCalldataPointer(const calldata_t *data, const char *name)
|
template <typename T> T* GetCalldataPointer(const calldata_t *data, const char* name) {
|
||||||
{
|
|
||||||
void *ptr = nullptr;
|
void *ptr = nullptr;
|
||||||
calldata_get_ptr(data, name, &ptr);
|
calldata_get_ptr(data, name, &ptr);
|
||||||
return static_cast<T *>(ptr);
|
return reinterpret_cast<T*>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ObsOutputState {
|
enum ObsOutputState {
|
||||||
@ -77,16 +76,6 @@ enum ObsOutputState {
|
|||||||
OBS_WEBSOCKET_OUTPUT_RESUMED,
|
OBS_WEBSOCKET_OUTPUT_RESUMED,
|
||||||
};
|
};
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(ObsOutputState, {
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_UNKNOWN, "OBS_WEBSOCKET_OUTPUT_UNKNOWN"},
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_STARTING, "OBS_WEBSOCKET_OUTPUT_STARTING"},
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_STARTED, "OBS_WEBSOCKET_OUTPUT_STARTED"},
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_STOPPING, "OBS_WEBSOCKET_OUTPUT_STOPPING"},
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_STOPPED, "OBS_WEBSOCKET_OUTPUT_STOPPED"},
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_PAUSED, "OBS_WEBSOCKET_OUTPUT_PAUSED"},
|
|
||||||
{OBS_WEBSOCKET_OUTPUT_RESUMED, "OBS_WEBSOCKET_OUTPUT_RESUMED"},
|
|
||||||
})
|
|
||||||
|
|
||||||
enum ObsMediaInputAction {
|
enum ObsMediaInputAction {
|
||||||
/**
|
/**
|
||||||
* No action.
|
* No action.
|
||||||
@ -160,17 +149,6 @@ enum ObsMediaInputAction {
|
|||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(ObsMediaInputAction,
|
|
||||||
{
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE"},
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY"},
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE"},
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP"},
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART"},
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT"},
|
|
||||||
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS"},
|
|
||||||
})
|
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Obs {
|
namespace Obs {
|
||||||
namespace StringHelper {
|
namespace StringHelper {
|
||||||
@ -179,15 +157,24 @@ namespace Utils {
|
|||||||
std::string GetCurrentProfile();
|
std::string GetCurrentProfile();
|
||||||
std::string GetCurrentProfilePath();
|
std::string GetCurrentProfilePath();
|
||||||
std::string GetCurrentRecordOutputPath();
|
std::string GetCurrentRecordOutputPath();
|
||||||
std::string GetLastRecordFileName();
|
std::string GetSourceType(obs_source_t *source);
|
||||||
std::string GetLastReplayBufferFileName();
|
std::string GetInputMonitorType(enum obs_monitoring_type monitorType);
|
||||||
|
std::string GetInputMonitorType(obs_source_t *input);
|
||||||
|
std::string GetMediaInputState(obs_source_t *input);
|
||||||
|
std::string GetLastReplayBufferFilePath();
|
||||||
|
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
||||||
std::string DurationToTimecode(uint64_t);
|
std::string DurationToTimecode(uint64_t);
|
||||||
|
std::string GetOutputState(ObsOutputState state);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace EnumHelper {
|
||||||
|
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
||||||
|
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NumberHelper {
|
namespace NumberHelper {
|
||||||
uint64_t GetOutputDuration(obs_output_t *output);
|
uint64_t GetOutputDuration(obs_output_t *output);
|
||||||
size_t GetSceneCount();
|
size_t GetSceneCount();
|
||||||
size_t GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ArrayHelper {
|
namespace ArrayHelper {
|
||||||
@ -196,16 +183,12 @@ namespace Utils {
|
|||||||
std::vector<obs_hotkey_t *> GetHotkeyList();
|
std::vector<obs_hotkey_t *> GetHotkeyList();
|
||||||
std::vector<std::string> GetHotkeyNameList();
|
std::vector<std::string> GetHotkeyNameList();
|
||||||
std::vector<json> GetSceneList();
|
std::vector<json> GetSceneList();
|
||||||
std::vector<std::string> GetGroupList();
|
|
||||||
std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false);
|
std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false);
|
||||||
std::vector<json> GetInputList(std::string inputKind = "");
|
std::vector<json> GetInputList(std::string inputKind = "");
|
||||||
std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false);
|
std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false);
|
||||||
std::vector<json> GetListPropertyItems(obs_property_t *property);
|
std::vector<json> GetListPropertyItems(obs_property_t *property);
|
||||||
std::vector<std::string> GetTransitionKindList();
|
std::vector<std::string> GetTransitionKindList();
|
||||||
std::vector<json> GetSceneTransitionList();
|
std::vector<json> GetSceneTransitionList();
|
||||||
std::vector<json> GetSourceFilterList(obs_source_t *source);
|
|
||||||
std::vector<std::string> GetFilterKindList();
|
|
||||||
std::vector<json> GetOutputList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ObjectHelper {
|
namespace ObjectHelper {
|
||||||
@ -216,22 +199,12 @@ namespace Utils {
|
|||||||
namespace SearchHelper {
|
namespace SearchHelper {
|
||||||
obs_hotkey_t *GetHotkeyByName(std::string name);
|
obs_hotkey_t *GetHotkeyByName(std::string name);
|
||||||
obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
|
obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
|
||||||
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name,
|
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ActionHelper {
|
namespace ActionHelper {
|
||||||
obs_sceneitem_t *
|
obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true, obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true,
|
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
obs_transform_info *sceneItemTransform = nullptr,
|
|
||||||
obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
|
|
||||||
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
|
|
||||||
obs_scene_t *scene,
|
|
||||||
bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
|
|
||||||
obs_source_t *
|
|
||||||
CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
|
|
||||||
obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
|
|
||||||
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,9 @@ struct CreateSceneItemData {
|
|||||||
OBSSceneItem sceneItem; // Out
|
OBSSceneItem sceneItem; // Out
|
||||||
};
|
};
|
||||||
|
|
||||||
static void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
||||||
{
|
{
|
||||||
auto *data = static_cast<CreateSceneItemData *>(_data);
|
auto *data = reinterpret_cast<CreateSceneItemData*>(_data);
|
||||||
data->sceneItem = obs_scene_add(scene, data->source);
|
data->sceneItem = obs_scene_add(scene, data->source);
|
||||||
|
|
||||||
if (data->sceneItemTransform)
|
if (data->sceneItemTransform)
|
||||||
@ -41,9 +41,7 @@ static void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
|||||||
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
|
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled,
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
|
||||||
obs_transform_info *sceneItemTransform,
|
|
||||||
obs_sceneitem_crop *sceneItemCrop)
|
|
||||||
{
|
{
|
||||||
// Sanity check for valid scene
|
// Sanity check for valid scene
|
||||||
if (!(source && scene))
|
if (!(source && scene))
|
||||||
@ -66,8 +64,7 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source,
|
|||||||
return data.sceneItem;
|
return data.sceneItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
|
||||||
obs_scene_t *scene, bool sceneItemEnabled)
|
|
||||||
{
|
{
|
||||||
// Create the input
|
// Create the input
|
||||||
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
||||||
@ -90,31 +87,3 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
|
|
||||||
obs_data_t *filterSettings)
|
|
||||||
{
|
|
||||||
obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
|
|
||||||
|
|
||||||
if (!filter)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
obs_source_filter_add(source, filter);
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index)
|
|
||||||
{
|
|
||||||
size_t currentIndex = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
|
|
||||||
obs_order_movement direction = index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP;
|
|
||||||
|
|
||||||
while (currentIndex != index) {
|
|
||||||
obs_source_filter_set_order(source, filter, direction);
|
|
||||||
|
|
||||||
if (direction == OBS_ORDER_MOVE_DOWN)
|
|
||||||
currentIndex++;
|
|
||||||
else
|
|
||||||
currentIndex--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -60,15 +60,13 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
|
|||||||
{
|
{
|
||||||
std::vector<obs_hotkey_t *> ret;
|
std::vector<obs_hotkey_t *> ret;
|
||||||
|
|
||||||
auto cb = [](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) {
|
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
|
||||||
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
auto ret = reinterpret_cast<std::vector<obs_hotkey_t *> *>(data);
|
||||||
|
|
||||||
ret->push_back(hotkey);
|
ret->push_back(hotkey);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}, &ret);
|
||||||
|
|
||||||
obs_enum_hotkeys(cb, &ret);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -90,10 +88,12 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
|
|||||||
obs_frontend_get_scenes(&sceneList);
|
obs_frontend_get_scenes(&sceneList);
|
||||||
|
|
||||||
std::vector<json> ret;
|
std::vector<json> ret;
|
||||||
ret.reserve(sceneList.sources.num);
|
|
||||||
for (size_t i = 0; i < sceneList.sources.num; i++) {
|
for (size_t i = 0; i < sceneList.sources.num; i++) {
|
||||||
obs_source_t *scene = sceneList.sources.array[i];
|
obs_source_t *scene = sceneList.sources.array[i];
|
||||||
|
|
||||||
|
if (obs_source_is_group(scene))
|
||||||
|
continue;
|
||||||
|
|
||||||
json sceneJson;
|
json sceneJson;
|
||||||
sceneJson["sceneName"] = obs_source_get_name(scene);
|
sceneJson["sceneName"] = obs_source_get_name(scene);
|
||||||
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
|
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
|
||||||
@ -109,48 +109,22 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ArrayHelper::GetGroupList()
|
|
||||||
{
|
|
||||||
std::vector<std::string> ret;
|
|
||||||
|
|
||||||
auto cb = [](void *priv_data, obs_source_t *scene) {
|
|
||||||
auto ret = static_cast<std::vector<std::string> *>(priv_data);
|
|
||||||
|
|
||||||
if (!obs_source_is_group(scene))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
ret->emplace_back(obs_source_get_name(scene));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
obs_enum_scenes(cb, &ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
|
std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
|
||||||
{
|
{
|
||||||
std::pair<std::vector<json>, bool> enumData;
|
std::pair<std::vector<json>, bool> enumData;
|
||||||
enumData.second = basic;
|
enumData.second = basic;
|
||||||
|
|
||||||
auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
|
||||||
auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
|
auto enumData = reinterpret_cast<std::pair<std::vector<json>, bool>*>(param);
|
||||||
|
|
||||||
// TODO: Make ObjectHelper util for scene items
|
|
||||||
|
|
||||||
json item;
|
json item;
|
||||||
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
item["sceneItemIndex"] =
|
// Should be slightly faster than calling obs_sceneitem_get_order_position()
|
||||||
enumData->first.size(); // Should be slightly faster than calling obs_sceneitem_get_order_position()
|
item["sceneItemIndex"] = enumData->first.size();
|
||||||
if (!enumData->second) {
|
if (!enumData->second) {
|
||||||
item["sceneItemEnabled"] = obs_sceneitem_visible(sceneItem);
|
|
||||||
item["sceneItemLocked"] = obs_sceneitem_locked(sceneItem);
|
|
||||||
item["sceneItemTransform"] = ObjectHelper::GetSceneItemTransform(sceneItem);
|
|
||||||
item["sceneItemBlendMode"] = obs_sceneitem_get_blending_mode(sceneItem);
|
|
||||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||||
item["sourceName"] = obs_source_get_name(itemSource);
|
item["sourceName"] = obs_source_get_name(itemSource);
|
||||||
item["sourceType"] = obs_source_get_type(itemSource);
|
item["sourceType"] = StringHelper::GetSourceType(itemSource);
|
||||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
||||||
item["inputKind"] = obs_source_get_id(itemSource);
|
item["inputKind"] = obs_source_get_id(itemSource);
|
||||||
else
|
else
|
||||||
@ -164,9 +138,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
|
|||||||
enumData->first.push_back(item);
|
enumData->first.push_back(item);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}, &enumData);
|
||||||
|
|
||||||
obs_scene_enum_items(scene, cb, &enumData);
|
|
||||||
|
|
||||||
return enumData.first;
|
return enumData.first;
|
||||||
}
|
}
|
||||||
@ -181,12 +153,12 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
|||||||
EnumInputInfo inputInfo;
|
EnumInputInfo inputInfo;
|
||||||
inputInfo.inputKind = inputKind;
|
inputInfo.inputKind = inputKind;
|
||||||
|
|
||||||
auto cb = [](void *param, obs_source_t *input) {
|
auto inputEnumProc = [](void *param, obs_source_t *input) {
|
||||||
// Sanity check in case the API changes
|
// Sanity check in case the API changes
|
||||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto inputInfo = static_cast<EnumInputInfo *>(param);
|
auto inputInfo = reinterpret_cast<EnumInputInfo*>(param);
|
||||||
|
|
||||||
std::string inputKind = obs_source_get_id(input);
|
std::string inputKind = obs_source_get_id(input);
|
||||||
|
|
||||||
@ -201,9 +173,8 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
|||||||
inputInfo->inputs.push_back(inputJson);
|
inputInfo->inputs.push_back(inputJson);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actually enumerates only public inputs, despite the name
|
// Actually enumerates only public inputs, despite the name
|
||||||
obs_enum_sources(cb, &inputInfo);
|
obs_enum_sources(inputEnumProc, &inputInfo);
|
||||||
|
|
||||||
return inputInfo.inputs;
|
return inputInfo.inputs;
|
||||||
}
|
}
|
||||||
@ -237,8 +208,6 @@ std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *
|
|||||||
enum obs_combo_format itemFormat = obs_property_list_format(property);
|
enum obs_combo_format itemFormat = obs_property_list_format(property);
|
||||||
size_t itemCount = obs_property_list_item_count(property);
|
size_t itemCount = obs_property_list_item_count(property);
|
||||||
|
|
||||||
ret.reserve(itemCount);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < itemCount; i++) {
|
for (size_t i = 0; i < itemCount; i++) {
|
||||||
json itemData;
|
json itemData;
|
||||||
itemData["itemName"] = obs_property_list_item_name(property, i);
|
itemData["itemName"] = obs_property_list_item_name(property, i);
|
||||||
@ -276,7 +245,6 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
|
|||||||
obs_frontend_get_transitions(&transitionList);
|
obs_frontend_get_transitions(&transitionList);
|
||||||
|
|
||||||
std::vector<json> ret;
|
std::vector<json> ret;
|
||||||
ret.reserve(transitionList.sources.num);
|
|
||||||
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
||||||
obs_source_t *transition = transitionList.sources.array[i];
|
obs_source_t *transition = transitionList.sources.array[i];
|
||||||
json transitionJson;
|
json transitionJson;
|
||||||
@ -291,71 +259,3 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ArrayHelper::GetFilterKindList()
|
|
||||||
{
|
|
||||||
std::vector<std::string> ret;
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
const char *kind;
|
|
||||||
while (obs_enum_filter_types(idx++, &kind))
|
|
||||||
ret.push_back(kind);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source)
|
|
||||||
{
|
|
||||||
std::vector<json> filters;
|
|
||||||
|
|
||||||
auto cb = [](obs_source_t *, obs_source_t *filter, void *param) {
|
|
||||||
auto filters = reinterpret_cast<std::vector<json> *>(param);
|
|
||||||
|
|
||||||
json filterJson;
|
|
||||||
filterJson["filterEnabled"] = obs_source_enabled(filter);
|
|
||||||
filterJson["filterIndex"] = filters->size();
|
|
||||||
filterJson["filterKind"] = obs_source_get_id(filter);
|
|
||||||
filterJson["filterName"] = obs_source_get_name(filter);
|
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = obs_source_get_settings(filter);
|
|
||||||
filterJson["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
|
|
||||||
|
|
||||||
filters->push_back(filterJson);
|
|
||||||
};
|
|
||||||
|
|
||||||
obs_source_enum_filters(source, cb, &filters);
|
|
||||||
|
|
||||||
return filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ArrayHelper::GetOutputList()
|
|
||||||
{
|
|
||||||
std::vector<json> outputs;
|
|
||||||
|
|
||||||
auto cb = [](void *param, obs_output_t *output) {
|
|
||||||
auto outputs = reinterpret_cast<std::vector<json> *>(param);
|
|
||||||
|
|
||||||
auto rawFlags = obs_output_get_flags(output);
|
|
||||||
json flags;
|
|
||||||
flags["OBS_OUTPUT_AUDIO"] = !!(rawFlags & OBS_OUTPUT_AUDIO);
|
|
||||||
flags["OBS_OUTPUT_VIDEO"] = !!(rawFlags & OBS_OUTPUT_VIDEO);
|
|
||||||
flags["OBS_OUTPUT_ENCODED"] = !!(rawFlags & OBS_OUTPUT_ENCODED);
|
|
||||||
flags["OBS_OUTPUT_MULTI_TRACK"] = !!(rawFlags & OBS_OUTPUT_MULTI_TRACK);
|
|
||||||
flags["OBS_OUTPUT_SERVICE"] = !!(rawFlags & OBS_OUTPUT_SERVICE);
|
|
||||||
|
|
||||||
json outputJson;
|
|
||||||
outputJson["outputName"] = obs_output_get_name(output);
|
|
||||||
outputJson["outputKind"] = obs_output_get_id(output);
|
|
||||||
outputJson["outputWidth"] = obs_output_get_width(output);
|
|
||||||
outputJson["outputHeight"] = obs_output_get_height(output);
|
|
||||||
outputJson["outputActive"] = obs_output_active(output);
|
|
||||||
outputJson["outputFlags"] = flags;
|
|
||||||
|
|
||||||
outputs->push_back(outputJson);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
obs_enum_outputs(cb, &outputs);
|
|
||||||
|
|
||||||
return outputs;
|
|
||||||
}
|
|
||||||
|
47
src/utils/Obs_EnumHelper.cpp
Normal file
47
src/utils/Obs_EnumHelper.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
obs-websocket
|
||||||
|
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Obs.h"
|
||||||
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
|
#define RET_COMPARE(str, x) if (str == #x) return x;
|
||||||
|
|
||||||
|
enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
|
||||||
|
{
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_NONE);
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH);
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER);
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER);
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH);
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT);
|
||||||
|
RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY);
|
||||||
|
|
||||||
|
return OBS_BOUNDS_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
|
||||||
|
{
|
||||||
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
|
||||||
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
|
||||||
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
|
||||||
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
|
||||||
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
|
||||||
|
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
|
||||||
|
|
||||||
|
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
|
||||||
|
}
|
@ -39,7 +39,7 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
|
|||||||
{
|
{
|
||||||
size_t ret;
|
size_t ret;
|
||||||
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
||||||
auto ret = static_cast<size_t *>(param);
|
auto ret = reinterpret_cast<size_t*>(param);
|
||||||
|
|
||||||
if (obs_source_is_group(scene))
|
if (obs_source_is_group(scene))
|
||||||
return true;
|
return true;
|
||||||
@ -52,28 +52,3 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter)
|
|
||||||
{
|
|
||||||
struct FilterSearch {
|
|
||||||
obs_source_t *filter;
|
|
||||||
bool found;
|
|
||||||
size_t index;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto search = [](obs_source_t *, obs_source_t *filter, void *priv_data) {
|
|
||||||
auto filterSearch = static_cast<FilterSearch *>(priv_data);
|
|
||||||
|
|
||||||
if (filter == filterSearch->filter)
|
|
||||||
filterSearch->found = true;
|
|
||||||
|
|
||||||
if (!filterSearch->found)
|
|
||||||
filterSearch->index++;
|
|
||||||
};
|
|
||||||
|
|
||||||
FilterSearch filterSearch = {filter, 0, 0};
|
|
||||||
|
|
||||||
obs_source_enum_filters(source, search, &filterSearch);
|
|
||||||
|
|
||||||
return filterSearch.index;
|
|
||||||
}
|
|
||||||
|
@ -54,8 +54,8 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
|||||||
obs_sceneitem_get_crop(item, &crop);
|
obs_sceneitem_get_crop(item, &crop);
|
||||||
|
|
||||||
OBSSource source = obs_sceneitem_get_source(item);
|
OBSSource source = obs_sceneitem_get_source(item);
|
||||||
float sourceWidth = (float)obs_source_get_width(source);
|
float sourceWidth = float(obs_source_get_width(source));
|
||||||
float sourceHeight = (float)obs_source_get_height(source);
|
float sourceHeight = float(obs_source_get_height(source));
|
||||||
|
|
||||||
ret["sourceWidth"] = sourceWidth;
|
ret["sourceWidth"] = sourceWidth;
|
||||||
ret["sourceHeight"] = sourceHeight;
|
ret["sourceHeight"] = sourceHeight;
|
||||||
@ -73,15 +73,15 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
|||||||
|
|
||||||
ret["alignment"] = osi.alignment;
|
ret["alignment"] = osi.alignment;
|
||||||
|
|
||||||
ret["boundsType"] = osi.bounds_type;
|
ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
|
||||||
ret["boundsAlignment"] = osi.bounds_alignment;
|
ret["boundsAlignment"] = osi.bounds_alignment;
|
||||||
ret["boundsWidth"] = osi.bounds.x;
|
ret["boundsWidth"] = osi.bounds.x;
|
||||||
ret["boundsHeight"] = osi.bounds.y;
|
ret["boundsHeight"] = osi.bounds.y;
|
||||||
|
|
||||||
ret["cropLeft"] = (int)crop.left;
|
ret["cropLeft"] = int(crop.left);
|
||||||
ret["cropRight"] = (int)crop.right;
|
ret["cropRight"] = int(crop.right);
|
||||||
ret["cropTop"] = (int)crop.top;
|
ret["cropTop"] = int(crop.top);
|
||||||
ret["cropBottom"] = (int)crop.bottom;
|
ret["cropBottom"] = int(crop.bottom);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -54,44 +54,15 @@ obs_source_t *Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string nam
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneItemSearchData {
|
|
||||||
std::string name;
|
|
||||||
int offset;
|
|
||||||
obs_sceneitem_t *ret = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Increments item ref. Use OBSSceneItemAutoRelease
|
// Increments item ref. Use OBSSceneItemAutoRelease
|
||||||
obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name, int offset)
|
obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name)
|
||||||
{
|
{
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
SceneItemSearchData enumData;
|
// Finds first matching scene item in scene, search starts at index 0
|
||||||
enumData.name = name;
|
OBSSceneItem ret = obs_scene_find_source(scene, name.c_str());
|
||||||
enumData.offset = offset;
|
obs_sceneitem_addref(ret);
|
||||||
|
|
||||||
auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
return ret;
|
||||||
auto enumData = static_cast<SceneItemSearchData *>(param);
|
|
||||||
|
|
||||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
|
||||||
std::string sourceName = obs_source_get_name(itemSource);
|
|
||||||
if (sourceName == enumData->name) {
|
|
||||||
if (enumData->offset > 0) {
|
|
||||||
enumData->offset--;
|
|
||||||
} else {
|
|
||||||
if (enumData->ret) // Release existing selection in the case of last match selection
|
|
||||||
obs_sceneitem_release(enumData->ret);
|
|
||||||
obs_sceneitem_addref(sceneItem);
|
|
||||||
enumData->ret = sceneItem;
|
|
||||||
if (enumData->offset == 0) // Only break if in normal selection mode (not offset == -1)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
obs_scene_enum_items(scene, cb, &enumData);
|
|
||||||
|
|
||||||
return enumData.ret;
|
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
#define CASE(x) \
|
#define CASE(x) case x: return #x;
|
||||||
case x: \
|
|
||||||
return #x;
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetObsVersion()
|
std::string Utils::Obs::StringHelper::GetObsVersion()
|
||||||
{
|
{
|
||||||
@ -66,48 +64,84 @@ std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
|
|||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
|
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
|
||||||
{
|
{
|
||||||
char *recordOutputPath = obs_frontend_get_current_record_output_path();
|
//char *recordOutputPath = obs_frontend_get_current_record_output_path();
|
||||||
std::string ret = recordOutputPath;
|
//std::string ret = recordOutputPath;
|
||||||
bfree(recordOutputPath);
|
//bfree(recordOutputPath);
|
||||||
return ret;
|
//return ret;
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetLastRecordFileName()
|
std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease output = obs_frontend_get_recording_output();
|
obs_source_type sourceType = obs_source_get_type(source);
|
||||||
if (!output)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
OBSDataAutoRelease outputSettings = obs_output_get_settings(output);
|
switch (sourceType) {
|
||||||
|
default:
|
||||||
obs_data_item_t *item = obs_data_item_byname(outputSettings, "url");
|
CASE(OBS_SOURCE_TYPE_INPUT)
|
||||||
if (!item) {
|
CASE(OBS_SOURCE_TYPE_FILTER)
|
||||||
item = obs_data_item_byname(outputSettings, "path");
|
CASE(OBS_SOURCE_TYPE_TRANSITION)
|
||||||
if (!item)
|
CASE(OBS_SOURCE_TYPE_SCENE)
|
||||||
return "";
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ret = obs_data_item_get_string(item);
|
std::string Utils::Obs::StringHelper::GetInputMonitorType(enum obs_monitoring_type monitorType)
|
||||||
obs_data_item_release(&item);
|
{
|
||||||
return ret;
|
switch (monitorType) {
|
||||||
|
default:
|
||||||
|
CASE(OBS_MONITORING_TYPE_NONE)
|
||||||
|
CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
|
||||||
|
CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetLastReplayBufferFileName()
|
std::string Utils::Obs::StringHelper::GetInputMonitorType(obs_source_t *input)
|
||||||
|
{
|
||||||
|
obs_monitoring_type monitorType = obs_source_get_monitoring_type(input);
|
||||||
|
|
||||||
|
return GetInputMonitorType(monitorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
|
||||||
|
{
|
||||||
|
obs_media_state mediaState = obs_source_media_get_state(input);
|
||||||
|
|
||||||
|
switch (mediaState) {
|
||||||
|
default:
|
||||||
|
CASE(OBS_MEDIA_STATE_NONE)
|
||||||
|
CASE(OBS_MEDIA_STATE_PLAYING)
|
||||||
|
CASE(OBS_MEDIA_STATE_OPENING)
|
||||||
|
CASE(OBS_MEDIA_STATE_BUFFERING)
|
||||||
|
CASE(OBS_MEDIA_STATE_PAUSED)
|
||||||
|
CASE(OBS_MEDIA_STATE_STOPPED)
|
||||||
|
CASE(OBS_MEDIA_STATE_ENDED)
|
||||||
|
CASE(OBS_MEDIA_STATE_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
|
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
|
||||||
if (!output)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
||||||
proc_handler_call(ph, "get_last_replay", &cd);
|
proc_handler_call(ph, "get_last_replay", &cd);
|
||||||
const char *savedReplayPath = calldata_string(&cd, "path");
|
auto ret = calldata_string(&cd, "path");
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (!savedReplayPath)
|
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
|
||||||
return "";
|
{
|
||||||
|
switch (type) {
|
||||||
return savedReplayPath;
|
default:
|
||||||
|
CASE(OBS_BOUNDS_NONE)
|
||||||
|
CASE(OBS_BOUNDS_STRETCH)
|
||||||
|
CASE(OBS_BOUNDS_SCALE_INNER)
|
||||||
|
CASE(OBS_BOUNDS_SCALE_OUTER)
|
||||||
|
CASE(OBS_BOUNDS_SCALE_TO_WIDTH)
|
||||||
|
CASE(OBS_BOUNDS_SCALE_TO_HEIGHT)
|
||||||
|
CASE(OBS_BOUNDS_MAX_ONLY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
||||||
@ -120,7 +154,20 @@ std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
|||||||
uint64_t secsPart = secs % 60ULL;
|
uint64_t secsPart = secs % 60ULL;
|
||||||
uint64_t msPart = ms % 1000ULL;
|
uint64_t msPart = ms % 1000ULL;
|
||||||
|
|
||||||
QString formatted =
|
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
||||||
QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
|
||||||
return formatted.toStdString();
|
return formatted.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetOutputState(ObsOutputState state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
default:
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_UNKNOWN)
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_STARTING)
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_STARTED)
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_STOPPING)
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_STOPPED)
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_PAUSED)
|
||||||
|
CASE(OBS_WEBSOCKET_OUTPUT_RESUMED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,8 +26,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs_VolumeMeter_Helpers.h"
|
#include "Obs_VolumeMeter_Helpers.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input)
|
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
||||||
: PeakMeterType(SAMPLE_PEAK_METER),
|
PeakMeterType(SAMPLE_PEAK_METER),
|
||||||
_input(obs_source_get_weak_source(input)),
|
_input(obs_source_get_weak_source(input)),
|
||||||
_channels(0),
|
_channels(0),
|
||||||
_lastUpdate(0),
|
_lastUpdate(0),
|
||||||
@ -45,8 +45,7 @@ Utils::Obs::VolumeMeter::Meter::~Meter()
|
|||||||
{
|
{
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
"[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +59,8 @@ Utils::Obs::VolumeMeter::Meter::~Meter()
|
|||||||
|
|
||||||
bool Utils::Obs::VolumeMeter::Meter::InputValid()
|
bool Utils::Obs::VolumeMeter::Meter::InputValid()
|
||||||
{
|
{
|
||||||
return !obs_weak_source_expired(_input);
|
// return !obs_weak_source_expired(_input);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
||||||
@ -69,8 +69,7 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
|||||||
|
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING,
|
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
"[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +213,7 @@ void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data,
|
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
|
||||||
bool muted)
|
|
||||||
{
|
{
|
||||||
auto c = static_cast<Meter*>(priv_data);
|
auto c = static_cast<Meter*>(priv_data);
|
||||||
|
|
||||||
@ -236,8 +234,10 @@ void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, callda
|
|||||||
c->_volume = (float)calldata_float(cd, "volume");
|
c->_volume = (float)calldata_float(cd, "volume");
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod)
|
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) :
|
||||||
: _updateCallback(cb), _updatePeriod(updatePeriod), _running(false)
|
_updateCallback(cb),
|
||||||
|
_updatePeriod(updatePeriod),
|
||||||
|
_running(false)
|
||||||
{
|
{
|
||||||
signal_handler_t *sh = obs_get_signal_handler();
|
signal_handler_t *sh = obs_get_signal_handler();
|
||||||
if (!sh)
|
if (!sh)
|
||||||
|
@ -65,8 +65,7 @@ namespace Utils {
|
|||||||
void ProcessPeak(const struct audio_data *data);
|
void ProcessPeak(const struct audio_data *data);
|
||||||
void ProcessMagnitude(const struct audio_data *data);
|
void ProcessMagnitude(const struct audio_data *data);
|
||||||
|
|
||||||
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source,
|
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted);
|
||||||
const struct audio_data *data, bool muted);
|
|
||||||
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
|
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#define SHIFT_RIGHT_2PS(msb, lsb) \
|
#define SHIFT_RIGHT_2PS(msb, lsb) \
|
||||||
{ \
|
{ \
|
||||||
__m128 tmp = _mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \
|
__m128 tmp = \
|
||||||
|
_mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \
|
||||||
lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1)); \
|
lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1)); \
|
||||||
msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1)); \
|
msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1)); \
|
||||||
}
|
}
|
||||||
@ -55,7 +56,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
r = fmaxf(r, x4_mem[3]); \
|
r = fmaxf(r, x4_mem[3]); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
static float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
||||||
{
|
{
|
||||||
__m128 peak = previousSamples;
|
__m128 peak = previousSamples;
|
||||||
for (size_t i = 0; (i + 3) < sampleCount; i += 4) {
|
for (size_t i = 0; (i + 3) < sampleCount; i += 4) {
|
||||||
@ -68,7 +69,7 @@ static float GetSamplePeak(__m128 previousSamples, const float *samples, size_t
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
||||||
{
|
{
|
||||||
const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
|
const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
|
||||||
const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);
|
const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);
|
||||||
|
@ -53,11 +53,7 @@ std::string Utils::Platform::GetLocalAddress()
|
|||||||
std::vector<std::pair<QString, uint8_t>> preferredAddresses;
|
std::vector<std::pair<QString, uint8_t>> preferredAddresses;
|
||||||
for (auto address : validAddresses) {
|
for (auto address : validAddresses) {
|
||||||
// Attribute a priority (0 is best) to the address to choose the best picks
|
// Attribute a priority (0 is best) to the address to choose the best picks
|
||||||
if (address.startsWith("192.168.1.") ||
|
if (address.startsWith("192.168.1.") || address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
|
||||||
address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
|
|
||||||
if (address.startsWith("192.168.56."))
|
|
||||||
preferredAddresses.push_back(std::make_pair(address, 255)); // Ignore virtualbox default
|
|
||||||
else
|
|
||||||
preferredAddresses.push_back(std::make_pair(address, 0));
|
preferredAddresses.push_back(std::make_pair(address, 0));
|
||||||
} else if (address.startsWith("172.16.")) { // Slightly less common consumer router network prefixes
|
} else if (address.startsWith("172.16.")) { // Slightly less common consumer router network prefixes
|
||||||
preferredAddresses.push_back(std::make_pair(address, 1));
|
preferredAddresses.push_back(std::make_pair(address, 1));
|
||||||
@ -69,8 +65,9 @@ std::string Utils::Platform::GetLocalAddress()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort by priority
|
// Sort by priority
|
||||||
std::sort(preferredAddresses.begin(), preferredAddresses.end(),
|
std::sort(preferredAddresses.begin(), preferredAddresses.end(), [=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) {
|
||||||
[=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) { return a.second < b.second; });
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
|
||||||
// Return highest priority address
|
// Return highest priority address
|
||||||
return preferredAddresses[0].first.toStdString();
|
return preferredAddresses[0].first.toStdString();
|
||||||
@ -112,17 +109,14 @@ void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QS
|
|||||||
|
|
||||||
SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
|
SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
|
||||||
|
|
||||||
obs_queue_task(
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
OBS_TASK_UI,
|
|
||||||
[](void *param) {
|
|
||||||
void *systemTrayPtr = obs_frontend_get_system_tray();
|
void *systemTrayPtr = obs_frontend_get_system_tray();
|
||||||
auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
|
auto systemTray = reinterpret_cast<QSystemTrayIcon*>(systemTrayPtr);
|
||||||
|
|
||||||
auto notification = static_cast<SystemTrayNotification *>(param);
|
auto notification = reinterpret_cast<SystemTrayNotification*>(param);
|
||||||
systemTray->showMessage(notification->title, notification->body, notification->icon);
|
systemTray->showMessage(notification->title, notification->body, notification->icon);
|
||||||
delete notification;
|
delete notification;
|
||||||
},
|
}, (void*)notification, false);
|
||||||
(void *)notification, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
|
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
|
||||||
|
@ -31,7 +31,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Platform.h"
|
#include "../utils/Platform.h"
|
||||||
#include "../utils/Compat.h"
|
#include "../utils/Compat.h"
|
||||||
|
|
||||||
WebSocketServer::WebSocketServer() : QObject(nullptr), _sessions()
|
WebSocketServer::WebSocketServer() :
|
||||||
|
QObject(nullptr),
|
||||||
|
_sessions()
|
||||||
{
|
{
|
||||||
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
||||||
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
||||||
@ -42,17 +44,38 @@ WebSocketServer::WebSocketServer() : QObject(nullptr), _sessions()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
_server.set_validate_handler(
|
_server.set_validate_handler(
|
||||||
websocketpp::lib::bind(&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1));
|
websocketpp::lib::bind(
|
||||||
_server.set_open_handler(websocketpp::lib::bind(&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1));
|
&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1
|
||||||
_server.set_close_handler(websocketpp::lib::bind(&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1));
|
)
|
||||||
_server.set_message_handler(websocketpp::lib::bind(&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1,
|
);
|
||||||
websocketpp::lib::placeholders::_2));
|
_server.set_open_handler(
|
||||||
|
websocketpp::lib::bind(
|
||||||
|
&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1
|
||||||
|
)
|
||||||
|
);
|
||||||
|
_server.set_close_handler(
|
||||||
|
websocketpp::lib::bind(
|
||||||
|
&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1
|
||||||
|
)
|
||||||
|
);
|
||||||
|
_server.set_message_handler(
|
||||||
|
websocketpp::lib::bind(
|
||||||
|
&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
auto eventHandler = GetEventHandler();
|
auto eventHandler = GetEventHandler();
|
||||||
eventHandler->SetBroadcastCallback(std::bind(&WebSocketServer::BroadcastEvent, this, std::placeholders::_1,
|
eventHandler->SetBroadcastCallback(
|
||||||
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
std::bind(
|
||||||
|
&WebSocketServer::BroadcastEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
eventHandler->SetObsLoadedCallback(std::bind(&WebSocketServer::onObsLoaded, this));
|
eventHandler->SetObsLoadedCallback(
|
||||||
|
std::bind(
|
||||||
|
&WebSocketServer::onObsLoaded, this
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketServer::~WebSocketServer()
|
WebSocketServer::~WebSocketServer()
|
||||||
@ -95,8 +118,7 @@ void WebSocketServer::Start()
|
|||||||
// Set log levels if debug is enabled
|
// Set log levels if debug is enabled
|
||||||
if (IsDebugEnabled()) {
|
if (IsDebugEnabled()) {
|
||||||
_server.get_alog().set_channels(websocketpp::log::alevel::all);
|
_server.get_alog().set_channels(websocketpp::log::alevel::all);
|
||||||
_server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload |
|
_server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control);
|
||||||
websocketpp::log::alevel::control);
|
|
||||||
_server.get_elog().set_channels(websocketpp::log::elevel::all);
|
_server.get_elog().set_channels(websocketpp::log::elevel::all);
|
||||||
_server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
|
_server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
|
||||||
} else {
|
} else {
|
||||||
@ -107,13 +129,7 @@ void WebSocketServer::Start()
|
|||||||
_server.reset();
|
_server.reset();
|
||||||
|
|
||||||
websocketpp::lib::error_code errorCode;
|
websocketpp::lib::error_code errorCode;
|
||||||
if (conf->Ipv4Only) {
|
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings");
|
|
||||||
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode);
|
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode);
|
||||||
} else {
|
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Not locked to IPv4 bindings");
|
|
||||||
_server.listen(conf->ServerPort, errorCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
std::string errorCodeMessage = errorCode.message();
|
std::string errorCodeMessage = errorCode.message();
|
||||||
@ -125,8 +141,7 @@ void WebSocketServer::Start()
|
|||||||
|
|
||||||
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
||||||
|
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s",
|
blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s", conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
|
||||||
conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::Stop()
|
void WebSocketServer::Stop()
|
||||||
@ -197,8 +212,7 @@ std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocke
|
|||||||
std::string remoteAddress = session->RemoteAddress();
|
std::string remoteAddress = session->RemoteAddress();
|
||||||
bool isIdentified = session->IsIdentified();
|
bool isIdentified = session->IsIdentified();
|
||||||
|
|
||||||
webSocketSessions.emplace_back(
|
webSocketSessions.emplace_back(WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
|
||||||
WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
|
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@ -344,8 +358,7 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
|
|||||||
emit ClientDisconnected(state, conn->get_local_close_code());
|
emit ClientDisconnected(state, conn->get_local_close_code());
|
||||||
|
|
||||||
// Log disconnection
|
// Log disconnection
|
||||||
blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client `%s` has disconnected with code `%d` and reason: %s",
|
blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client %s has disconnected", remoteAddress.c_str());
|
||||||
remoteAddress.c_str(), conn->get_local_close_code(), conn->get_local_close_reason().c_str());
|
|
||||||
|
|
||||||
// Get config for tray notification
|
// Get config for tray notification
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
@ -357,14 +370,12 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
|
|||||||
// If previously identified, not going away, and notifications enabled, send a tray notification
|
// If previously identified, not going away, and notifications enabled, send a tray notification
|
||||||
if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
|
if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
|
QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body"))
|
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body")).arg(QString::fromStdString(remoteAddress));
|
||||||
.arg(QString::fromStdString(remoteAddress));
|
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
|
void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message)
|
||||||
websocketpp::server<websocketpp::config::asio>::message_ptr message)
|
|
||||||
{
|
{
|
||||||
auto opCode = message->get_opcode();
|
auto opCode = message->get_opcode();
|
||||||
std::string payload = message->get_payload();
|
std::string payload = message->get_payload();
|
||||||
@ -387,32 +398,26 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
|
|||||||
uint8_t sessionEncoding = session->Encoding();
|
uint8_t sessionEncoding = session->Encoding();
|
||||||
if (sessionEncoding == WebSocketEncoding::Json) {
|
if (sessionEncoding == WebSocketEncoding::Json) {
|
||||||
if (opCode != websocketpp::frame::opcode::text) {
|
if (opCode != websocketpp::frame::opcode::text) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to Json, but a binary message was received.", errorCode);
|
||||||
"Your session encoding is set to Json, but a binary message was received.",
|
|
||||||
errorCode);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
incomingMessage = json::parse(payload);
|
incomingMessage = json::parse(payload);
|
||||||
} catch (json::parse_error& e) {
|
} catch (json::parse_error& e) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode Json: ") + e.what(), errorCode);
|
||||||
std::string("Unable to decode Json: ") + e.what(), errorCode);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
||||||
if (opCode != websocketpp::frame::opcode::binary) {
|
if (opCode != websocketpp::frame::opcode::binary) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to MsgPack, but a text message was received.", errorCode);
|
||||||
"Your session encoding is set to MsgPack, but a text message was received.",
|
|
||||||
errorCode);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
incomingMessage = json::from_msgpack(payload);
|
incomingMessage = json::from_msgpack(payload);
|
||||||
} catch (json::parse_error& e) {
|
} catch (json::parse_error& e) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
|
||||||
std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,11 +435,9 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
|
|||||||
|
|
||||||
// Disconnect client if 4.x protocol is detected
|
// Disconnect client if 4.x protocol is detected
|
||||||
if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
|
if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
|
||||||
blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.",
|
blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.", session->RemoteAddress().c_str());
|
||||||
session->RemoteAddress().c_str());
|
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
|
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
|
||||||
ret.closeReason =
|
ret.closeReason = "You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated.";
|
||||||
"You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated.";
|
|
||||||
goto skipProcessing;
|
goto skipProcessing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,8 +472,7 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
|
|||||||
blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
|
blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
|
||||||
|
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s",
|
blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s", errorCode.message().c_str());
|
||||||
errorCode.message().c_str());
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <asio.hpp>
|
|
||||||
#include <websocketpp/config/asio_no_tls.hpp>
|
#include <websocketpp/config/asio_no_tls.hpp>
|
||||||
#include <websocketpp/server.hpp>
|
#include <websocketpp/server.hpp>
|
||||||
|
|
||||||
@ -34,11 +33,15 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../requesthandler/rpc/Request.h"
|
#include "../requesthandler/rpc/Request.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class WebSocketServer : QObject {
|
class WebSocketServer : QObject
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum WebSocketEncoding { Json, MsgPack };
|
enum WebSocketEncoding {
|
||||||
|
Json,
|
||||||
|
MsgPack
|
||||||
|
};
|
||||||
|
|
||||||
struct WebSocketSessionState {
|
struct WebSocketSessionState {
|
||||||
websocketpp::connection_hdl hdl;
|
websocketpp::connection_hdl hdl;
|
||||||
@ -55,14 +58,17 @@ public:
|
|||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
void InvalidateSession(websocketpp::connection_hdl hdl);
|
void InvalidateSession(websocketpp::connection_hdl hdl);
|
||||||
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr,
|
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr, uint8_t rpcVersion = 0);
|
||||||
uint8_t rpcVersion = 0);
|
|
||||||
|
|
||||||
bool IsListening() { return _server.is_listening(); }
|
bool IsListening() {
|
||||||
|
return _server.is_listening();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<WebSocketSessionState> GetWebSocketSessions();
|
std::vector<WebSocketSessionState> GetWebSocketSessions();
|
||||||
|
|
||||||
QThreadPool *GetThreadPool() { return &_threadPool; }
|
QThreadPool *GetThreadPool() {
|
||||||
|
return &_threadPool;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ClientConnected(WebSocketSessionState state);
|
void ClientConnected(WebSocketSessionState state);
|
||||||
|
@ -44,7 +44,10 @@ static json ConstructRequestResult(RequestResult requestResult, const json &requ
|
|||||||
if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
|
if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
|
||||||
ret["requestId"] = requestJson["requestId"];
|
ret["requestId"] = requestJson["requestId"];
|
||||||
|
|
||||||
ret["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success}, {"code", requestResult.StatusCode}};
|
ret["requestStatus"] = {
|
||||||
|
{"result", requestResult.StatusCode == RequestStatus::Success},
|
||||||
|
{"code", requestResult.StatusCode}
|
||||||
|
};
|
||||||
|
|
||||||
if (!requestResult.Comment.empty())
|
if (!requestResult.Comment.empty())
|
||||||
ret["requestStatus"]["comment"] = requestResult.Comment;
|
ret["requestStatus"]["comment"] = requestResult.Comment;
|
||||||
@ -67,8 +70,7 @@ void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret,
|
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
|
||||||
WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
|
|
||||||
{
|
{
|
||||||
if (!payloadData.is_object()) {
|
if (!payloadData.is_object()) {
|
||||||
if (payloadData.is_null()) {
|
if (payloadData.is_null()) {
|
||||||
@ -100,18 +102,14 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
if (session->AuthenticationRequired()) {
|
if (session->AuthenticationRequired()) {
|
||||||
if (!payloadData.contains("authentication")) {
|
if (!payloadData.contains("authentication")) {
|
||||||
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
||||||
ret.closeReason =
|
ret.closeReason = "Your payload's data is missing an `authentication` string, however authentication is required.";
|
||||||
"Your payload's data is missing an `authentication` string, however authentication is required.";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(),
|
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(), payloadData["authentication"])) {
|
||||||
payloadData["authentication"])) {
|
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (conf && conf->AlertsEnabled) {
|
if (conf && conf->AlertsEnabled) {
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
|
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
|
||||||
QString body =
|
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body")).arg(QString::fromStdString(session->RemoteAddress()));
|
||||||
QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body"))
|
|
||||||
.arg(QString::fromStdString(session->RemoteAddress()));
|
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
|
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
|
||||||
}
|
}
|
||||||
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
||||||
@ -129,7 +127,6 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
if (!payloadData["rpcVersion"].is_number_unsigned()) {
|
if (!payloadData["rpcVersion"].is_number_unsigned()) {
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
||||||
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
|
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
|
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
|
||||||
@ -156,15 +153,13 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (conf && conf->AlertsEnabled) {
|
if (conf && conf->AlertsEnabled) {
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
|
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body"))
|
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body")).arg(QString::fromStdString(session->RemoteAddress()));
|
||||||
.arg(QString::fromStdString(session->RemoteAddress()));
|
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::Identified;
|
ret.result["op"] = WebSocketOpCode::Identified;
|
||||||
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
||||||
}
|
} return;
|
||||||
return;
|
|
||||||
case WebSocketOpCode::Reidentify: { // Reidentify
|
case WebSocketOpCode::Reidentify: { // Reidentify
|
||||||
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
||||||
|
|
||||||
@ -182,8 +177,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::Identified;
|
ret.result["op"] = WebSocketOpCode::Identified;
|
||||||
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
||||||
}
|
} return;
|
||||||
return;
|
|
||||||
case WebSocketOpCode::Request: { // Request
|
case WebSocketOpCode::Request: { // Request
|
||||||
// RequestID checking has to be done here where we are able to close the connection.
|
// RequestID checking has to be done here where we are able to close the connection.
|
||||||
if (!payloadData.contains("requestId")) {
|
if (!payloadData.contains("requestId")) {
|
||||||
@ -192,39 +186,25 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payloadData.contains("requestType")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload's data is missing an `requestType`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData["requestType"].is_string()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `requestType` is not a string.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestHandler requestHandler(session);
|
RequestHandler requestHandler(session);
|
||||||
|
Request request(payloadData["requestType"], payloadData["requestData"]);
|
||||||
std::string requestType = payloadData["requestType"];
|
|
||||||
json requestData = payloadData["requestData"];
|
|
||||||
Request request(requestType, requestData);
|
|
||||||
|
|
||||||
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
||||||
|
|
||||||
json resultPayloadData;
|
json resultPayloadData;
|
||||||
resultPayloadData["requestType"] = requestType;
|
resultPayloadData["requestType"] = payloadData["requestType"];
|
||||||
resultPayloadData["requestId"] = payloadData["requestId"];
|
resultPayloadData["requestId"] = payloadData["requestId"];
|
||||||
resultPayloadData["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success},
|
resultPayloadData["requestStatus"] = {
|
||||||
{"code", requestResult.StatusCode}};
|
{"result", requestResult.StatusCode == RequestStatus::Success},
|
||||||
|
{"code", requestResult.StatusCode}
|
||||||
|
};
|
||||||
if (!requestResult.Comment.empty())
|
if (!requestResult.Comment.empty())
|
||||||
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
|
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
|
||||||
if (requestResult.ResponseData.is_object())
|
if (requestResult.ResponseData.is_object())
|
||||||
resultPayloadData["responseData"] = requestResult.ResponseData;
|
resultPayloadData["responseData"] = requestResult.ResponseData;
|
||||||
ret.result["op"] = WebSocketOpCode::RequestResponse;
|
ret.result["op"] = WebSocketOpCode::RequestResponse;
|
||||||
ret.result["d"] = resultPayloadData;
|
ret.result["d"] = resultPayloadData;
|
||||||
}
|
} return;
|
||||||
return;
|
|
||||||
case WebSocketOpCode::RequestBatch: { // RequestBatch
|
case WebSocketOpCode::RequestBatch: { // RequestBatch
|
||||||
// RequestID checking has to be done here where we are able to close the connection.
|
// RequestID checking has to be done here where we are able to close the connection.
|
||||||
if (!payloadData.contains("requestId")) {
|
if (!payloadData.contains("requestId")) {
|
||||||
@ -233,6 +213,18 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!payloadData.contains("requests")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason = "Your payload data is missing a `requests`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData["requests"].is_array()) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason = "Your `requests` is not an array.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
|
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
|
||||||
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
|
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
|
||||||
if (!payloadData["executionType"].is_number_unsigned()) {
|
if (!payloadData["executionType"].is_number_unsigned()) {
|
||||||
@ -241,9 +233,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t requestedExecutionType = payloadData["executionType"];
|
uint8_t requestedExecutionType = payloadData["executionType"];
|
||||||
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) ||
|
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) {
|
||||||
requestedExecutionType == RequestBatchExecutionType::None) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
|
||||||
ret.closeReason = "Your `executionType` has an invalid value.";
|
ret.closeReason = "Your `executionType` has an invalid value.";
|
||||||
return;
|
return;
|
||||||
@ -252,8 +243,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
// The thread pool must support 2 or more threads else parallel requests will deadlock.
|
// The thread pool must support 2 or more threads else parallel requests will deadlock.
|
||||||
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
|
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
|
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
|
||||||
ret.closeReason =
|
ret.closeReason = "Parallel request batch processing is not available on this system due to limited core count.";
|
||||||
"Parallel request batch processing is not available on this system due to limited core count.";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,34 +275,13 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
haltOnFailure = payloadData["haltOnFailure"];
|
haltOnFailure = payloadData["haltOnFailure"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payloadData.contains("requests")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload data is missing a `requests`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData["requests"].is_array()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `requests` is not an array.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> requests = payloadData["requests"];
|
std::vector<json> requests = payloadData["requests"];
|
||||||
|
|
||||||
std::vector<RequestBatchRequest> requestsVector;
|
std::vector<RequestBatchRequest> requestsVector;
|
||||||
for (auto &requestJson : requests) {
|
for (auto &requestJson : requests)
|
||||||
if (!requestJson["requestType"].is_string())
|
requestsVector.emplace_back(requestJson["requestType"], requestJson["requestData"], executionType, requestJson["inputVariables"], requestJson["outputVariables"]);
|
||||||
requestJson["requestType"] =
|
|
||||||
""; // Workaround for what would otherwise be extensive additional logic for a rare edge case
|
|
||||||
std::string requestType = requestJson["requestType"];
|
|
||||||
json requestData = requestJson["requestData"];
|
|
||||||
json inputVariables = requestJson["inputVariables"];
|
|
||||||
json outputVariables = requestJson["outputVariables"];
|
|
||||||
requestsVector.emplace_back(requestType, requestData, executionType, inputVariables, outputVariables);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector,
|
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector, payloadData["variables"], haltOnFailure);
|
||||||
payloadData["variables"], haltOnFailure);
|
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
std::vector<json> results;
|
std::vector<json> results;
|
||||||
@ -324,8 +293,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
|
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
|
||||||
ret.result["d"]["requestId"] = payloadData["requestId"];
|
ret.result["d"]["requestId"] = payloadData["requestId"];
|
||||||
ret.result["d"]["results"] = results;
|
ret.result["d"]["results"] = results;
|
||||||
}
|
} return;
|
||||||
return;
|
|
||||||
default:
|
default:
|
||||||
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
||||||
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
|
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
|
||||||
@ -334,8 +302,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
}
|
}
|
||||||
|
|
||||||
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
|
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
|
||||||
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData,
|
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData, uint8_t rpcVersion)
|
||||||
uint8_t rpcVersion)
|
|
||||||
{
|
{
|
||||||
if (!_server.is_listening())
|
if (!_server.is_listening())
|
||||||
return;
|
return;
|
||||||
@ -369,8 +336,7 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
|
|||||||
if (messageJson.empty()) {
|
if (messageJson.empty()) {
|
||||||
messageJson = eventMessage.dump();
|
messageJson = eventMessage.dump();
|
||||||
}
|
}
|
||||||
_server.send((websocketpp::connection_hdl)it.first, messageJson,
|
_server.send((websocketpp::connection_hdl)it.first, messageJson, websocketpp::frame::opcode::text, errorCode);
|
||||||
websocketpp::frame::opcode::text, errorCode);
|
|
||||||
it.second->IncrementOutgoingMessages();
|
it.second->IncrementOutgoingMessages();
|
||||||
break;
|
break;
|
||||||
case WebSocketEncoding::MsgPack:
|
case WebSocketEncoding::MsgPack:
|
||||||
@ -378,14 +344,12 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
|
|||||||
auto msgPackData = json::to_msgpack(eventMessage);
|
auto msgPackData = json::to_msgpack(eventMessage);
|
||||||
messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
|
messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
|
||||||
}
|
}
|
||||||
_server.send((websocketpp::connection_hdl)it.first, messageMsgPack,
|
_server.send((websocketpp::connection_hdl)it.first, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
|
||||||
websocketpp::frame::opcode::binary, errorCode);
|
|
||||||
it.second->IncrementOutgoingMessages();
|
it.second->IncrementOutgoingMessages();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s",
|
blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s", errorCode.message().c_str());
|
||||||
errorCode.message().c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
@ -20,8 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "WebSocketSession.h"
|
#include "WebSocketSession.h"
|
||||||
#include "../../eventhandler/types/EventSubscription.h"
|
#include "../../eventhandler/types/EventSubscription.h"
|
||||||
|
|
||||||
WebSocketSession::WebSocketSession()
|
WebSocketSession::WebSocketSession() :
|
||||||
: _remoteAddress(""),
|
_remoteAddress(""),
|
||||||
_connectedAt(0),
|
_connectedAt(0),
|
||||||
_incomingMessages(0),
|
_incomingMessages(0),
|
||||||
_outgoingMessages(0),
|
_outgoingMessages(0),
|
||||||
|
@ -29,7 +29,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
class WebSocketSession;
|
class WebSocketSession;
|
||||||
typedef std::shared_ptr<WebSocketSession> SessionPtr;
|
typedef std::shared_ptr<WebSocketSession> SessionPtr;
|
||||||
|
|
||||||
class WebSocketSession {
|
class WebSocketSession
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
WebSocketSession();
|
WebSocketSession();
|
||||||
|
|
||||||
|
@ -122,5 +122,8 @@ namespace WebSocketOpCode {
|
|||||||
RequestBatchResponse = 9,
|
RequestBatchResponse = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(uint8_t opCode) { return opCode >= Hello && opCode <= RequestBatchResponse; }
|
inline bool IsValid(uint8_t opCode)
|
||||||
|
{
|
||||||
|
return opCode >= Hello && opCode <= RequestBatchResponse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user