mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Merge branch 'master' into docs-formatting
This commit is contained in:
commit
d7de347b37
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -69,8 +69,8 @@ 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-alpha3
|
||||||
- 5.0.0-alpha2
|
- 5.0.0-alpha2
|
||||||
- 5.0.0-alpha1
|
|
||||||
- 4.9.1
|
- 4.9.1
|
||||||
- 4.9.0
|
- 4.9.0
|
||||||
- Git
|
- Git
|
||||||
|
264
.github/workflows/main.yml
vendored
264
.github/workflows/main.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: "CI Multiplatform Build"
|
name: 'CI Multiplatform Build'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -18,15 +18,15 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
windows:
|
windows:
|
||||||
name: 'Windows 32/64-bit'
|
name: 'Windows 32/64-bit'
|
||||||
runs-on: [windows-latest]
|
runs-on: [windows-2019]
|
||||||
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,8 +78,6 @@ 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: |
|
||||||
@ -87,20 +85,18 @@ 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 }}'
|
||||||
restore-keys: |
|
- name: 'Install Prerequisite: Pre-built OBS Studio dependencies'
|
||||||
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:
|
||||||
@ -108,22 +104,20 @@ 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 }}
|
||||||
restore-keys: |
|
- name: 'Configure OBS Studio 32-bit'
|
||||||
${{ 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:
|
||||||
@ -131,16 +125,14 @@ 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 }}
|
||||||
restore-keys: |
|
- name: 'Configure OBS Studio 64-bit'
|
||||||
${{ 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: |
|
||||||
@ -188,7 +180,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:
|
||||||
@ -197,20 +189,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: |
|
||||||
@ -284,21 +276,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: |
|
||||||
@ -316,7 +308,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 }}" .. ; \
|
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 .. ; \
|
||||||
fi
|
fi
|
||||||
- name: 'Build obs-websocket'
|
- name: 'Build obs-websocket'
|
||||||
working-directory: ${{ github.workspace }}/obs-websocket
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
@ -355,3 +347,213 @@ 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
|
||||||
|
- 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,3 +11,4 @@
|
|||||||
/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,5 +0,0 @@
|
|||||||
brew "jack"
|
|
||||||
brew "speexdsp"
|
|
||||||
brew "cmake"
|
|
||||||
brew "freetype"
|
|
||||||
brew "fdk-aac"
|
|
@ -1,26 +0,0 @@
|
|||||||
#!/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 .. \
|
|
||||||
-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
|
|
@ -1,39 +0,0 @@
|
|||||||
#!/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
|
|
@ -1,57 +0,0 @@
|
|||||||
#!/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>fr.palakis.obs-websocket</string>
|
<string>com.obsproject.obs-websocket</string>
|
||||||
<key>OVERWRITE_PERMISSIONS</key>
|
<key>OVERWRITE_PERMISSIONS</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>VERSION</key>
|
<key>VERSION</key>
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
#!/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
|
|
@ -103,6 +103,7 @@ set(obs-websocket_SOURCES
|
|||||||
src/eventhandler/EventHandler_Outputs.cpp
|
src/eventhandler/EventHandler_Outputs.cpp
|
||||||
src/eventhandler/EventHandler_SceneItems.cpp
|
src/eventhandler/EventHandler_SceneItems.cpp
|
||||||
src/eventhandler/EventHandler_MediaInputs.cpp
|
src/eventhandler/EventHandler_MediaInputs.cpp
|
||||||
|
src/eventhandler/EventHandler_Ui.cpp
|
||||||
src/requesthandler/RequestHandler.cpp
|
src/requesthandler/RequestHandler.cpp
|
||||||
src/requesthandler/RequestBatchHandler.cpp
|
src/requesthandler/RequestBatchHandler.cpp
|
||||||
src/requesthandler/RequestHandler_General.cpp
|
src/requesthandler/RequestHandler_General.cpp
|
||||||
@ -110,10 +111,14 @@ set(obs-websocket_SOURCES
|
|||||||
src/requesthandler/RequestHandler_Sources.cpp
|
src/requesthandler/RequestHandler_Sources.cpp
|
||||||
src/requesthandler/RequestHandler_Scenes.cpp
|
src/requesthandler/RequestHandler_Scenes.cpp
|
||||||
src/requesthandler/RequestHandler_Inputs.cpp
|
src/requesthandler/RequestHandler_Inputs.cpp
|
||||||
|
src/requesthandler/RequestHandler_Transitions.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
|
||||||
|
src/requesthandler/RequestHandler_Ui.cpp
|
||||||
src/requesthandler/rpc/Request.cpp
|
src/requesthandler/rpc/Request.cpp
|
||||||
src/requesthandler/rpc/RequestBatchRequest.cpp
|
src/requesthandler/rpc/RequestBatchRequest.cpp
|
||||||
src/requesthandler/rpc/RequestResult.cpp
|
src/requesthandler/rpc/RequestResult.cpp
|
||||||
@ -123,7 +128,14 @@ set(obs-websocket_SOURCES
|
|||||||
src/utils/Crypto.cpp
|
src/utils/Crypto.cpp
|
||||||
src/utils/Json.cpp
|
src/utils/Json.cpp
|
||||||
src/utils/Obs.cpp
|
src/utils/Obs.cpp
|
||||||
src/utils/ObsVolumeMeter.cpp
|
src/utils/Obs_StringHelper.cpp
|
||||||
|
src/utils/Obs_EnumHelper.cpp
|
||||||
|
src/utils/Obs_NumberHelper.cpp
|
||||||
|
src/utils/Obs_ArrayHelper.cpp
|
||||||
|
src/utils/Obs_ObjectHelper.cpp
|
||||||
|
src/utils/Obs_SearchHelper.cpp
|
||||||
|
src/utils/Obs_ActionHelper.cpp
|
||||||
|
src/utils/Obs_VolumeMeter.cpp
|
||||||
src/utils/Platform.cpp
|
src/utils/Platform.cpp
|
||||||
src/utils/Compat.cpp
|
src/utils/Compat.cpp
|
||||||
deps/qr/cpp/QrCode.cpp)
|
deps/qr/cpp/QrCode.cpp)
|
||||||
@ -150,8 +162,8 @@ set(obs-websocket_HEADERS
|
|||||||
src/utils/Crypto.h
|
src/utils/Crypto.h
|
||||||
src/utils/Json.h
|
src/utils/Json.h
|
||||||
src/utils/Obs.h
|
src/utils/Obs.h
|
||||||
src/utils/ObsVolumeMeter.h
|
src/utils/Obs_VolumeMeter.h
|
||||||
src/utils/ObsVolumeMeter_Helpers.h
|
src/utils/Obs_VolumeMeter_Helpers.h
|
||||||
src/utils/Platform.h
|
src/utils/Platform.h
|
||||||
src/utils/Compat.h
|
src/utils/Compat.h
|
||||||
src/utils/Utils.h
|
src/utils/Utils.h
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
WebSocket API for OBS Studio.
|
WebSocket API for OBS Studio.
|
||||||
|
|
||||||
[](https://github.com/obs-websocket/obs-websocket/actions/workflows/main.yml)
|
[](https://github.com/obsproject/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)
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ It is **highly recommended** to protect obs-websocket with a password against un
|
|||||||
|
|
||||||
### 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)
|
||||||
|
|
||||||
@ -39,10 +39,10 @@ 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
|
||||||
|
|
||||||
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 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 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 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)
|
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)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
2
deps/asio
vendored
2
deps/asio
vendored
@ -1 +1 @@
|
|||||||
Subproject commit b84e6c16b2ea907dbad94206b7510d85aafc0b42
|
Subproject commit b73dc1d2c0ecb9452a87c26544d7f71e24342df6
|
@ -25,6 +25,7 @@ categoryOrder = [
|
|||||||
'Stream',
|
'Stream',
|
||||||
'Record',
|
'Record',
|
||||||
'Media Inputs',
|
'Media Inputs',
|
||||||
|
'Ui',
|
||||||
'High-Volume'
|
'High-Volume'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ for comment in comments_raw:
|
|||||||
enumValue = field_to_string(comment['enumValue'])
|
enumValue = field_to_string(comment['enumValue'])
|
||||||
enum['enumValue'] = int(enumValue) if enumValue.isdigit() else enumValue
|
enum['enumValue'] = int(enumValue) if enumValue.isdigit() else enumValue
|
||||||
else:
|
else:
|
||||||
enum['enumValue'] = None
|
enum['enumValue'] = enum['enumIdentifier']
|
||||||
|
|
||||||
if enumType not in enums_raw:
|
if enumType not in enums_raw:
|
||||||
enums_raw[enumType] = {'enumIdentifiers': [enum]}
|
enums_raw[enumType] = {'enumIdentifiers': [enum]}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <obs.h>
|
#include <obs.h>
|
||||||
|
|
||||||
|
#define OBS_WEBSOCKET_API_VERSION 1
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -41,7 +41,7 @@ Config::Config() :
|
|||||||
PasswordOverridden(false),
|
PasswordOverridden(false),
|
||||||
FirstLoad(true),
|
FirstLoad(true),
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4444),
|
ServerPort(4455),
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(false),
|
AlertsEnabled(false),
|
||||||
AuthRequired(true),
|
AuthRequired(true),
|
||||||
@ -70,7 +70,7 @@ void Config::Load()
|
|||||||
// future loads use the override flag.
|
// future loads use the override flag.
|
||||||
if (FirstLoad) {
|
if (FirstLoad) {
|
||||||
FirstLoad = false;
|
FirstLoad = false;
|
||||||
if (!ServerPassword.isEmpty()) {
|
if (ServerPassword.isEmpty()) {
|
||||||
blog(LOG_INFO, "[Config::Load] (FirstLoad) Generating new server password.");
|
blog(LOG_INFO, "[Config::Load] (FirstLoad) Generating new server password.");
|
||||||
ServerPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
|
ServerPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,8 +16,7 @@ WebSocketApi::Vendor *get_vendor(calldata_t *cd)
|
|||||||
return static_cast<WebSocketApi::Vendor*>(voidVendor);
|
return static_cast<WebSocketApi::Vendor*>(voidVendor);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketApi::WebSocketApi(EventCallback cb) :
|
WebSocketApi::WebSocketApi()
|
||||||
_eventCallback(cb)
|
|
||||||
{
|
{
|
||||||
blog_debug("[WebSocketApi::WebSocketApi] Setting up...");
|
blog_debug("[WebSocketApi::WebSocketApi] Setting up...");
|
||||||
|
|
||||||
@ -50,6 +49,11 @@ WebSocketApi::~WebSocketApi()
|
|||||||
blog_debug("[WebSocketApi::~WebSocketApi] Finished.");
|
blog_debug("[WebSocketApi::~WebSocketApi] Finished.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebSocketApi::SetEventCallback(EventCallback cb)
|
||||||
|
{
|
||||||
|
_eventCallback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData)
|
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData)
|
||||||
{
|
{
|
||||||
std::shared_lock l(_mutex);
|
std::shared_lock l(_mutex);
|
||||||
@ -196,6 +200,9 @@ void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
|
|||||||
|
|
||||||
auto eventData = static_cast<obs_data_t*>(voidEventData);
|
auto eventData = static_cast<obs_data_t*>(voidEventData);
|
||||||
|
|
||||||
|
if (!c->_eventCallback)
|
||||||
|
RETURN_FAILURE();
|
||||||
|
|
||||||
c->_eventCallback(v->_name, eventType, eventData);
|
c->_eventCallback(v->_name, eventType, eventData);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
|
@ -25,9 +25,11 @@ class WebSocketApi {
|
|||||||
std::map<std::string, obs_websocket_request_callback> _requests;
|
std::map<std::string, obs_websocket_request_callback> _requests;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketApi(EventCallback cb);
|
WebSocketApi();
|
||||||
~WebSocketApi();
|
~WebSocketApi();
|
||||||
|
|
||||||
|
void SetEventCallback(EventCallback cb);
|
||||||
|
|
||||||
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData, obs_data_t *responseData);
|
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData, 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);
|
||||||
|
@ -134,9 +134,13 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
|
|||||||
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);
|
||||||
|
signal_handler_connect(sh, "filter_add", HandleSourceFilterCreated, this);
|
||||||
|
signal_handler_connect(sh, "filter_remove", HandleSourceFilterRemoved, this);
|
||||||
|
signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
|
||||||
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
|
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
|
||||||
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
||||||
@ -156,6 +160,7 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,9 +179,10 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
|||||||
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);
|
||||||
@ -185,6 +191,9 @@ 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);
|
||||||
|
signal_handler_disconnect(sh, "filter_add", HandleSourceFilterCreated, this);
|
||||||
|
signal_handler_disconnect(sh, "filter_remove", HandleSourceFilterRemoved, this);
|
||||||
|
signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
|
||||||
|
|
||||||
// Scenes
|
// Scenes
|
||||||
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
|
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
|
||||||
@ -192,12 +201,37 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventHandler::ConnectFilterSignals(obs_source_t *filter)
|
||||||
|
{
|
||||||
|
if (!filter || obs_source_removed(filter))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DisconnectFilterSignals(filter);
|
||||||
|
|
||||||
|
signal_handler_t* sh = obs_source_get_signal_handler(filter);
|
||||||
|
|
||||||
|
signal_handler_connect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
|
||||||
|
signal_handler_connect(sh, "rename", HandleSourceFilterNameChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::DisconnectFilterSignals(obs_source_t *filter)
|
||||||
|
{
|
||||||
|
if (!filter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
signal_handler_t* sh = obs_source_get_signal_handler(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 = reinterpret_cast<EventHandler*>(private_data);
|
auto eventHandler = static_cast<EventHandler*>(private_data);
|
||||||
|
|
||||||
if (!eventHandler->_obsLoaded.load()) {
|
if (!eventHandler->_obsLoaded.load()) {
|
||||||
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
||||||
@ -207,18 +241,38 @@ 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 connect each one
|
// Enumerate inputs and connect each one
|
||||||
obs_enum_sources([](void* param, obs_source_t* source) {
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto enumInputs = [](void *param, obs_source_t *source) {
|
||||||
eventHandler->ConnectSourceSignals(source);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
return true;
|
eventHandler->ConnectSourceSignals(source);
|
||||||
}, private_data);
|
|
||||||
|
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){
|
||||||
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
eventHandler->ConnectFilterSignals(filter);
|
||||||
|
};
|
||||||
|
obs_source_enum_filters(source, enumFilters, param);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
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 eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||||
eventHandler->ConnectSourceSignals(source);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
return true;
|
eventHandler->ConnectSourceSignals(source);
|
||||||
}, private_data);
|
|
||||||
|
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){
|
||||||
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
eventHandler->ConnectFilterSignals(filter);
|
||||||
|
};
|
||||||
|
obs_source_enum_filters(source, enumFilters, param);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
obs_enum_scenes(enumScenes, private_data);
|
||||||
|
}
|
||||||
|
|
||||||
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
||||||
|
|
||||||
@ -240,18 +294,38 @@ 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 eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto enumInputs = [](void *param, obs_source_t *source) {
|
||||||
eventHandler->DisconnectSourceSignals(source);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
return true;
|
eventHandler->DisconnectSourceSignals(source);
|
||||||
}, private_data);
|
|
||||||
|
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){
|
||||||
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
eventHandler->ConnectFilterSignals(filter);
|
||||||
|
};
|
||||||
|
obs_source_enum_filters(source, enumFilters, param);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
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 eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||||
eventHandler->DisconnectSourceSignals(source);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
return true;
|
eventHandler->DisconnectSourceSignals(source);
|
||||||
}, private_data);
|
|
||||||
|
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){
|
||||||
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
eventHandler->ConnectFilterSignals(filter);
|
||||||
|
};
|
||||||
|
obs_source_enum_filters(source, enumFilters, param);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
obs_enum_scenes(enumScenes, private_data);
|
||||||
|
}
|
||||||
|
|
||||||
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
blog_debug("[EventHandler::OnFrontendEvent] Finished.");
|
||||||
|
|
||||||
@ -264,18 +338,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:
|
||||||
// eventHandler->HandleCurrentSceneCollectionChanging();
|
eventHandler->HandleCurrentSceneCollectionChanging();
|
||||||
// break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
|
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
|
||||||
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;
|
||||||
@ -285,7 +359,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
|
|
||||||
// Scenes
|
// Scenes
|
||||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||||
eventHandler->HandleCurrentSceneChanged();
|
eventHandler->HandleCurrentProgramSceneChanged();
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
|
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
|
||||||
eventHandler->HandleCurrentPreviewSceneChanged();
|
eventHandler->HandleCurrentPreviewSceneChanged();
|
||||||
@ -296,10 +370,12 @@ 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:
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
|
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
|
||||||
|
eventHandler->HandleCurrentSceneTransitionDurationChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
@ -363,7 +439,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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())
|
||||||
@ -379,10 +455,6 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
|
|||||||
case OBS_SOURCE_TYPE_INPUT:
|
case OBS_SOURCE_TYPE_INPUT:
|
||||||
eventHandler->HandleInputCreated(source);
|
eventHandler->HandleInputCreated(source);
|
||||||
break;
|
break;
|
||||||
case OBS_SOURCE_TYPE_FILTER:
|
|
||||||
break;
|
|
||||||
case OBS_SOURCE_TYPE_TRANSITION:
|
|
||||||
break;
|
|
||||||
case OBS_SOURCE_TYPE_SCENE:
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
eventHandler->HandleSceneCreated(source);
|
eventHandler->HandleSceneCreated(source);
|
||||||
break;
|
break;
|
||||||
@ -394,7 +466,7 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
|
|||||||
// Only called for destruction of a public source
|
// Only called for destruction of a public source
|
||||||
void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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");
|
||||||
@ -413,10 +485,6 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
|||||||
// We have to call `InputRemoved` with source_destroy because source_removed is not called when an input's last scene item is removed
|
// We have to call `InputRemoved` with source_destroy because source_removed is not called when an input's last scene item is removed
|
||||||
eventHandler->HandleInputRemoved(source);
|
eventHandler->HandleInputRemoved(source);
|
||||||
break;
|
break;
|
||||||
case OBS_SOURCE_TYPE_FILTER:
|
|
||||||
break;
|
|
||||||
case OBS_SOURCE_TYPE_TRANSITION:
|
|
||||||
break;
|
|
||||||
case OBS_SOURCE_TYPE_SCENE:
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -426,7 +494,7 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
|
|||||||
|
|
||||||
void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
|
void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_obsLoaded.load())
|
if (!eventHandler->_obsLoaded.load())
|
||||||
return;
|
return;
|
||||||
@ -438,10 +506,6 @@ 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:
|
||||||
break;
|
break;
|
||||||
case OBS_SOURCE_TYPE_FILTER:
|
|
||||||
break;
|
|
||||||
case OBS_SOURCE_TYPE_TRANSITION:
|
|
||||||
break;
|
|
||||||
case OBS_SOURCE_TYPE_SCENE:
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
// Scenes emit the `removed` signal when they are removed from OBS, as expected
|
// Scenes emit the `removed` signal when they are removed from OBS, as expected
|
||||||
eventHandler->HandleSceneRemoved(source);
|
eventHandler->HandleSceneRemoved(source);
|
||||||
@ -453,7 +517,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_obsLoaded.load())
|
if (!eventHandler->_obsLoaded.load())
|
||||||
return;
|
return;
|
||||||
@ -471,8 +535,6 @@ 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:
|
||||||
|
@ -22,12 +22,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <util/platform.h>
|
|
||||||
|
|
||||||
#include "types/EventSubscription.h"
|
#include "types/EventSubscription.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
#include "../utils/Obs.h"
|
#include "../utils/Obs.h"
|
||||||
#include "../utils/ObsVolumeMeter.h"
|
#include "../utils/Obs_VolumeMeter.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class EventHandler
|
class EventHandler
|
||||||
@ -59,6 +58,9 @@ class EventHandler
|
|||||||
void ConnectSourceSignals(obs_source_t *source);
|
void ConnectSourceSignals(obs_source_t *source);
|
||||||
void DisconnectSourceSignals(obs_source_t *source);
|
void DisconnectSourceSignals(obs_source_t *source);
|
||||||
|
|
||||||
|
void ConnectFilterSignals(obs_source_t *filter);
|
||||||
|
void DisconnectFilterSignals(obs_source_t *filter);
|
||||||
|
|
||||||
void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0);
|
void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr, uint8_t rpcVersion = 0);
|
||||||
|
|
||||||
// Signal handler: frontend
|
// Signal handler: frontend
|
||||||
@ -95,7 +97,7 @@ class EventHandler
|
|||||||
void HandleSceneCreated(obs_source_t *source);
|
void HandleSceneCreated(obs_source_t *source);
|
||||||
void HandleSceneRemoved(obs_source_t *source);
|
void HandleSceneRemoved(obs_source_t *source);
|
||||||
void HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName);
|
void HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName);
|
||||||
void HandleCurrentSceneChanged();
|
void HandleCurrentProgramSceneChanged();
|
||||||
void HandleCurrentPreviewSceneChanged();
|
void HandleCurrentPreviewSceneChanged();
|
||||||
void HandleSceneListChanged();
|
void HandleSceneListChanged();
|
||||||
|
|
||||||
@ -108,14 +110,14 @@ class EventHandler
|
|||||||
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
|
// Transitions
|
||||||
void HandleTransitionCreated(obs_source_t *source);
|
void HandleCurrentSceneTransitionChanged();
|
||||||
void HandleTransitionRemoved(obs_source_t *source);
|
void HandleCurrentSceneTransitionDurationChanged();
|
||||||
void HandleTransitionNameChanged(obs_source_t *source, std::string oldTransitionName, std::string transitionName);
|
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
void HandleStreamStateChanged(ObsOutputState state);
|
void HandleStreamStateChanged(ObsOutputState state);
|
||||||
@ -130,10 +132,18 @@ class EventHandler
|
|||||||
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
|
||||||
static void HandleMediaInputPlaybackStarted(void *param, calldata_t *data); // Direct callback
|
static void HandleMediaInputPlaybackStarted(void *param, calldata_t *data); // Direct callback
|
||||||
static void HandleMediaInputPlaybackEnded(void *param, calldata_t *data); // Direct callback
|
static void HandleMediaInputPlaybackEnded(void *param, calldata_t *data); // Direct callback
|
||||||
void HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action);
|
void HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action);
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback
|
||||||
|
static void HandleSourceFilterCreated(void *param, calldata_t *data); // Direct callback
|
||||||
|
static void HandleSourceFilterRemoved(void *param, calldata_t *data); // Direct callback
|
||||||
|
static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback
|
||||||
|
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
|
||||||
};
|
};
|
||||||
|
@ -80,7 +80,7 @@ void EventHandler::HandleCurrentSceneCollectionChanged()
|
|||||||
void EventHandler::HandleSceneCollectionListChanged()
|
void EventHandler::HandleSceneCollectionListChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneCollections"] = Utils::Obs::ListHelper::GetSceneCollectionList();
|
eventData["sceneCollections"] = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
BroadcastEvent(EventSubscription::Config, "SceneCollectionListChanged", eventData);
|
BroadcastEvent(EventSubscription::Config, "SceneCollectionListChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +140,6 @@ void EventHandler::HandleCurrentProfileChanged()
|
|||||||
void EventHandler::HandleProfileListChanged()
|
void EventHandler::HandleProfileListChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["profiles"] = Utils::Obs::ListHelper::GetProfileList();
|
eventData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
BroadcastEvent(EventSubscription::Config, "ProfileListChanged", eventData);
|
BroadcastEvent(EventSubscription::Config, "ProfileListChanged", eventData);
|
||||||
}
|
}
|
||||||
|
@ -18,3 +18,174 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(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->ConnectFilterSignals(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);
|
||||||
|
eventHandler->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(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->DisconnectFilterSignals(filter);
|
||||||
|
|
||||||
|
json eventData;
|
||||||
|
eventData["sourceName"] = obs_source_get_name(source);
|
||||||
|
eventData["filterName"] = obs_source_get_name(filter);
|
||||||
|
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", eventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
@ -34,23 +34,3 @@ void EventHandler::HandleExitStarted()
|
|||||||
{
|
{
|
||||||
BroadcastEvent(EventSubscription::General, "ExitStarted");
|
BroadcastEvent(EventSubscription::General, "ExitStarted");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Studio mode has been enabled or disabled.
|
|
||||||
*
|
|
||||||
* @dataField studioModeEnabled | Boolean | True == Enabled, False == Disabled
|
|
||||||
*
|
|
||||||
* @eventType StudioModeStateChanged
|
|
||||||
* @eventSubscription General
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category general
|
|
||||||
* @api events
|
|
||||||
*/
|
|
||||||
void EventHandler::HandleStudioModeStateChanged(bool enabled)
|
|
||||||
{
|
|
||||||
json eventData;
|
|
||||||
eventData["studioModeEnabled"] = enabled;
|
|
||||||
BroadcastEvent(EventSubscription::General, "StudioModeStateChanged", eventData);
|
|
||||||
}
|
|
||||||
|
@ -19,6 +19,23 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input has been created.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField inputKind | String | The kind of the input
|
||||||
|
* @dataField unversionedInputKind | String | The unversioned kind of input (aka no `_v2` stuff)
|
||||||
|
* @dataField inputSettings | Object | The settings configured to the input when it was created
|
||||||
|
* @dataField defaultInputSettings | Object | The default settings for the input
|
||||||
|
*
|
||||||
|
* @eventType InputCreated
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputCreated(obs_source_t *source)
|
void EventHandler::HandleInputCreated(obs_source_t *source)
|
||||||
{
|
{
|
||||||
std::string inputKind = obs_source_get_id(source);
|
std::string inputKind = obs_source_get_id(source);
|
||||||
@ -34,6 +51,19 @@ void EventHandler::HandleInputCreated(obs_source_t *source)
|
|||||||
BroadcastEvent(EventSubscription::Inputs, "InputCreated", eventData);
|
BroadcastEvent(EventSubscription::Inputs, "InputCreated", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input has been removed.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
*
|
||||||
|
* @eventType InputRemoved
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputRemoved(obs_source_t *source)
|
void EventHandler::HandleInputRemoved(obs_source_t *source)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
@ -41,6 +71,20 @@ void EventHandler::HandleInputRemoved(obs_source_t *source)
|
|||||||
BroadcastEvent(EventSubscription::Inputs, "InputRemoved", eventData);
|
BroadcastEvent(EventSubscription::Inputs, "InputRemoved", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of an input has changed.
|
||||||
|
*
|
||||||
|
* @dataField oldInputName | String | Old name of the input
|
||||||
|
* @dataField inputName | String | New name of the input
|
||||||
|
*
|
||||||
|
* @eventType InputNameChanged
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputName, std::string inputName)
|
void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputName, std::string inputName)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
@ -49,16 +93,25 @@ void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputNa
|
|||||||
BroadcastEvent(EventSubscription::Inputs, "InputNameChanged", eventData);
|
BroadcastEvent(EventSubscription::Inputs, "InputNameChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::HandleInputVolumeMeters(std::vector<json> inputs)
|
/**
|
||||||
{
|
* An input's active state has changed.
|
||||||
json eventData;
|
*
|
||||||
eventData["inputs"] = inputs;
|
* When an input is active, it means it's being shown by the program feed.
|
||||||
BroadcastEvent(EventSubscription::InputVolumeMeters, "InputVolumeMeters", eventData);
|
*
|
||||||
}
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField videoActive | Boolean | Whether the input is active
|
||||||
|
*
|
||||||
|
* @eventType InputActiveStateChanged
|
||||||
|
* @eventSubscription InputActiveStateChanged
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_inputActiveStateChangedRef.load())
|
if (!eventHandler->_inputActiveStateChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -76,9 +129,25 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::InputActiveStateChanged, "InputActiveStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::InputActiveStateChanged, "InputActiveStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input's show state has changed.
|
||||||
|
*
|
||||||
|
* When an input is showing, it means it's being shown by the preview or a dialog.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField videoShowing | Boolean | Whether the input is showing
|
||||||
|
*
|
||||||
|
* @eventType InputShowStateChanged
|
||||||
|
* @eventSubscription InputShowStateChanged
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_inputShowStateChangedRef.load())
|
if (!eventHandler->_inputShowStateChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -96,9 +165,23 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::InputShowStateChanged, "InputShowStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::InputShowStateChanged, "InputShowStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input's mute state has changed.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField inputMuted | Boolean | Whether the input is muted
|
||||||
|
*
|
||||||
|
* @eventType InputMuteStateChanged
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -113,9 +196,24 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputMuteStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputMuteStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input's volume level has changed.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField inputVolumeMul | Number | New volume level in multimap
|
||||||
|
* @dataField inputVolumeDb | Number | New volume level in dB
|
||||||
|
*
|
||||||
|
* @eventType InputVolumeChanged
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -138,9 +236,56 @@ 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.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField inputAudioSyncOffset | Number | New sync offset in milliseconds
|
||||||
|
*
|
||||||
|
* @eventType InputAudioSyncOffsetChanged
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -157,9 +302,23 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioSyncOffsetChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioSyncOffsetChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The audio tracks of an input have changed.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField inputAudioTracks | Object | Object of audio tracks along with their associated enable states
|
||||||
|
*
|
||||||
|
* @eventType InputAudioTracksChanged
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -181,9 +340,28 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioTracksChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioTracksChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The monitor type of an input has changed.
|
||||||
|
*
|
||||||
|
* Available types are:
|
||||||
|
* - `OBS_MONITORING_TYPE_NONE`
|
||||||
|
* - `OBS_MONITORING_TYPE_MONITOR_ONLY`
|
||||||
|
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField monitorType | String | New monitor type of the input
|
||||||
|
*
|
||||||
|
* @eventType InputAudioMonitorTypeChanged
|
||||||
|
* @eventSubscription Inputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
|
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -194,22 +372,30 @@ 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;
|
std::string monitorTypeString = Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
|
||||||
switch (monitorType) {
|
|
||||||
default:
|
|
||||||
case OBS_MONITORING_TYPE_NONE:
|
|
||||||
monitorTypeString = "OBS_WEBSOCKET_MONITOR_TYPE_NONE";
|
|
||||||
break;
|
|
||||||
case OBS_MONITORING_TYPE_MONITOR_ONLY:
|
|
||||||
monitorTypeString = "OBS_WEBSOCKET_MONITOR_TYPE_MONITOR_ONLY";
|
|
||||||
break;
|
|
||||||
case OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT:
|
|
||||||
monitorTypeString = "OBS_WEBSOCKET_MONITOR_TYPE_MONITOR_AND_OUTPUT";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["inputName"] = obs_source_get_name(source);
|
eventData["inputName"] = obs_source_get_name(source);
|
||||||
eventData["monitorType"] = monitorTypeString;
|
eventData["monitorType"] = monitorTypeString;
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A high-volume event providing volume levels of all active inputs every 50 milliseconds.
|
||||||
|
*
|
||||||
|
* @dataField inputs | Array<Object> | Array of active inputs with their associated volume levels
|
||||||
|
*
|
||||||
|
* @eventType InputVolumeMeters
|
||||||
|
* @eventSubscription InputVolumeMeters
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category inputs
|
||||||
|
*/
|
||||||
|
void EventHandler::HandleInputVolumeMeters(std::vector<json> inputs)
|
||||||
|
{
|
||||||
|
json eventData;
|
||||||
|
eventData["inputs"] = inputs;
|
||||||
|
BroadcastEvent(EventSubscription::InputVolumeMeters, "InputVolumeMeters", eventData);
|
||||||
|
}
|
||||||
|
@ -35,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -49,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -63,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -77,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -91,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -105,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 = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -117,9 +117,22 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data
|
|||||||
eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
|
eventHandler->HandleMediaInputActionTriggered(source, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media input has started playing.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
*
|
||||||
|
* @eventType MediaInputPlaybackStarted
|
||||||
|
* @eventSubscription MediaInputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
|
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -133,9 +146,22 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackStarted", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackStarted", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media input has finished playing.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
*
|
||||||
|
* @eventType MediaInputPlaybackEnded
|
||||||
|
* @eventSubscription MediaInputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -149,6 +175,20 @@ void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackEnded", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackEnded", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action has been performed on an input.
|
||||||
|
*
|
||||||
|
* @dataField inputName | String | Name of the input
|
||||||
|
* @dataField mediaAction | String | Action performed on the input. See `ObsMediaInputAction` enum
|
||||||
|
*
|
||||||
|
* @eventType MediaInputActionTriggered
|
||||||
|
* @eventSubscription MediaInputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action)
|
void EventHandler::HandleMediaInputActionTriggered(obs_source_t *source, ObsMediaInputAction action)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
|
@ -19,21 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
static bool GetOutputStateActive(ObsOutputState state) {
|
||||||
|
|
||||||
std::string GetOutputStateString(ObsOutputState state) {
|
|
||||||
switch (state) {
|
|
||||||
default:
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
||||||
@ -48,38 +34,107 @@ bool GetOutputStateActive(ObsOutputState state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of the stream output has changed.
|
||||||
|
*
|
||||||
|
* @dataField outputActive | Boolean | Whether the output is active
|
||||||
|
* @dataField outputState | String | The specific state of the output
|
||||||
|
*
|
||||||
|
* @eventType StreamStateChanged
|
||||||
|
* @eventSubscription Outputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = GetOutputStateString(state);
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of the record output has changed.
|
||||||
|
*
|
||||||
|
* @dataField outputActive | Boolean | Whether the output is active
|
||||||
|
* @dataField outputState | String | The specific state of the output
|
||||||
|
*
|
||||||
|
* @eventType RecordStateChanged
|
||||||
|
* @eventSubscription Outputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleRecordStateChanged(ObsOutputState state)
|
void EventHandler::HandleRecordStateChanged(ObsOutputState state)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = GetOutputStateString(state);
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of the replay buffer output has changed.
|
||||||
|
*
|
||||||
|
* @dataField outputActive | Boolean | Whether the output is active
|
||||||
|
* @dataField outputState | String | The specific state of the output
|
||||||
|
*
|
||||||
|
* @eventType ReplayBufferStateChanged
|
||||||
|
* @eventSubscription Outputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
|
void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = GetOutputStateString(state);
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of the virtualcam output has changed.
|
||||||
|
*
|
||||||
|
* @dataField outputActive | Boolean | Whether the output is active
|
||||||
|
* @dataField outputState | String | The specific state of the output
|
||||||
|
*
|
||||||
|
* @eventType VirtualcamStateChanged
|
||||||
|
* @eventSubscription Outputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = GetOutputStateString(state);
|
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
||||||
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The replay buffer has been saved.
|
||||||
|
*
|
||||||
|
* @dataField savedReplayPath | String | Path of the saved replay file
|
||||||
|
*
|
||||||
|
* @eventType ReplayBufferSaved
|
||||||
|
* @eventSubscription Outputs
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
void EventHandler::HandleReplayBufferSaved()
|
void EventHandler::HandleReplayBufferSaved()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
|
@ -19,9 +19,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scene item has been created.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene the item was added to
|
||||||
|
* @dataField sourceName | String | Name of the underlying source (input/scene)
|
||||||
|
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
* @dataField sceneItemIndex | Number | Index position of the item
|
||||||
|
*
|
||||||
|
* @eventType SceneItemCreated
|
||||||
|
* @eventSubscription SceneItems
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -33,16 +49,32 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
|
|||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
||||||
eventData["inputName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
|
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
|
||||||
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
eventData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
|
eventData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemCreated", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemCreated", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will not be emitted if an item is removed due to the parent scene being removed.
|
/**
|
||||||
|
* A scene item has been removed.
|
||||||
|
*
|
||||||
|
* This event is not emitted when the scene the item is in is removed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene the item was removed from
|
||||||
|
* @dataField sourceName | String | Name of the underlying source (input/scene)
|
||||||
|
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
*
|
||||||
|
* @eventType SceneItemRemoved
|
||||||
|
* @eventSubscription SceneItems
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -54,15 +86,28 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
|
|||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
||||||
eventData["inputName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
|
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
|
||||||
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
eventData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
|
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemRemoved", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemRemoved", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scene's item list has been reindexed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene
|
||||||
|
* @dataField sceneItems | Array<Object> | Array of scene item objects
|
||||||
|
*
|
||||||
|
* @eventType SceneItemListReindexed
|
||||||
|
* @eventSubscription SceneItems
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -70,13 +115,28 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
|
|||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
||||||
eventData["sceneItems"] = Utils::Obs::ListHelper::GetSceneItemList(scene, true);
|
eventData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemListReindexed", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemListReindexed", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scene item's enable state has changed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene the item is in
|
||||||
|
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
* @dataField sceneItemEnabled | Boolean | Whether the scene item is enabled (visible)
|
||||||
|
*
|
||||||
|
* @eventType SceneItemEnableStateChanged
|
||||||
|
* @eventSubscription SceneItems
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -95,9 +155,24 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
|
|||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemEnableStateChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemEnableStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scene item's lock state has changed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene the item is in
|
||||||
|
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
* @dataField sceneItemLocked | Boolean | Whether the scene item is locked
|
||||||
|
*
|
||||||
|
* @eventType SceneItemLockStateChanged
|
||||||
|
* @eventSubscription SceneItems
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_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)
|
||||||
@ -116,9 +191,56 @@ 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.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | The name of the scene the item is in
|
||||||
|
* @dataField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
* @dataField sceneItemTransform | Object | New transform/crop info of the scene item
|
||||||
|
*
|
||||||
|
* @eventType SceneItemTransformChanged
|
||||||
|
* @eventSubscription SceneItemTransformChanged
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
|
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
|
||||||
{
|
{
|
||||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
auto eventHandler = static_cast<EventHandler*>(param);
|
||||||
|
|
||||||
if (!eventHandler->_sceneItemTransformChangedRef.load())
|
if (!eventHandler->_sceneItemTransformChangedRef.load())
|
||||||
return;
|
return;
|
||||||
@ -134,6 +256,6 @@ void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data
|
|||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
|
||||||
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
eventData["sceneItemTransform"] = Utils::Obs::DataHelper::GetSceneItemTransform(sceneItem);
|
eventData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||||
eventHandler->BroadcastEvent(EventSubscription::SceneItemTransformChanged, "SceneItemTransformChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::SceneItemTransformChanged, "SceneItemTransformChanged", eventData);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,20 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A new scene has been created.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the new scene
|
||||||
|
* @dataField isGroup | Boolean | Whether the new scene is a group
|
||||||
|
*
|
||||||
|
* @eventType SceneCreated
|
||||||
|
* @eventSubscription Scenes
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scenes
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneCreated(obs_source_t *source)
|
void EventHandler::HandleSceneCreated(obs_source_t *source)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
@ -27,6 +41,20 @@ void EventHandler::HandleSceneCreated(obs_source_t *source)
|
|||||||
BroadcastEvent(EventSubscription::Scenes, "SceneCreated", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "SceneCreated", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scene has been removed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the removed scene
|
||||||
|
* @dataField isGroup | Boolean | Whether the scene was a group
|
||||||
|
*
|
||||||
|
* @eventType SceneRemoved
|
||||||
|
* @eventSubscription Scenes
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scenes
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneRemoved(obs_source_t *source)
|
void EventHandler::HandleSceneRemoved(obs_source_t *source)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
@ -35,6 +63,20 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
|
|||||||
BroadcastEvent(EventSubscription::Scenes, "SceneRemoved", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "SceneRemoved", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of a scene has changed.
|
||||||
|
*
|
||||||
|
* @dataField oldSceneName | String | Old name of the scene
|
||||||
|
* @dataField sceneName | String | New name of the scene
|
||||||
|
*
|
||||||
|
* @eventType SceneNameChanged
|
||||||
|
* @eventSubscription Scenes
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scenes
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneName, std::string sceneName)
|
void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneName, std::string sceneName)
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
@ -43,15 +85,41 @@ void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneNa
|
|||||||
BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::HandleCurrentSceneChanged()
|
/**
|
||||||
|
* The current program scene has changed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene that was switched to
|
||||||
|
*
|
||||||
|
* @eventType CurrentProgramSceneChanged
|
||||||
|
* @eventSubscription Scenes
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scenes
|
||||||
|
*/
|
||||||
|
void EventHandler::HandleCurrentProgramSceneChanged()
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
|
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["sceneName"] = obs_source_get_name(currentScene);
|
eventData["sceneName"] = obs_source_get_name(currentScene);
|
||||||
BroadcastEvent(EventSubscription::Scenes, "CurrentSceneChanged", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "CurrentProgramSceneChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current preview scene has changed.
|
||||||
|
*
|
||||||
|
* @dataField sceneName | String | Name of the scene that was switched to
|
||||||
|
*
|
||||||
|
* @eventType CurrentPreviewSceneChanged
|
||||||
|
* @eventSubscription Scenes
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scenes
|
||||||
|
*/
|
||||||
void EventHandler::HandleCurrentPreviewSceneChanged()
|
void EventHandler::HandleCurrentPreviewSceneChanged()
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
|
||||||
@ -65,9 +133,24 @@ void EventHandler::HandleCurrentPreviewSceneChanged()
|
|||||||
BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of scenes has changed.
|
||||||
|
*
|
||||||
|
* TODO: Make OBS fire this event when scenes are reordered.
|
||||||
|
*
|
||||||
|
* @dataField scenes | Array<Object> | Updated array of scenes
|
||||||
|
*
|
||||||
|
* @eventType SceneListChanged
|
||||||
|
* @eventSubscription Scenes
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category scenes
|
||||||
|
*/
|
||||||
void EventHandler::HandleSceneListChanged()
|
void EventHandler::HandleSceneListChanged()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["scenes"] = Utils::Obs::ListHelper::GetSceneList();
|
eventData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList();
|
||||||
BroadcastEvent(EventSubscription::Scenes, "SceneListChanged", eventData);
|
BroadcastEvent(EventSubscription::Scenes, "SceneListChanged", eventData);
|
||||||
}
|
}
|
||||||
|
@ -19,26 +19,44 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
void EventHandler::HandleTransitionCreated(obs_source_t *source)
|
/**
|
||||||
|
* The current scene transition has changed.
|
||||||
|
*
|
||||||
|
* @dataField transitionName | String | Name of the new transition
|
||||||
|
*
|
||||||
|
* @eventType CurrentSceneTransitionChanged
|
||||||
|
* @eventSubscription Transitions
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api events
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
void EventHandler::HandleCurrentSceneTransitionChanged()
|
||||||
{
|
{
|
||||||
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["transitionName"] = obs_source_get_name(source);
|
eventData["transitionName"] = obs_source_get_name(transition);
|
||||||
eventData["transitionKind"] = obs_source_get_id(source);
|
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionChanged", eventData);
|
||||||
eventData["transitionFixed"] = obs_transition_fixed(source);
|
|
||||||
BroadcastEvent(EventSubscription::Transitions, "TransitionCreated", eventData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandler::HandleTransitionRemoved(obs_source_t *source)
|
/**
|
||||||
|
* 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;
|
json eventData;
|
||||||
eventData["transitionName"] = obs_source_get_name(source);
|
eventData["transitionDuration"] = obs_frontend_get_transition_duration();
|
||||||
BroadcastEvent(EventSubscription::Transitions, "TransitionRemoved", eventData);
|
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionDurationChanged", eventData);
|
||||||
}
|
|
||||||
|
|
||||||
void EventHandler::HandleTransitionNameChanged(obs_source_t *, std::string oldTransitionName, std::string transitionName)
|
|
||||||
{
|
|
||||||
json eventData;
|
|
||||||
eventData["oldTransitionName"] = oldTransitionName;
|
|
||||||
eventData["transitionName"] = transitionName;
|
|
||||||
BroadcastEvent(EventSubscription::Transitions, "TransitionNameChanged", eventData);
|
|
||||||
}
|
}
|
||||||
|
@ -19,3 +19,22 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Studio mode has been enabled or disabled.
|
||||||
|
*
|
||||||
|
* @dataField studioModeEnabled | Boolean | True == Enabled, False == Disabled
|
||||||
|
*
|
||||||
|
* @eventType StudioModeStateChanged
|
||||||
|
* @eventSubscription Ui
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @category ui
|
||||||
|
* @api events
|
||||||
|
*/
|
||||||
|
void EventHandler::HandleStudioModeStateChanged(bool enabled)
|
||||||
|
{
|
||||||
|
json eventData;
|
||||||
|
eventData["studioModeEnabled"] = enabled;
|
||||||
|
BroadcastEvent(EventSubscription::Ui, "StudioModeStateChanged", eventData);
|
||||||
|
}
|
@ -130,7 +130,6 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// Receive events in the `MediaInputs` category
|
|
||||||
MediaInputs = (1 << 8),
|
MediaInputs = (1 << 8),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `VendorEvent` event.
|
* Subscription value to receive the `VendorEvent` event.
|
||||||
@ -142,9 +141,19 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// Receive events from external OBS plugins and scripts
|
|
||||||
Vendors = (1 << 9),
|
Vendors = (1 << 9),
|
||||||
/**
|
/**
|
||||||
|
* Subscription value to receive events in the `Ui` category.
|
||||||
|
*
|
||||||
|
* @enumIdentifier Ui
|
||||||
|
* @enumValue (1 << 10)
|
||||||
|
* @enumType EventSubscription
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
|
Ui = (1 << 10),
|
||||||
|
/**
|
||||||
* Helper to receive all non-high-volume events.
|
* Helper to receive all non-high-volume events.
|
||||||
*
|
*
|
||||||
* @enumIdentifier All
|
* @enumIdentifier All
|
||||||
@ -154,8 +163,7 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// Receive all event categories (exclude high-volume)
|
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Ui | Vendors),
|
||||||
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors),
|
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
||||||
*
|
*
|
||||||
@ -166,7 +174,6 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// InputVolumeMeters event (high-volume)
|
|
||||||
InputVolumeMeters = (1 << 16),
|
InputVolumeMeters = (1 << 16),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputActiveStateChanged` high-volume event.
|
* Subscription value to receive the `InputActiveStateChanged` high-volume event.
|
||||||
@ -178,7 +185,6 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// InputActiveStateChanged event (high-volume)
|
|
||||||
InputActiveStateChanged = (1 << 17),
|
InputActiveStateChanged = (1 << 17),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputShowStateChanged` high-volume event.
|
* Subscription value to receive the `InputShowStateChanged` high-volume event.
|
||||||
@ -190,7 +196,6 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// InputShowStateChanged event (high-volume)
|
|
||||||
InputShowStateChanged = (1 << 18),
|
InputShowStateChanged = (1 << 18),
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `SceneItemTransformChanged` high-volume event.
|
* Subscription value to receive the `SceneItemTransformChanged` high-volume event.
|
||||||
@ -202,7 +207,6 @@ namespace EventSubscription {
|
|||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
// SceneItemTransformChanged event (high-volume)
|
|
||||||
SceneItemTransformChanged = (1 << 19),
|
SceneItemTransformChanged = (1 << 19),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@
|
|||||||
<number>65534</number>
|
<number>65534</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>4444</number>
|
<number>4455</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -35,12 +35,12 @@ OBS_MODULE_AUTHOR("OBSProject")
|
|||||||
const char *obs_module_name(void) { return "obs-websocket"; }
|
const char *obs_module_name(void) { return "obs-websocket"; }
|
||||||
const char *obs_module_description(void) { return obs_module_text("OBSWebSocket.Plugin.Description"); }
|
const char *obs_module_description(void) { return obs_module_text("OBSWebSocket.Plugin.Description"); }
|
||||||
|
|
||||||
|
os_cpu_usage_info_t* _cpuUsageInfo;
|
||||||
ConfigPtr _config;
|
ConfigPtr _config;
|
||||||
|
EventHandlerPtr _eventHandler;
|
||||||
WebSocketApiPtr _webSocketApi;
|
WebSocketApiPtr _webSocketApi;
|
||||||
WebSocketServerPtr _webSocketServer;
|
WebSocketServerPtr _webSocketServer;
|
||||||
EventHandlerPtr _eventHandler;
|
|
||||||
SettingsDialog *_settingsDialog = nullptr;
|
SettingsDialog *_settingsDialog = nullptr;
|
||||||
os_cpu_usage_info_t* _cpuUsageInfo;
|
|
||||||
|
|
||||||
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData);
|
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData);
|
||||||
|
|
||||||
@ -48,32 +48,37 @@ bool obs_module_load(void)
|
|||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION, OBS_WEBSOCKET_RPC_VERSION);
|
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_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);
|
||||||
|
|
||||||
// Create the config object then load the parameters from storage
|
// Initialize the cpu stats
|
||||||
|
_cpuUsageInfo = os_cpu_usage_info_start();
|
||||||
|
|
||||||
|
// Create the config manager then load the parameters from storage
|
||||||
_config = ConfigPtr(new Config());
|
_config = ConfigPtr(new Config());
|
||||||
_config->Load();
|
_config->Load();
|
||||||
|
|
||||||
// Initialize event handler before server, as the server configures the event handler.
|
// Initialize the event handler
|
||||||
_eventHandler = EventHandlerPtr(new EventHandler());
|
_eventHandler = EventHandlerPtr(new EventHandler());
|
||||||
|
|
||||||
_webSocketApi = WebSocketApiPtr(new WebSocketApi(WebSocketApiEventCallback));
|
// Initialize the plugin/script API
|
||||||
|
_webSocketApi = WebSocketApiPtr(new WebSocketApi());
|
||||||
|
_webSocketApi->SetEventCallback(WebSocketApiEventCallback);
|
||||||
|
|
||||||
|
// Initialize the WebSocket server
|
||||||
_webSocketServer = WebSocketServerPtr(new WebSocketServer());
|
_webSocketServer = WebSocketServerPtr(new WebSocketServer());
|
||||||
|
|
||||||
|
// Initialize the settings dialog
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = static_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();
|
||||||
|
|
||||||
|
// Add the settings dialog to the tools menu
|
||||||
const char* menuActionText = obs_module_text("OBSWebSocket.Settings.DialogTitle");
|
const char* menuActionText = obs_module_text("OBSWebSocket.Settings.DialogTitle");
|
||||||
QAction* menuAction = (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText);
|
QAction* menuAction = (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText);
|
||||||
QObject::connect(menuAction, &QAction::triggered, [] { _settingsDialog->ToggleShowHide(); });
|
QObject::connect(menuAction, &QAction::triggered, [] { _settingsDialog->ToggleShowHide(); });
|
||||||
|
|
||||||
_cpuUsageInfo = os_cpu_usage_info_start();
|
|
||||||
|
|
||||||
// Loading finished
|
|
||||||
blog(LOG_INFO, "[obs_module_load] Module loaded.");
|
blog(LOG_INFO, "[obs_module_load] Module loaded.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,29 +86,46 @@ void obs_module_unload()
|
|||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs_module_unload] Shutting down...");
|
blog(LOG_INFO, "[obs_module_unload] Shutting down...");
|
||||||
|
|
||||||
|
// Shutdown the WebSocket server if it is running
|
||||||
if (_webSocketServer->IsListening()) {
|
if (_webSocketServer->IsListening()) {
|
||||||
blog_debug("[obs_module_unload] WebSocket server is running. Stopping...");
|
blog_debug("[obs_module_unload] WebSocket server is running. Stopping...");
|
||||||
_webSocketServer->Stop();
|
_webSocketServer->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy the WebSocket server
|
||||||
_webSocketServer.reset();
|
_webSocketServer.reset();
|
||||||
|
|
||||||
_eventHandler.reset();
|
// Destroy the plugin/script api
|
||||||
|
|
||||||
_webSocketApi.reset();
|
_webSocketApi.reset();
|
||||||
|
|
||||||
|
// Destroy the event handler
|
||||||
|
_eventHandler.reset();
|
||||||
|
|
||||||
|
// Save and destroy the config manager
|
||||||
_config->Save();
|
_config->Save();
|
||||||
_config.reset();
|
_config.reset();
|
||||||
|
|
||||||
|
// Destroy the cpu stats
|
||||||
os_cpu_usage_info_destroy(_cpuUsageInfo);
|
os_cpu_usage_info_destroy(_cpuUsageInfo);
|
||||||
|
|
||||||
blog(LOG_INFO, "[obs_module_unload] Finished shutting down.");
|
blog(LOG_INFO, "[obs_module_unload] Finished shutting down.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os_cpu_usage_info_t* GetCpuUsageInfo()
|
||||||
|
{
|
||||||
|
return _cpuUsageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
ConfigPtr GetConfig()
|
ConfigPtr GetConfig()
|
||||||
{
|
{
|
||||||
return _config;
|
return _config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EventHandlerPtr GetEventHandler()
|
||||||
|
{
|
||||||
|
return _eventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
WebSocketApiPtr GetWebSocketApi()
|
WebSocketApiPtr GetWebSocketApi()
|
||||||
{
|
{
|
||||||
return _webSocketApi;
|
return _webSocketApi;
|
||||||
@ -114,32 +136,11 @@ WebSocketServerPtr GetWebSocketServer()
|
|||||||
return _webSocketServer;
|
return _webSocketServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventHandlerPtr GetEventHandler()
|
|
||||||
{
|
|
||||||
return _eventHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
os_cpu_usage_info_t* GetCpuUsageInfo()
|
|
||||||
{
|
|
||||||
return _cpuUsageInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsDebugEnabled()
|
bool IsDebugEnabled()
|
||||||
{
|
{
|
||||||
return !_config || _config->DebugEnabled;
|
return !_config || _config->DebugEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ___source_dummy_addref(obs_source_t*) {}
|
|
||||||
void ___weak_source_dummy_addref(obs_weak_source_t*) {}
|
|
||||||
void ___scene_dummy_addref(obs_scene_t*) {}
|
|
||||||
void ___sceneitem_dummy_addref(obs_sceneitem_t*) {}
|
|
||||||
void ___data_dummy_addref(obs_data_t*) {}
|
|
||||||
void ___data_array_dummy_addref(obs_data_array_t*) {}
|
|
||||||
void ___output_dummy_addref(obs_output_t*) {}
|
|
||||||
void ___data_item_dummy_addref(obs_data_item_t*) {}
|
|
||||||
void ___data_item_release(obs_data_item_t* dataItem){ obs_data_item_release(&dataItem); }
|
|
||||||
void ___properties_dummy_addref(obs_properties_t*) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event has been emitted from a vendor.
|
* An event has been emitted from a vendor.
|
||||||
*
|
*
|
||||||
|
@ -21,58 +21,31 @@ 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 "plugin-macros.generated.h"
|
#include "plugin-macros.generated.h"
|
||||||
|
|
||||||
// Autorelease object definitions
|
|
||||||
void ___source_dummy_addref(obs_source_t*);
|
|
||||||
void ___weak_source_dummy_addref(obs_weak_source_t*);
|
|
||||||
void ___scene_dummy_addref(obs_scene_t*);
|
|
||||||
void ___sceneitem_dummy_addref(obs_sceneitem_t*);
|
|
||||||
void ___data_dummy_addref(obs_data_t*);
|
|
||||||
void ___data_array_dummy_addref(obs_data_array_t*);
|
|
||||||
void ___output_dummy_addref(obs_output_t*);
|
|
||||||
void ___data_item_dummy_addref(obs_data_item_t*);
|
|
||||||
void ___data_item_release(obs_data_item_t*);
|
|
||||||
void ___properties_dummy_addref(obs_properties_t*);
|
|
||||||
|
|
||||||
using OBSSourceAutoRelease = OBSRef<obs_source_t*, ___source_dummy_addref, obs_source_release>;
|
|
||||||
using OBSWeakSourceAutoRelease = OBSRef<obs_weak_source_t*, ___weak_source_dummy_addref, obs_weak_source_release>;
|
|
||||||
using OBSSceneAutoRelease = OBSRef<obs_scene_t*, ___scene_dummy_addref, obs_scene_release>;
|
|
||||||
using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t*, ___sceneitem_dummy_addref, obs_sceneitem_release>;
|
|
||||||
using OBSDataAutoRelease = OBSRef<obs_data_t*, ___data_dummy_addref, obs_data_release>;
|
|
||||||
using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t*, ___data_array_dummy_addref, obs_data_array_release>;
|
|
||||||
using OBSOutputAutoRelease = OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>;
|
|
||||||
using OBSDataItemAutoRelease = OBSRef<obs_data_item_t*, ___data_item_dummy_addref, ___data_item_release>;
|
|
||||||
using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t*, ___properties_dummy_addref, obs_properties_destroy>;
|
|
||||||
|
|
||||||
class Config;
|
class Config;
|
||||||
typedef std::shared_ptr<Config> ConfigPtr;
|
typedef std::shared_ptr<Config> ConfigPtr;
|
||||||
|
|
||||||
|
class EventHandler;
|
||||||
|
typedef std::shared_ptr<EventHandler> EventHandlerPtr;
|
||||||
|
|
||||||
class WebSocketApi;
|
class WebSocketApi;
|
||||||
typedef std::shared_ptr<WebSocketApi> WebSocketApiPtr;
|
typedef std::shared_ptr<WebSocketApi> WebSocketApiPtr;
|
||||||
|
|
||||||
class WebSocketServer;
|
class WebSocketServer;
|
||||||
typedef std::shared_ptr<WebSocketServer> WebSocketServerPtr;
|
typedef std::shared_ptr<WebSocketServer> WebSocketServerPtr;
|
||||||
|
|
||||||
class EventHandler;
|
os_cpu_usage_info_t* GetCpuUsageInfo();
|
||||||
typedef std::shared_ptr<EventHandler> EventHandlerPtr;
|
|
||||||
|
|
||||||
ConfigPtr GetConfig();
|
ConfigPtr GetConfig();
|
||||||
|
|
||||||
|
EventHandlerPtr GetEventHandler();
|
||||||
|
|
||||||
WebSocketApiPtr GetWebSocketApi();
|
WebSocketApiPtr GetWebSocketApi();
|
||||||
|
|
||||||
WebSocketServerPtr GetWebSocketServer();
|
WebSocketServerPtr GetWebSocketServer();
|
||||||
|
|
||||||
EventHandlerPtr GetEventHandler();
|
|
||||||
|
|
||||||
os_cpu_usage_info_t* GetCpuUsageInfo();
|
|
||||||
|
|
||||||
bool IsDebugEnabled();
|
bool IsDebugEnabled();
|
||||||
|
@ -109,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 = reinterpret_cast<SerialFrameBatch*>(param);
|
auto serialFrameBatch = static_cast<SerialFrameBatch*>(param);
|
||||||
|
|
||||||
// Increment frame count
|
// Increment frame count
|
||||||
serialFrameBatch->frameCount++;
|
serialFrameBatch->frameCount++;
|
||||||
|
@ -17,9 +17,13 @@ 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::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||||
{
|
{
|
||||||
// General
|
// General
|
||||||
{"GetVersion", &RequestHandler::GetVersion},
|
{"GetVersion", &RequestHandler::GetVersion},
|
||||||
@ -29,8 +33,6 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
|||||||
{"GetHotkeyList", &RequestHandler::GetHotkeyList},
|
{"GetHotkeyList", &RequestHandler::GetHotkeyList},
|
||||||
{"TriggerHotkeyByName", &RequestHandler::TriggerHotkeyByName},
|
{"TriggerHotkeyByName", &RequestHandler::TriggerHotkeyByName},
|
||||||
{"TriggerHotkeyByKeySequence", &RequestHandler::TriggerHotkeyByKeySequence},
|
{"TriggerHotkeyByKeySequence", &RequestHandler::TriggerHotkeyByKeySequence},
|
||||||
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
|
||||||
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
|
||||||
{"Sleep", &RequestHandler::Sleep},
|
{"Sleep", &RequestHandler::Sleep},
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
@ -49,14 +51,18 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
|||||||
{"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},
|
||||||
@ -64,12 +70,15 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
|||||||
{"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}, // Disabled for now. Pending obs-studio#5276
|
{"RemoveInput", &RequestHandler::RemoveInput},
|
||||||
{"SetInputName", &RequestHandler::SetInputName},
|
{"SetInputName", &RequestHandler::SetInputName},
|
||||||
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
|
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
|
||||||
{"GetInputSettings", &RequestHandler::GetInputSettings},
|
{"GetInputSettings", &RequestHandler::GetInputSettings},
|
||||||
@ -79,13 +88,39 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
|||||||
{"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},
|
||||||
|
|
||||||
|
// Transitions
|
||||||
|
{"GetTransitionKindList", &RequestHandler::GetTransitionKindList},
|
||||||
|
{"GetSceneTransitionList", &RequestHandler::GetSceneTransitionList},
|
||||||
|
{"GetCurrentSceneTransition", &RequestHandler::GetCurrentSceneTransition},
|
||||||
|
{"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
|
||||||
|
{"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
|
||||||
|
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
|
||||||
|
{"GetCurrentSceneTransitionCursor", &RequestHandler::GetCurrentSceneTransitionCursor},
|
||||||
|
{"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},
|
||||||
{"GetGroupSceneItemList", &RequestHandler::GetGroupSceneItemList},
|
{"GetGroupSceneItemList", &RequestHandler::GetGroupSceneItemList},
|
||||||
@ -101,12 +136,27 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
|||||||
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
|
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
|
||||||
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
|
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
|
||||||
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
|
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
|
||||||
|
{"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode},
|
||||||
|
{"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode},
|
||||||
|
|
||||||
|
// 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},
|
||||||
|
|
||||||
// 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},
|
||||||
@ -116,13 +166,19 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
|||||||
{"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},
|
||||||
{"SetMediaInputCursor", &RequestHandler::SetMediaInputCursor},
|
{"SetMediaInputCursor", &RequestHandler::SetMediaInputCursor},
|
||||||
{"OffsetMediaInputCursor", &RequestHandler::OffsetMediaInputCursor},
|
{"OffsetMediaInputCursor", &RequestHandler::OffsetMediaInputCursor},
|
||||||
{"TriggerMediaInputAction", &RequestHandler::TriggerMediaInputAction},
|
{"TriggerMediaInputAction", &RequestHandler::TriggerMediaInputAction},
|
||||||
|
|
||||||
|
// Ui
|
||||||
|
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
||||||
|
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
||||||
|
{"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog},
|
||||||
|
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
||||||
|
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
||||||
};
|
};
|
||||||
|
|
||||||
RequestHandler::RequestHandler(SessionPtr session) :
|
RequestHandler::RequestHandler(SessionPtr session) :
|
||||||
@ -132,6 +188,10 @@ RequestHandler::RequestHandler(SessionPtr 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.");
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <unordered_map>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
@ -51,8 +51,6 @@ class RequestHandler {
|
|||||||
RequestResult GetHotkeyList(const Request&);
|
RequestResult GetHotkeyList(const Request&);
|
||||||
RequestResult TriggerHotkeyByName(const Request&);
|
RequestResult TriggerHotkeyByName(const Request&);
|
||||||
RequestResult TriggerHotkeyByKeySequence(const Request&);
|
RequestResult TriggerHotkeyByKeySequence(const Request&);
|
||||||
RequestResult GetStudioModeEnabled(const Request&);
|
|
||||||
RequestResult SetStudioModeEnabled(const Request&);
|
|
||||||
RequestResult Sleep(const Request&);
|
RequestResult Sleep(const Request&);
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
@ -71,14 +69,18 @@ class RequestHandler {
|
|||||||
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&);
|
||||||
@ -86,10 +88,13 @@ class RequestHandler {
|
|||||||
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&);
|
||||||
@ -101,13 +106,39 @@ class RequestHandler {
|
|||||||
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&);
|
||||||
|
|
||||||
|
// Transitions
|
||||||
|
RequestResult GetTransitionKindList(const Request&);
|
||||||
|
RequestResult GetSceneTransitionList(const Request&);
|
||||||
|
RequestResult GetCurrentSceneTransition(const Request&);
|
||||||
|
RequestResult SetCurrentSceneTransition(const Request&);
|
||||||
|
RequestResult SetCurrentSceneTransitionDuration(const Request&);
|
||||||
|
RequestResult SetCurrentSceneTransitionSettings(const Request&);
|
||||||
|
RequestResult GetCurrentSceneTransitionCursor(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&);
|
||||||
RequestResult GetGroupSceneItemList(const Request&);
|
RequestResult GetGroupSceneItemList(const Request&);
|
||||||
@ -123,12 +154,27 @@ class RequestHandler {
|
|||||||
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&);
|
||||||
|
|
||||||
|
// 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&);
|
||||||
|
|
||||||
// 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&);
|
||||||
@ -138,7 +184,6 @@ class RequestHandler {
|
|||||||
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&);
|
||||||
@ -146,6 +191,13 @@ class RequestHandler {
|
|||||||
RequestResult OffsetMediaInputCursor(const Request&);
|
RequestResult OffsetMediaInputCursor(const Request&);
|
||||||
RequestResult TriggerMediaInputAction(const Request&);
|
RequestResult TriggerMediaInputAction(const Request&);
|
||||||
|
|
||||||
|
// Ui
|
||||||
|
RequestResult GetStudioModeEnabled(const Request&);
|
||||||
|
RequestResult SetStudioModeEnabled(const Request&);
|
||||||
|
RequestResult OpenInputPropertiesDialog(const Request&);
|
||||||
|
RequestResult OpenInputFiltersDialog(const Request&);
|
||||||
|
RequestResult OpenInputInteractDialog(const Request&);
|
||||||
|
|
||||||
SessionPtr _session;
|
SessionPtr _session;
|
||||||
static const std::map<std::string, RequestMethodHandler> _handlerMap;
|
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
||||||
};
|
};
|
||||||
|
@ -124,7 +124,7 @@ RequestResult RequestHandler::GetSceneCollectionList(const Request&)
|
|||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["currentSceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
responseData["currentSceneCollectionName"] = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
responseData["sceneCollections"] = Utils::Obs::ListHelper::GetSceneCollectionList();
|
responseData["sceneCollections"] = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
|
|||||||
|
|
||||||
std::string sceneCollectionName = request.RequestData["sceneCollectionName"];
|
std::string sceneCollectionName = request.RequestData["sceneCollectionName"];
|
||||||
|
|
||||||
auto sceneCollections = Utils::Obs::ListHelper::GetSceneCollectionList();
|
auto sceneCollections = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) == sceneCollections.end())
|
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) == sceneCollections.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
|
|||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentSceneCollectionName != sceneCollectionName) {
|
if (currentSceneCollectionName != sceneCollectionName) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
obs_frontend_set_current_scene_collection(reinterpret_cast<const char*>(param));
|
obs_frontend_set_current_scene_collection(static_cast<const char*>(param));
|
||||||
}, (void*)sceneCollectionName.c_str(), true);
|
}, (void*)sceneCollectionName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,11 +189,11 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
|
|||||||
|
|
||||||
std::string sceneCollectionName = request.RequestData["sceneCollectionName"];
|
std::string sceneCollectionName = request.RequestData["sceneCollectionName"];
|
||||||
|
|
||||||
auto sceneCollections = Utils::Obs::ListHelper::GetSceneCollectionList();
|
auto sceneCollections = Utils::Obs::ArrayHelper::GetSceneCollectionList();
|
||||||
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 = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
bool success = false;
|
bool success = false;
|
||||||
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -219,7 +219,7 @@ RequestResult RequestHandler::GetProfileList(const Request&)
|
|||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["currentProfileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
|
responseData["currentProfileName"] = Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
responseData["profiles"] = Utils::Obs::ListHelper::GetProfileList();
|
responseData["profiles"] = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +244,7 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
|
|||||||
|
|
||||||
std::string profileName = request.RequestData["profileName"];
|
std::string profileName = request.RequestData["profileName"];
|
||||||
|
|
||||||
auto profiles = Utils::Obs::ListHelper::GetProfileList();
|
auto profiles = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
if (std::find(profiles.begin(), profiles.end(), profileName) == profiles.end())
|
if (std::find(profiles.begin(), profiles.end(), profileName) == profiles.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
|
|||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentProfileName != profileName) {
|
if (currentProfileName != profileName) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
obs_frontend_set_current_profile(reinterpret_cast<const char*>(param));
|
obs_frontend_set_current_profile(static_cast<const char*>(param));
|
||||||
}, (void*)profileName.c_str(), true);
|
}, (void*)profileName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,11 +280,11 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
|
|||||||
|
|
||||||
std::string profileName = request.RequestData["profileName"];
|
std::string profileName = request.RequestData["profileName"];
|
||||||
|
|
||||||
auto profiles = Utils::Obs::ListHelper::GetProfileList();
|
auto profiles = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
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 = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -311,14 +311,14 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
|
|||||||
|
|
||||||
std::string profileName = request.RequestData["profileName"];
|
std::string profileName = request.RequestData["profileName"];
|
||||||
|
|
||||||
auto profiles = Utils::Obs::ListHelper::GetProfileList();
|
auto profiles = Utils::Obs::ArrayHelper::GetProfileList();
|
||||||
if (std::find(profiles.begin(), profiles.end(), profileName) == profiles.end())
|
if (std::find(profiles.begin(), profiles.end(), profileName) == profiles.end())
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
return RequestResult::Error(RequestStatus::ResourceNotFound);
|
||||||
|
|
||||||
if (profiles.size() < 2)
|
if (profiles.size() < 2)
|
||||||
return RequestResult::Error(RequestStatus::NotEnoughResources);
|
return RequestResult::Error(RequestStatus::NotEnoughResources);
|
||||||
|
|
||||||
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -334,7 +334,7 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
|
|||||||
* @responseField defaultParameterValue | String | Default value associated with the parameter. `null` if no default
|
* @responseField defaultParameterValue | String | Default value associated with the parameter. `null` if no default
|
||||||
*
|
*
|
||||||
* @requestType GetProfileParameter
|
* @requestType GetProfileParameter
|
||||||
* @complexity 3
|
* @complexity 4
|
||||||
* @rpcVersion -1
|
* @rpcVersion -1
|
||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @category config
|
* @category config
|
||||||
@ -378,7 +378,7 @@ RequestResult RequestHandler::GetProfileParameter(const Request& request)
|
|||||||
* @requestField parameterValue | String | Value of the parameter to set. Use `null` to delete
|
* @requestField parameterValue | String | Value of the parameter to set. Use `null` to delete
|
||||||
*
|
*
|
||||||
* @requestType SetProfileParameter
|
* @requestType SetProfileParameter
|
||||||
* @complexity 3
|
* @complexity 4
|
||||||
* @rpcVersion -1
|
* @rpcVersion -1
|
||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @category config
|
* @category config
|
||||||
@ -408,6 +408,8 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,3 +594,23 @@ 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);
|
||||||
|
}
|
||||||
|
334
src/requesthandler/RequestHandler_Filters.cpp
Normal file
334
src/requesthandler/RequestHandler_Filters.cpp
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 | Number | 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();
|
||||||
|
}
|
@ -84,7 +84,7 @@ RequestResult RequestHandler::GetVersion(const Request&)
|
|||||||
*/
|
*/
|
||||||
RequestResult RequestHandler::GetStats(const Request&)
|
RequestResult RequestHandler::GetStats(const Request&)
|
||||||
{
|
{
|
||||||
json responseData = Utils::Obs::DataHelper::GetStats();
|
json responseData = Utils::Obs::ObjectHelper::GetStats();
|
||||||
|
|
||||||
responseData["webSocketSessionIncomingMessages"] = _session->IncomingMessages();
|
responseData["webSocketSessionIncomingMessages"] = _session->IncomingMessages();
|
||||||
responseData["webSocketSessionOutgoingMessages"] = _session->OutgoingMessages();
|
responseData["webSocketSessionOutgoingMessages"] = _session->OutgoingMessages();
|
||||||
@ -195,7 +195,7 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
|
|||||||
RequestResult RequestHandler::GetHotkeyList(const Request&)
|
RequestResult RequestHandler::GetHotkeyList(const Request&)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["hotkeys"] = Utils::Obs::ListHelper::GetHotkeyNameList();
|
responseData["hotkeys"] = Utils::Obs::ArrayHelper::GetHotkeyNameList();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,58 +288,6 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets whether studio is enabled.
|
|
||||||
*
|
|
||||||
* @responseField studioModeEnabled | Boolean | Whether studio mode is enabled
|
|
||||||
*
|
|
||||||
* @requestType GetStudioModeEnabled
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category general
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::GetStudioModeEnabled(const Request&)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active();
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables studio mode
|
|
||||||
*
|
|
||||||
* @requestField studioModeEnabled | Boolean | True == Enabled, False == Disabled
|
|
||||||
*
|
|
||||||
* @requestType SetStudioModeEnabled
|
|
||||||
* @complexity 1
|
|
||||||
* @rpcVersion -1
|
|
||||||
* @initialVersion 5.0.0
|
|
||||||
* @category general
|
|
||||||
* @api requests
|
|
||||||
*/
|
|
||||||
RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
|
|
||||||
{
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
|
||||||
std::string comment;
|
|
||||||
if (!request.ValidateBoolean("studioModeEnabled", statusCode, comment))
|
|
||||||
return RequestResult::Error(statusCode, comment);
|
|
||||||
|
|
||||||
// Avoid queueing tasks if nothing will change
|
|
||||||
if (obs_frontend_preview_program_mode_active() != request.RequestData["studioModeEnabled"]) {
|
|
||||||
// (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"];
|
|
||||||
// Queue the task inside of the UI thread to prevent race conditions
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
|
||||||
auto studioModeEnabled = (bool*)param;
|
|
||||||
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
|
||||||
}, &studioModeEnabled, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestResult::Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sleeps for a time duration or number of frames. Only available in request batches with types `SERIAL_REALTIME` or `SERIAL_FRAME`.
|
* Sleeps for a time duration or number of frames. Only available in request batches with types `SERIAL_REALTIME` or `SERIAL_FRAME`.
|
||||||
*
|
*
|
||||||
|
@ -47,7 +47,7 @@ RequestResult RequestHandler::GetInputList(const Request& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["inputs"] = Utils::Obs::ListHelper::GetInputList(inputKind);
|
responseData["inputs"] = Utils::Obs::ArrayHelper::GetInputList(inputKind);
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,44 @@ RequestResult RequestHandler::GetInputKindList(const Request& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["inputKinds"] = Utils::Obs::ListHelper::GetInputKindList(unversioned);
|
responseData["inputKinds"] = Utils::Obs::ArrayHelper::GetInputKindList(unversioned);
|
||||||
|
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);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +152,7 @@ RequestResult RequestHandler::CreateInput(const Request& request)
|
|||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that input name.");
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that input name.");
|
||||||
|
|
||||||
std::string inputKind = request.RequestData["inputKind"];
|
std::string inputKind = request.RequestData["inputKind"];
|
||||||
auto kinds = Utils::Obs::ListHelper::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(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.");
|
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.");
|
||||||
|
|
||||||
@ -232,6 +269,9 @@ 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)
|
||||||
@ -278,8 +318,9 @@ RequestResult RequestHandler::GetInputSettings(const Request& request)
|
|||||||
/**
|
/**
|
||||||
* Sets the settings of an input.
|
* Sets the settings of an input.
|
||||||
*
|
*
|
||||||
* @requestField inputName | String | Name of the input to set the settings of
|
* @requestField inputName | String | Name of the input to set the settings of
|
||||||
* @requestField inputSettings | Object | Object of settings to apply
|
* @requestField inputSettings | 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 SetInputSettings
|
* @requestType SetInputSettings
|
||||||
* @complexity 3
|
* @complexity 3
|
||||||
@ -345,6 +386,9 @@ 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);
|
||||||
@ -371,6 +415,9 @@ 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();
|
||||||
@ -398,6 +445,9 @@ 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);
|
||||||
|
|
||||||
@ -429,6 +479,9 @@ 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)
|
||||||
@ -445,7 +498,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
|
||||||
@ -462,6 +515,9 @@ 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);
|
||||||
@ -487,6 +543,67 @@ 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.
|
||||||
*
|
*
|
||||||
@ -511,6 +628,9 @@ 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;
|
||||||
@ -539,6 +659,9 @@ 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);
|
||||||
|
|
||||||
@ -573,6 +696,9 @@ 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"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
|
responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
|
||||||
|
|
||||||
@ -600,6 +726,12 @@ 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")
|
||||||
@ -616,30 +748,93 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<json> GetListPropertyItems(obs_property_t *property)
|
/**
|
||||||
|
* 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)
|
||||||
{
|
{
|
||||||
std::vector<json> ret;
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
||||||
|
if (!input)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
enum obs_combo_format itemFormat = obs_property_list_format(property);
|
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
||||||
size_t itemCount = obs_property_list_item_count(property);
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
||||||
|
|
||||||
for (size_t i = 0; i < itemCount; i++) {
|
long long tracks = obs_source_get_audio_mixers(input);
|
||||||
json itemData;
|
|
||||||
itemData["itemName"] = obs_property_list_item_name(property, i);
|
json inputAudioTracks;
|
||||||
itemData["itemEnabled"] = !obs_property_list_item_disabled(property, i);
|
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
|
||||||
if (itemFormat == OBS_COMBO_FORMAT_INT) {
|
inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1);
|
||||||
itemData["itemValue"] = obs_property_list_item_int(property, i);
|
|
||||||
} else if (itemFormat == OBS_COMBO_FORMAT_FLOAT) {
|
|
||||||
itemData["itemValue"] = obs_property_list_item_float(property, i);
|
|
||||||
} else if (itemFormat == OBS_COMBO_FORMAT_STRING) {
|
|
||||||
itemData["itemValue"] = obs_property_list_item_string(property, i);
|
|
||||||
} else {
|
|
||||||
itemData["itemValue"] = nullptr;
|
|
||||||
}
|
|
||||||
ret.push_back(itemData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -677,7 +872,7 @@ RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request&
|
|||||||
return RequestResult::Error(RequestStatus::InvalidResourceType, "The property found is not a list.");
|
return RequestResult::Error(RequestStatus::InvalidResourceType, "The property found is not a list.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["propertyItems"] = GetListPropertyItems(property);
|
responseData["propertyItems"] = Utils::Obs::ArrayHelper::GetListPropertyItems(property);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,32 @@ bool IsMediaTimeValid(obs_source_t *input)
|
|||||||
return mediaState == OBS_MEDIA_STATE_PLAYING || mediaState == OBS_MEDIA_STATE_PAUSED;
|
return mediaState == OBS_MEDIA_STATE_PLAYING || mediaState == OBS_MEDIA_STATE_PAUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the status of a media input.
|
||||||
|
*
|
||||||
|
* Media States:
|
||||||
|
* - `OBS_MEDIA_STATE_NONE`
|
||||||
|
* - `OBS_MEDIA_STATE_PLAYING`
|
||||||
|
* - `OBS_MEDIA_STATE_OPENING`
|
||||||
|
* - `OBS_MEDIA_STATE_BUFFERING`
|
||||||
|
* - `OBS_MEDIA_STATE_PAUSED`
|
||||||
|
* - `OBS_MEDIA_STATE_STOPPED`
|
||||||
|
* - `OBS_MEDIA_STATE_ENDED`
|
||||||
|
* - `OBS_MEDIA_STATE_ERROR`
|
||||||
|
*
|
||||||
|
* @requestField inputName | String | Name of the media input
|
||||||
|
*
|
||||||
|
* @responseField mediaState | String | State of the media input
|
||||||
|
* @responseField mediaDuration | Number | Total duration of the playing media in milliseconds. `null` if not playing
|
||||||
|
* @responseField mediaCursor | Number | Position of the cursor in milliseconds. `null` if not playing
|
||||||
|
*
|
||||||
|
* @requestType GetMediaInputStatus
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
|
RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -47,6 +73,21 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the cursor position of a media input.
|
||||||
|
*
|
||||||
|
* This request does not perform bounds checking of the cursor position.
|
||||||
|
*
|
||||||
|
* @requestField inputName | String | Name of the media input
|
||||||
|
* @requestField mediaCursor | Number | New cursor position to set | >= 0
|
||||||
|
*
|
||||||
|
* @requestType SetMediaInputCursor
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
|
RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -66,6 +107,21 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offsets the current cursor position of a media input by the specified value.
|
||||||
|
*
|
||||||
|
* This request does not perform bounds checking of the cursor position.
|
||||||
|
*
|
||||||
|
* @requestField inputName | String | Name of the media input
|
||||||
|
* @requestField mediaCursorOffset | Number | Value to offset the current cursor position by | None
|
||||||
|
*
|
||||||
|
* @requestType OffsetMediaInputCursor
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
|
RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -88,6 +144,19 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers an action on a media input.
|
||||||
|
*
|
||||||
|
* @requestField inputName | String | Name of the media input
|
||||||
|
* @requestField mediaAction | String | Identifier of the `ObsMediaInputAction` enum
|
||||||
|
*
|
||||||
|
* @requestType TriggerMediaInputAction
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category media inputs
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::TriggerMediaInputAction(const Request& request)
|
RequestResult RequestHandler::TriggerMediaInputAction(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
277
src/requesthandler/RequestHandler_Outputs.cpp
Normal file
277
src/requesthandler/RequestHandler_Outputs.cpp
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/*
|
||||||
|
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::GetLastReplayBufferFilePath();
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
@ -19,9 +19,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the status of the record output.
|
||||||
|
*
|
||||||
|
* @responseField outputActive | Boolean | Whether the output is active
|
||||||
|
* @responseField ouputPaused | Boolean | Whether the output is paused
|
||||||
|
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
||||||
|
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
||||||
|
* @responseField outputBytes | Number | Number of bytes sent by the output
|
||||||
|
*
|
||||||
|
* @requestType GetRecordStatus
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetRecordStatus(const Request&)
|
RequestResult RequestHandler::GetRecordStatus(const Request&)
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease recordOutput = obs_frontend_get_streaming_output();
|
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
|
||||||
|
|
||||||
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(recordOutput);
|
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(recordOutput);
|
||||||
|
|
||||||
@ -35,6 +51,16 @@ RequestResult RequestHandler::GetRecordStatus(const Request&)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the status of the record output.
|
||||||
|
*
|
||||||
|
* @requestType ToggleRecord
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::ToggleRecord(const Request&)
|
RequestResult RequestHandler::ToggleRecord(const Request&)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
@ -49,6 +75,16 @@ RequestResult RequestHandler::ToggleRecord(const Request&)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the record output.
|
||||||
|
*
|
||||||
|
* @requestType StartRecord
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::StartRecord(const Request&)
|
RequestResult RequestHandler::StartRecord(const Request&)
|
||||||
{
|
{
|
||||||
if (obs_frontend_recording_active())
|
if (obs_frontend_recording_active())
|
||||||
@ -60,6 +96,16 @@ RequestResult RequestHandler::StartRecord(const Request&)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the record output.
|
||||||
|
*
|
||||||
|
* @requestType StopRecord
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::StopRecord(const Request&)
|
RequestResult RequestHandler::StopRecord(const Request&)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_recording_active())
|
if (!obs_frontend_recording_active())
|
||||||
@ -71,6 +117,16 @@ RequestResult RequestHandler::StopRecord(const Request&)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles pause on the record output.
|
||||||
|
*
|
||||||
|
* @requestType ToggleRecordPause
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::ToggleRecordPause(const Request&)
|
RequestResult RequestHandler::ToggleRecordPause(const Request&)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
@ -85,6 +141,16 @@ RequestResult RequestHandler::ToggleRecordPause(const Request&)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the record output.
|
||||||
|
*
|
||||||
|
* @requestType PauseRecord
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::PauseRecord(const Request&)
|
RequestResult RequestHandler::PauseRecord(const Request&)
|
||||||
{
|
{
|
||||||
if (obs_frontend_recording_paused())
|
if (obs_frontend_recording_paused())
|
||||||
@ -96,6 +162,16 @@ RequestResult RequestHandler::PauseRecord(const Request&)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes the record output.
|
||||||
|
*
|
||||||
|
* @requestType ResumeRecord
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category record
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::ResumeRecord(const Request&)
|
RequestResult RequestHandler::ResumeRecord(const Request&)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_recording_paused())
|
if (!obs_frontend_recording_paused())
|
||||||
@ -106,11 +182,3 @@ RequestResult RequestHandler::ResumeRecord(const Request&)
|
|||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestResult RequestHandler::GetRecordDirectory(const Request&)
|
|
||||||
{
|
|
||||||
json responseData;
|
|
||||||
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
|
||||||
}
|
|
||||||
|
@ -19,6 +19,22 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of all scene items in a scene.
|
||||||
|
*
|
||||||
|
* Scenes only
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene to get the items of
|
||||||
|
*
|
||||||
|
* @responseField sceneItems | Array<Object> | Array of scene items in the scene
|
||||||
|
*
|
||||||
|
* @requestType GetSceneItemList
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemList(const Request& request)
|
RequestResult RequestHandler::GetSceneItemList(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -28,11 +44,29 @@ RequestResult RequestHandler::GetSceneItemList(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItems"] = Utils::Obs::ListHelper::GetSceneItemList(obs_scene_from_source(scene));
|
responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_from_source(scene));
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basically GetSceneItemList, but for groups.
|
||||||
|
*
|
||||||
|
* Using groups at all in OBS is discouraged, as they are very broken under the hood.
|
||||||
|
*
|
||||||
|
* Groups only
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the group to get the items of
|
||||||
|
*
|
||||||
|
* @responseField sceneItems | Array<Object> | Array of scene items in the group
|
||||||
|
*
|
||||||
|
* @requestType GetGroupItemList
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetGroupSceneItemList(const Request& request)
|
RequestResult RequestHandler::GetGroupSceneItemList(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -42,26 +76,36 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItems"] = Utils::Obs::ListHelper::GetSceneItemList(obs_group_from_source(scene));
|
responseData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(obs_group_from_source(scene));
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches a scene for a source, and returns its id.
|
||||||
|
*
|
||||||
|
* Scenes and Groups
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene or group to search in
|
||||||
|
* @requestField sourceName | String | Name of the source to find
|
||||||
|
*
|
||||||
|
* @responseField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
*
|
||||||
|
* @requestType GetSceneItemId
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneAutoRelease scene = request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(sceneSource && request.ValidateString("sourceName", statusCode, comment)))
|
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSScene scene = obs_scene_from_source(sceneSource);
|
|
||||||
if (!scene) {
|
|
||||||
scene = obs_group_from_source(sceneSource);
|
|
||||||
if (!scene) // This should never happen
|
|
||||||
return RequestResult::Error(RequestStatus::GenericError, "Somehow the scene was found but the scene object could not be fetched. Please report this to the obs-websocket developers.");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string sourceName = request.RequestData["sourceName"];
|
std::string sourceName = request.RequestData["sourceName"];
|
||||||
|
|
||||||
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName);
|
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName);
|
||||||
@ -74,6 +118,24 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new scene item using a source.
|
||||||
|
*
|
||||||
|
* Scenes only
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene to create the new item in
|
||||||
|
* @requestField sourceName | String | Name of the source to add to the scene
|
||||||
|
* @requestField ?sceneItemEnabled | Boolean | Enable state to apply to the scene item on creation | True
|
||||||
|
*
|
||||||
|
* @responseField sceneItemId | Number | Numeric ID of the scene item
|
||||||
|
*
|
||||||
|
* @requestType CreateSceneItem
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::CreateSceneItem(const Request& request)
|
RequestResult RequestHandler::CreateSceneItem(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -108,6 +170,21 @@ RequestResult RequestHandler::CreateSceneItem(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a scene item from a scene.
|
||||||
|
*
|
||||||
|
* Scenes only
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene the item is in
|
||||||
|
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
|
||||||
|
*
|
||||||
|
* @requestType RemoveSceneItem
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::RemoveSceneItem(const Request& request)
|
RequestResult RequestHandler::RemoveSceneItem(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -116,11 +193,30 @@ RequestResult RequestHandler::RemoveSceneItem(const Request& request)
|
|||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
// Makes the UI log `User Removed source '[source]' from scene '(null)'`. This is not a problem, just a side effect.
|
||||||
obs_sceneitem_remove(sceneItem);
|
obs_sceneitem_remove(sceneItem);
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicates a scene item, copying all transform and crop info.
|
||||||
|
*
|
||||||
|
* Scenes only
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene the item is in
|
||||||
|
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
|
||||||
|
* @requestField ?destinationSceneName | String | Name of the scene to create the duplicated item in | `sceneName` is assumed
|
||||||
|
*
|
||||||
|
* @responseField sceneItemId | Number | Numeric ID of the duplicated scene item
|
||||||
|
*
|
||||||
|
* @requestType DuplicateSceneItem
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -136,8 +232,9 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
|||||||
if (!destinationScene)
|
if (!destinationScene)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
} else {
|
} else {
|
||||||
destinationScene = obs_sceneitem_get_scene(sceneItem);
|
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
|
||||||
obs_scene_addref(destinationScene);
|
if (!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) {
|
||||||
@ -165,6 +262,23 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the transform and crop info 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
|
||||||
|
*
|
||||||
|
* @responseField sceneItemTransform | Object | Object containing scene item transform info
|
||||||
|
*
|
||||||
|
* @requestType GetSceneItemTransform
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
|
RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -174,11 +288,25 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemTransform"] = Utils::Obs::DataHelper::GetSceneItemTransform(sceneItem);
|
responseData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the transform and crop info of a scene item.
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene the item is in
|
||||||
|
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
|
||||||
|
* @requestField sceneItemTransform | Object | Object containing scene item transform info to update
|
||||||
|
*
|
||||||
|
* @requestType SetSceneItemTransform
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -317,6 +445,23 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the enable state 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
|
||||||
|
*
|
||||||
|
* @responseField sceneItemEnabled | Boolean | Whether the scene item is enabled. `true` for enabled, `false` for disabled
|
||||||
|
*
|
||||||
|
* @requestType GetSceneItemEnabled
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
|
RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -331,6 +476,22 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the enable state 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 sceneItemEnabled | Boolean | New enable state of the scene item
|
||||||
|
*
|
||||||
|
* @requestType SetSceneItemEnabled
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
|
RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -346,6 +507,23 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the lock state 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
|
||||||
|
*
|
||||||
|
* @responseField sceneItemLocked | Boolean | Whether the scene item is locked. `true` for locked, `false` for unlocked
|
||||||
|
*
|
||||||
|
* @requestType GetSceneItemLocked
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
|
RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -360,6 +538,22 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the lock state of a scene item.
|
||||||
|
*
|
||||||
|
* Scenes and Group
|
||||||
|
*
|
||||||
|
* @requestField sceneName | String | Name of the scene the item is in
|
||||||
|
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
|
||||||
|
* @requestField sceneItemLocked | Boolean | New lock state of the scene item
|
||||||
|
*
|
||||||
|
* @requestType SetSceneItemLocked
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
|
RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -375,6 +569,25 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the index position of a scene item in a scene.
|
||||||
|
*
|
||||||
|
* An index of 0 is at the bottom of the source list in the UI.
|
||||||
|
*
|
||||||
|
* 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 sceneItemIndex | Number | Index position of the scene item
|
||||||
|
*
|
||||||
|
* @requestType GetSceneItemIndex
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
|
RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -389,6 +602,22 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the index position of a scene item in a scene.
|
||||||
|
*
|
||||||
|
* 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 sceneItemIndex | Number | New index position of the scene item | >= 0
|
||||||
|
*
|
||||||
|
* @requestType SetSceneItemIndex
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category scene items
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
|
RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
|
||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
@ -403,3 +632,81 @@ 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"] = Utils::Obs::StringHelper::GetSceneItemBlendMode(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);
|
||||||
|
|
||||||
|
std::string blendModeString = request.RequestData["sceneItemBlendMode"];
|
||||||
|
|
||||||
|
auto blendMode = Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString);
|
||||||
|
if (blendMode == OBS_BLEND_NORMAL && blendModeString != "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();
|
||||||
|
}
|
||||||
|
@ -22,9 +22,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
/**
|
/**
|
||||||
* Gets an array of all scenes in OBS.
|
* Gets an array of all scenes in OBS.
|
||||||
*
|
*
|
||||||
* @responseField scenes | Array<String> | Array of scenes in OBS
|
|
||||||
* @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
|
||||||
*
|
*
|
||||||
* @requestType GetSceneList
|
* @requestType GetSceneList
|
||||||
* @complexity 2
|
* @complexity 2
|
||||||
@ -49,7 +49,30 @@ RequestResult RequestHandler::GetSceneList(const Request&)
|
|||||||
else
|
else
|
||||||
responseData["currentPreviewSceneName"] = nullptr;
|
responseData["currentPreviewSceneName"] = nullptr;
|
||||||
|
|
||||||
responseData["scenes"] = Utils::Obs::ListHelper::GetSceneList();
|
responseData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList();
|
||||||
|
|
||||||
|
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);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -250,3 +273,105 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
|
|||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
|
@ -312,3 +312,39 @@ 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();
|
||||||
|
}
|
||||||
|
@ -19,6 +19,24 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the status of the stream output.
|
||||||
|
*
|
||||||
|
* @responseField outputActive | Boolean | Whether the output is active
|
||||||
|
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
|
||||||
|
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
||||||
|
* @responseField outputDuration | Number | Current duration in milliseconds for 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 GetStreamStatus
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category stream
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::GetStreamStatus(const Request&)
|
RequestResult RequestHandler::GetStreamStatus(const Request&)
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
|
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
|
||||||
@ -37,6 +55,18 @@ RequestResult RequestHandler::GetStreamStatus(const Request&)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the status of the stream output.
|
||||||
|
*
|
||||||
|
* @responseField outputActive | Boolean | New state of the stream output
|
||||||
|
*
|
||||||
|
* @requestType ToggleStream
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category stream
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::ToggleStream(const Request&)
|
RequestResult RequestHandler::ToggleStream(const Request&)
|
||||||
{
|
{
|
||||||
json responseData;
|
json responseData;
|
||||||
@ -51,6 +81,16 @@ RequestResult RequestHandler::ToggleStream(const Request&)
|
|||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the stream output.
|
||||||
|
*
|
||||||
|
* @requestType StartStream
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category stream
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::StartStream(const Request&)
|
RequestResult RequestHandler::StartStream(const Request&)
|
||||||
{
|
{
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
@ -62,6 +102,16 @@ RequestResult RequestHandler::StartStream(const Request&)
|
|||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the stream output.
|
||||||
|
*
|
||||||
|
* @requestType StopStream
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category stream
|
||||||
|
*/
|
||||||
RequestResult RequestHandler::StopStream(const Request&)
|
RequestResult RequestHandler::StopStream(const Request&)
|
||||||
{
|
{
|
||||||
if (!obs_frontend_streaming_active())
|
if (!obs_frontend_streaming_active())
|
||||||
@ -72,3 +122,35 @@ 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();
|
||||||
|
}
|
||||||
|
322
src/requesthandler/RequestHandler_Transitions.cpp
Normal file
322
src/requesthandler/RequestHandler_Transitions.cpp
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
/*
|
||||||
|
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 <math.h>
|
||||||
|
|
||||||
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of all available transition kinds.
|
||||||
|
*
|
||||||
|
* Similar to `GetInputKindList`
|
||||||
|
*
|
||||||
|
* @responseField transitionKinds | Array<String> | Array of transition kinds
|
||||||
|
*
|
||||||
|
* @requestType GetTransitionKindList
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetTransitionKindList(const Request&)
|
||||||
|
{
|
||||||
|
json responseData;
|
||||||
|
responseData["transitionKinds"] = Utils::Obs::ArrayHelper::GetTransitionKindList();
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of all scene transitions in OBS.
|
||||||
|
*
|
||||||
|
* @responseField currentSceneTransitionName | String | Name of the current scene transition. Can be null
|
||||||
|
* @responseField currentSceneTransitionKind | String | Kind of the current scene transition. Can be null
|
||||||
|
* @responseField transitions | Array<Object> | Array of transitions
|
||||||
|
*
|
||||||
|
* @requestType GetSceneTransitionList
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetSceneTransitionList(const Request&)
|
||||||
|
{
|
||||||
|
json responseData;
|
||||||
|
|
||||||
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
|
if (transition) {
|
||||||
|
responseData["currentSceneTransitionName"] = obs_source_get_name(transition);
|
||||||
|
responseData["currentSceneTransitionKind"] = obs_source_get_id(transition);
|
||||||
|
} else {
|
||||||
|
responseData["currentSceneTransitionName"] = nullptr;
|
||||||
|
responseData["currentSceneTransitionKind"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData["transitions"] = Utils::Obs::ArrayHelper::GetSceneTransitionList();
|
||||||
|
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets information about the current scene transition.
|
||||||
|
*
|
||||||
|
* @responseField transitionName | String | Name of the transition
|
||||||
|
* @responseField transitionKind | String | Kind of the transition
|
||||||
|
* @responseField transitionFixed | Boolean | Whether the transition uses a fixed (unconfigurable) duration
|
||||||
|
* @responseField transitionDuration | Number | Configured transition duration in milliseconds. `null` if transition is fixed
|
||||||
|
* @responseField transitionConfigurable | Boolean | Whether the transition supports being configured
|
||||||
|
* @responseField transitionSettings | Object | Object of settings for the transition. `null` if transition is not configurable
|
||||||
|
*
|
||||||
|
* @requestType GetCurrentSceneTransition
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetCurrentSceneTransition(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["transitionName"] = obs_source_get_name(transition);
|
||||||
|
responseData["transitionKind"] = obs_source_get_id(transition);
|
||||||
|
|
||||||
|
if (obs_transition_fixed(transition)) {
|
||||||
|
responseData["transitionFixed"] = true;
|
||||||
|
responseData["transitionDuration"] = nullptr;
|
||||||
|
} else {
|
||||||
|
responseData["transitionFixed"] = false;
|
||||||
|
responseData["transitionDuration"] = obs_frontend_get_transition_duration();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obs_source_configurable(transition)) {
|
||||||
|
responseData["transitionConfigurable"] = true;
|
||||||
|
OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition);
|
||||||
|
responseData["transitionSettings"] = Utils::Json::ObsDataToJson(transitionSettings);
|
||||||
|
} else {
|
||||||
|
responseData["transitionConfigurable"] = false;
|
||||||
|
responseData["transitionSettings"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current scene transition.
|
||||||
|
*
|
||||||
|
* Small note: While the namespace of scene transitions is generally unique, that uniqueness is not a guarantee as it is with other resources like inputs.
|
||||||
|
*
|
||||||
|
* @requestField transitionName | String | Name of the transition to make active
|
||||||
|
*
|
||||||
|
* @requestType SetCurrentSceneTransition
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::SetCurrentSceneTransition(const Request& request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
if (!request.ValidateString("transitionName", statusCode, comment))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
std::string transitionName = request.RequestData["transitionName"];
|
||||||
|
|
||||||
|
OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(transitionName);
|
||||||
|
if (!transition)
|
||||||
|
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
|
||||||
|
|
||||||
|
obs_frontend_set_current_transition(transition);
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the duration of the current scene transition, if it is not fixed.
|
||||||
|
*
|
||||||
|
* @requestField transitionDuration | Number | Duration in milliseconds | >= 50, <= 20000
|
||||||
|
*
|
||||||
|
* @requestType SetCurrentSceneTransitionDuration
|
||||||
|
* @complexity 2
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::SetCurrentSceneTransitionDuration(const Request& request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
if (!request.ValidateNumber("transitionDuration", statusCode, comment, 50, 20000))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
int transitionDuration = request.RequestData["transitionDuration"];
|
||||||
|
|
||||||
|
obs_frontend_set_transition_duration(transitionDuration);
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the settings of the current scene transition.
|
||||||
|
*
|
||||||
|
* @requestField transitionSettings | Object | Settings object to apply to the transition. Can be `{}`
|
||||||
|
* @requestField ?overlay | Boolean | Whether to overlay over the current settings or replace them | true
|
||||||
|
*
|
||||||
|
* @requestType SetCurrentSceneTransitionSettings
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
if (!request.ValidateObject("transitionSettings", statusCode, comment, true))
|
||||||
|
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!
|
||||||
|
|
||||||
|
if (!obs_source_configurable(transition))
|
||||||
|
return RequestResult::Error(RequestStatus::ResourceNotConfigurable, "The current transition does not support custom settings.");
|
||||||
|
|
||||||
|
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["transitionSettings"]);
|
||||||
|
if (!newSettings)
|
||||||
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
|
if (overlay)
|
||||||
|
obs_source_update(transition, newSettings);
|
||||||
|
else
|
||||||
|
obs_source_reset_settings(transition, newSettings);
|
||||||
|
|
||||||
|
obs_source_update_properties(transition);
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @requestType TriggerStudioModeTransition
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category transitions
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::TriggerStudioModeTransition(const Request&)
|
||||||
|
{
|
||||||
|
if (!obs_frontend_preview_program_mode_active())
|
||||||
|
return RequestResult::Error(RequestStatus::StudioModeNotActive);
|
||||||
|
|
||||||
|
OBSSourceAutoRelease previewScene = obs_frontend_get_current_preview_scene();
|
||||||
|
|
||||||
|
obs_frontend_set_current_scene(previewScene);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
150
src/requesthandler/RequestHandler_Ui.cpp
Normal file
150
src/requesthandler/RequestHandler_Ui.cpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether studio is enabled.
|
||||||
|
*
|
||||||
|
* @responseField studioModeEnabled | Boolean | Whether studio mode is enabled
|
||||||
|
*
|
||||||
|
* @requestType GetStudioModeEnabled
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @category ui
|
||||||
|
* @api requests
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetStudioModeEnabled(const Request&)
|
||||||
|
{
|
||||||
|
json responseData;
|
||||||
|
responseData["studioModeEnabled"] = obs_frontend_preview_program_mode_active();
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables studio mode
|
||||||
|
*
|
||||||
|
* @requestField studioModeEnabled | Boolean | True == Enabled, False == Disabled
|
||||||
|
*
|
||||||
|
* @requestType SetStudioModeEnabled
|
||||||
|
* @complexity 1
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @category ui
|
||||||
|
* @api requests
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
if (!request.ValidateBoolean("studioModeEnabled", statusCode, comment))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
// Avoid queueing tasks if nothing will change
|
||||||
|
if (obs_frontend_preview_program_mode_active() != request.RequestData["studioModeEnabled"]) {
|
||||||
|
// (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"];
|
||||||
|
// Queue the task inside of the UI thread to prevent race conditions
|
||||||
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
|
auto studioModeEnabled = (bool*)param;
|
||||||
|
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
||||||
|
}, &studioModeEnabled, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
@ -264,18 +264,14 @@ 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;
|
||||||
}
|
}
|
||||||
OBSScene ret = obs_group_from_source(sceneSource);
|
return obs_scene_get_ref(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;
|
||||||
}
|
}
|
||||||
OBSScene ret = obs_scene_from_source(sceneSource);
|
return obs_scene_get_ref(obs_scene_from_source(sceneSource));
|
||||||
obs_scene_addref(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,24 +291,35 @@ 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, 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
|
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease sceneSource = ValidateScene(sceneKeyName, statusCode, comment, filter);
|
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
||||||
if (!sceneSource)
|
if (!scene)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!ValidateNumber(sceneItemIdKeyName, statusCode, comment, 0))
|
if (!ValidateNumber(sceneItemIdKeyName, statusCode, comment, 0))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
OBSScene scene = obs_scene_from_source(sceneSource);
|
|
||||||
if (!scene) {
|
|
||||||
scene = obs_group_from_source(sceneSource);
|
|
||||||
if (!scene) { // This should never happen
|
|
||||||
statusCode = RequestStatus::GenericError;
|
|
||||||
comment = "Somehow the scene was found but the scene object could not be fetched. Please report this to the obs-websocket developers.";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t sceneItemId = RequestData[sceneItemIdKeyName];
|
int64_t sceneItemId = RequestData[sceneItemIdKeyName];
|
||||||
|
|
||||||
|
@ -29,6 +29,12 @@ 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 FilterPair {
|
||||||
|
OBSSourceAutoRelease source;
|
||||||
|
OBSSourceAutoRelease filter;
|
||||||
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request
|
||||||
{
|
{
|
||||||
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
||||||
@ -53,6 +59,7 @@ struct Request
|
|||||||
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, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, 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_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_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;
|
||||||
|
|
||||||
std::string RequestType;
|
std::string RequestType;
|
||||||
|
@ -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 {
|
enum RequestBatchExecutionType: int8_t {
|
||||||
/**
|
/**
|
||||||
* Not a request batch.
|
* Not a request batch.
|
||||||
*
|
*
|
||||||
@ -77,7 +77,7 @@ namespace RequestBatchExecutionType {
|
|||||||
Parallel = 2,
|
Parallel = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(uint8_t executionType)
|
inline bool IsValid(int8_t executionType)
|
||||||
{
|
{
|
||||||
return executionType >= None && executionType <= Parallel;
|
return executionType >= None && executionType <= Parallel;
|
||||||
}
|
}
|
||||||
|
@ -332,6 +332,30 @@ namespace RequestStatus {
|
|||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
InvalidInputKind = 605,
|
InvalidInputKind = 605,
|
||||||
|
/**
|
||||||
|
* The resource does not support being configured.
|
||||||
|
*
|
||||||
|
* This is particularly relevant to transitions, where they do not always have changeable settings.
|
||||||
|
*
|
||||||
|
* @enumIdentifier ResourceNotConfigurable
|
||||||
|
* @enumValue 606
|
||||||
|
* @enumType RequestStatus
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
@ -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(json j)
|
bool Utils::Json::JsonArrayIsValidObsArray(const json &j)
|
||||||
{
|
{
|
||||||
for (auto it : j) {
|
for (auto it : j) {
|
||||||
if (!it.is_object())
|
if (!it.is_object())
|
||||||
@ -191,7 +191,7 @@ bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Json::SetJsonFileContent(std::string fileName, json content, bool createNew)
|
bool Utils::Json::SetJsonFileContent(std::string fileName, const 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);
|
||||||
|
@ -27,10 +27,11 @@ using json = nlohmann::json;
|
|||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Json {
|
namespace Json {
|
||||||
bool JsonArrayIsValidObsArray(json j);
|
bool JsonArrayIsValidObsArray(const 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, json content, bool createNew = true);
|
bool SetJsonFileContent(std::string fileName, const json &content, bool createNew = true);
|
||||||
|
static inline bool Contains(const json &j, std::string key) { return j.contains(key) && !j[key].is_null(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,562 +17,5 @@ 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 <inttypes.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <QString>
|
|
||||||
#include <obs-frontend-api.h>
|
|
||||||
#include <util/config-file.h>
|
|
||||||
#include <util/util_uint64.h>
|
|
||||||
|
|
||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "../obs-websocket.h"
|
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
|
||||||
|
|
||||||
#define RET_COMPARE(str, x) if (str == #x) return x;
|
|
||||||
|
|
||||||
std::vector<std::string> ConvertStringArray(char **array)
|
|
||||||
{
|
|
||||||
std::vector<std::string> ret;
|
|
||||||
if (!array)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
size_t index = 0;
|
|
||||||
char* value = nullptr;
|
|
||||||
do {
|
|
||||||
value = array[index];
|
|
||||||
if (value)
|
|
||||||
ret.push_back(value);
|
|
||||||
index++;
|
|
||||||
} while (value);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetObsVersion()
|
|
||||||
{
|
|
||||||
uint32_t version = obs_get_version();
|
|
||||||
|
|
||||||
uint8_t major, minor, patch;
|
|
||||||
major = (version >> 24) & 0xFF;
|
|
||||||
minor = (version >> 16) & 0xFF;
|
|
||||||
patch = version & 0xFF;
|
|
||||||
|
|
||||||
QString combined = QString("%1.%2.%3").arg(major).arg(minor).arg(patch);
|
|
||||||
return combined.toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetCurrentSceneCollection()
|
|
||||||
{
|
|
||||||
char *sceneCollectionName = obs_frontend_get_current_scene_collection();
|
|
||||||
std::string ret = sceneCollectionName;
|
|
||||||
bfree(sceneCollectionName);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetCurrentProfile()
|
|
||||||
{
|
|
||||||
char *profileName = obs_frontend_get_current_profile();
|
|
||||||
std::string ret = profileName;
|
|
||||||
bfree(profileName);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
|
|
||||||
{
|
|
||||||
char *profilePath = obs_frontend_get_current_profile_path();
|
|
||||||
std::string ret = profilePath;
|
|
||||||
bfree(profilePath);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
|
|
||||||
{
|
|
||||||
//char *recordOutputPath = obs_frontend_get_current_record_output_path();
|
|
||||||
//std::string ret = recordOutputPath;
|
|
||||||
//bfree(recordOutputPath);
|
|
||||||
//return ret;
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
|
|
||||||
{
|
|
||||||
obs_source_type sourceType = obs_source_get_type(source);
|
|
||||||
|
|
||||||
switch (sourceType) {
|
|
||||||
default:
|
|
||||||
CASE(OBS_SOURCE_TYPE_INPUT)
|
|
||||||
CASE(OBS_SOURCE_TYPE_FILTER)
|
|
||||||
CASE(OBS_SOURCE_TYPE_TRANSITION)
|
|
||||||
CASE(OBS_SOURCE_TYPE_SCENE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetInputMonitorType(obs_source_t *input)
|
|
||||||
{
|
|
||||||
obs_monitoring_type monitorType = obs_source_get_monitoring_type(input);
|
|
||||||
|
|
||||||
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::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();
|
|
||||||
calldata_t cd = {0};
|
|
||||||
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
|
||||||
proc_handler_call(ph, "get_last_replay", &cd);
|
|
||||||
auto ret = calldata_string(&cd, "path");
|
|
||||||
calldata_free(&cd);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
uint64_t secs = ms / 1000ULL;
|
|
||||||
uint64_t minutes = secs / 60ULL;
|
|
||||||
|
|
||||||
uint64_t hoursPart = minutes / 60ULL;
|
|
||||||
uint64_t minutesPart = minutes % 60ULL;
|
|
||||||
uint64_t secsPart = secs % 60ULL;
|
|
||||||
uint64_t msPart = ms % 1000ULL;
|
|
||||||
|
|
||||||
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
|
||||||
return formatted.toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t Utils::Obs::NumberHelper::GetOutputDuration(obs_output_t *output)
|
|
||||||
{
|
|
||||||
if (!output || !obs_output_active(output))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
video_t* video = obs_output_video(output);
|
|
||||||
uint64_t frameTimeNs = video_output_get_frame_time(video);
|
|
||||||
int totalFrames = obs_output_get_total_frames(output);
|
|
||||||
|
|
||||||
return util_mul_div64(totalFrames, frameTimeNs, 1000000ULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Utils::Obs::NumberHelper::GetSceneCount()
|
|
||||||
{
|
|
||||||
size_t ret;
|
|
||||||
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
|
||||||
auto ret = reinterpret_cast<size_t*>(param);
|
|
||||||
|
|
||||||
if (obs_source_is_group(scene))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
(*ret)++;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
obs_enum_scenes(sceneEnumProc, &ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ListHelper::GetSceneCollectionList()
|
|
||||||
{
|
|
||||||
char** sceneCollections = obs_frontend_get_scene_collections();
|
|
||||||
auto ret = ConvertStringArray(sceneCollections);
|
|
||||||
bfree(sceneCollections);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ListHelper::GetProfileList()
|
|
||||||
{
|
|
||||||
char** profiles = obs_frontend_get_profiles();
|
|
||||||
auto ret = ConvertStringArray(profiles);
|
|
||||||
bfree(profiles);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<obs_hotkey_t *> Utils::Obs::ListHelper::GetHotkeyList()
|
|
||||||
{
|
|
||||||
std::vector<obs_hotkey_t *> ret;
|
|
||||||
|
|
||||||
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
|
|
||||||
auto ret = reinterpret_cast<std::vector<obs_hotkey_t *> *>(data);
|
|
||||||
|
|
||||||
ret->push_back(hotkey);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, &ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ListHelper::GetHotkeyNameList()
|
|
||||||
{
|
|
||||||
auto hotkeys = GetHotkeyList();
|
|
||||||
|
|
||||||
std::vector<std::string> ret;
|
|
||||||
for (auto hotkey : hotkeys) {
|
|
||||||
ret.emplace_back(obs_hotkey_get_name(hotkey));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ListHelper::GetSceneList()
|
|
||||||
{
|
|
||||||
obs_frontend_source_list sceneList = {};
|
|
||||||
obs_frontend_get_scenes(&sceneList);
|
|
||||||
|
|
||||||
std::vector<json> ret;
|
|
||||||
for (size_t i = 0; i < sceneList.sources.num; i++) {
|
|
||||||
obs_source_t *scene = sceneList.sources.array[i];
|
|
||||||
|
|
||||||
if (obs_source_is_group(scene))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
json sceneJson;
|
|
||||||
sceneJson["sceneName"] = obs_source_get_name(scene);
|
|
||||||
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
|
|
||||||
|
|
||||||
ret.push_back(sceneJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_source_list_free(&sceneList);
|
|
||||||
|
|
||||||
// Reverse the vector order to match other array returns
|
|
||||||
std::reverse(ret.begin(), ret.end());
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ListHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
|
|
||||||
{
|
|
||||||
std::pair<std::vector<json>, bool> enumData;
|
|
||||||
enumData.second = basic;
|
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
|
|
||||||
auto enumData = reinterpret_cast<std::pair<std::vector<json>, bool>*>(param);
|
|
||||||
|
|
||||||
json item;
|
|
||||||
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
|
||||||
// Should be slightly faster than calling obs_sceneitem_get_order_position()
|
|
||||||
item["sceneItemIndex"] = enumData->first.size();
|
|
||||||
if (!enumData->second) {
|
|
||||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
|
||||||
item["sourceName"] = obs_source_get_name(itemSource);
|
|
||||||
item["sourceType"] = StringHelper::GetSourceType(itemSource);
|
|
||||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
|
||||||
item["inputKind"] = obs_source_get_id(itemSource);
|
|
||||||
else
|
|
||||||
item["inputKind"] = nullptr;
|
|
||||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_SCENE)
|
|
||||||
item["isGroup"] = obs_source_is_group(itemSource);
|
|
||||||
else
|
|
||||||
item["isGroup"] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumData->first.push_back(item);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, &enumData);
|
|
||||||
|
|
||||||
return enumData.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ListHelper::GetTransitionList()
|
|
||||||
{
|
|
||||||
obs_frontend_source_list transitionList = {};
|
|
||||||
obs_frontend_get_transitions(&transitionList);
|
|
||||||
|
|
||||||
std::vector<json> ret;
|
|
||||||
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
|
||||||
obs_source_t *transition = transitionList.sources.array[i];
|
|
||||||
json transitionJson;
|
|
||||||
transitionJson["transitionName"] = obs_source_get_name(transition);
|
|
||||||
transitionJson["transitionKind"] = obs_source_get_id(transition);
|
|
||||||
transitionJson["transitionFixed"] = obs_transition_fixed(transition);
|
|
||||||
ret.push_back(transitionJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_source_list_free(&transitionList);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EnumInputInfo {
|
|
||||||
std::string inputKind; // For searching by input kind
|
|
||||||
std::vector<json> inputs;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<json> Utils::Obs::ListHelper::GetInputList(std::string inputKind)
|
|
||||||
{
|
|
||||||
EnumInputInfo inputInfo;
|
|
||||||
inputInfo.inputKind = inputKind;
|
|
||||||
|
|
||||||
auto inputEnumProc = [](void *param, obs_source_t *input) {
|
|
||||||
// Sanity check in case the API changes
|
|
||||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
auto inputInfo = reinterpret_cast<EnumInputInfo*>(param);
|
|
||||||
|
|
||||||
std::string inputKind = obs_source_get_id(input);
|
|
||||||
|
|
||||||
if (!inputInfo->inputKind.empty() && inputInfo->inputKind != inputKind)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
json inputJson;
|
|
||||||
inputJson["inputName"] = obs_source_get_name(input);
|
|
||||||
inputJson["inputKind"] = inputKind;
|
|
||||||
inputJson["unversionedInputKind"] = obs_source_get_unversioned_id(input);
|
|
||||||
|
|
||||||
inputInfo->inputs.push_back(inputJson);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
// Actually enumerates only public inputs, despite the name
|
|
||||||
obs_enum_sources(inputEnumProc, &inputInfo);
|
|
||||||
|
|
||||||
return inputInfo.inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Utils::Obs::ListHelper::GetInputKindList(bool unversioned, bool includeDisabled)
|
|
||||||
{
|
|
||||||
std::vector<std::string> ret;
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
const char *kind;
|
|
||||||
const char *unversioned_kind;
|
|
||||||
while (obs_enum_input_types2(idx++, &kind, &unversioned_kind)) {
|
|
||||||
uint32_t caps = obs_get_source_output_flags(kind);
|
|
||||||
|
|
||||||
if (!includeDisabled && (caps & OBS_SOURCE_CAP_DISABLED) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (unversioned)
|
|
||||||
ret.push_back(unversioned_kind);
|
|
||||||
else
|
|
||||||
ret.push_back(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
json Utils::Obs::DataHelper::GetStats()
|
|
||||||
{
|
|
||||||
json ret;
|
|
||||||
|
|
||||||
config_t* currentProfile = obs_frontend_get_profile_config();
|
|
||||||
const char* outputMode = config_get_string(currentProfile, "Output", "Mode");
|
|
||||||
const char* recordPath = strcmp(outputMode, "Advanced") ? config_get_string(currentProfile, "SimpleOutput", "FilePath") : config_get_string(currentProfile, "AdvOut", "RecFilePath");
|
|
||||||
|
|
||||||
video_t* video = obs_get_video();
|
|
||||||
|
|
||||||
ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
|
|
||||||
ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
|
|
||||||
ret["availableDiskSpace"] = (double)os_get_free_disk_space(recordPath) / (1024.0 * 1024.0);
|
|
||||||
ret["activeFps"] = obs_get_active_fps();
|
|
||||||
ret["averageFrameRenderTime"] = (double)obs_get_average_frame_time_ns() / 1000000.0;
|
|
||||||
ret["renderSkippedFrames"] = obs_get_lagged_frames();
|
|
||||||
ret["renderTotalFrames"] = obs_get_total_frames();
|
|
||||||
ret["outputSkippedFrames"] = video_output_get_skipped_frames(video);
|
|
||||||
ret["outputTotalFrames"] = video_output_get_total_frames(video);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
json Utils::Obs::DataHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
|
||||||
{
|
|
||||||
json ret;
|
|
||||||
|
|
||||||
obs_transform_info osi;
|
|
||||||
obs_sceneitem_crop crop;
|
|
||||||
obs_sceneitem_get_info(item, &osi);
|
|
||||||
obs_sceneitem_get_crop(item, &crop);
|
|
||||||
|
|
||||||
OBSSource source = obs_sceneitem_get_source(item);
|
|
||||||
float sourceWidth = float(obs_source_get_width(source));
|
|
||||||
float sourceHeight = float(obs_source_get_height(source));
|
|
||||||
|
|
||||||
ret["sourceWidth"] = sourceWidth;
|
|
||||||
ret["sourceHeight"] = sourceHeight;
|
|
||||||
|
|
||||||
ret["positionX"] = osi.pos.x;
|
|
||||||
ret["positionY"] = osi.pos.y;
|
|
||||||
|
|
||||||
ret["rotation"] = osi.rot;
|
|
||||||
|
|
||||||
ret["scaleX"] = osi.scale.x;
|
|
||||||
ret["scaleY"] = osi.scale.y;
|
|
||||||
|
|
||||||
ret["width"] = osi.scale.x * sourceWidth;
|
|
||||||
ret["height"] = osi.scale.y * sourceHeight;
|
|
||||||
|
|
||||||
ret["alignment"] = osi.alignment;
|
|
||||||
|
|
||||||
ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
|
|
||||||
ret["boundsAlignment"] = osi.bounds_alignment;
|
|
||||||
ret["boundsWidth"] = osi.bounds.x;
|
|
||||||
ret["boundsHeight"] = osi.bounds.y;
|
|
||||||
|
|
||||||
ret["cropLeft"] = int(crop.left);
|
|
||||||
ret["cropRight"] = int(crop.right);
|
|
||||||
ret["cropTop"] = int(crop.top);
|
|
||||||
ret["cropBottom"] = int(crop.bottom);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
|
|
||||||
{
|
|
||||||
if (name.empty())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto hotkeys = ListHelper::GetHotkeyList();
|
|
||||||
|
|
||||||
for (auto hotkey : hotkeys) {
|
|
||||||
if (obs_hotkey_get_name(hotkey) == name)
|
|
||||||
return hotkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increments item ref. Use OBSSceneItemAutoRelease
|
|
||||||
obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name)
|
|
||||||
{
|
|
||||||
if (name.empty())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Finds first matching scene item in scene, search starts at index 0
|
|
||||||
OBSSceneItem ret = obs_scene_find_source(scene, name.c_str());
|
|
||||||
obs_sceneitem_addref(ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CreateSceneItemData {
|
|
||||||
obs_source_t *source; // In
|
|
||||||
bool sceneItemEnabled; // In
|
|
||||||
obs_transform_info *sceneItemTransform = nullptr; // In
|
|
||||||
obs_sceneitem_crop *sceneItemCrop = nullptr; // In
|
|
||||||
OBSSceneItem sceneItem; // Out
|
|
||||||
};
|
|
||||||
|
|
||||||
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
|
||||||
{
|
|
||||||
auto *data = reinterpret_cast<CreateSceneItemData*>(_data);
|
|
||||||
data->sceneItem = obs_scene_add(scene, data->source);
|
|
||||||
|
|
||||||
if (data->sceneItemTransform)
|
|
||||||
obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
|
|
||||||
|
|
||||||
if (data->sceneItemCrop)
|
|
||||||
obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
|
|
||||||
|
|
||||||
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_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
|
|
||||||
{
|
|
||||||
// Sanity check for valid scene
|
|
||||||
if (!(source && scene))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Create data struct and populate for scene item creation
|
|
||||||
CreateSceneItemData data;
|
|
||||||
data.source = source;
|
|
||||||
data.sceneItemEnabled = sceneItemEnabled;
|
|
||||||
data.sceneItemTransform = sceneItemTransform;
|
|
||||||
data.sceneItemCrop = sceneItemCrop;
|
|
||||||
|
|
||||||
// Enter graphics context and create the scene item
|
|
||||||
obs_enter_graphics();
|
|
||||||
obs_scene_atomic_update(scene, CreateSceneItemHelper, &data);
|
|
||||||
obs_leave_graphics();
|
|
||||||
|
|
||||||
obs_sceneitem_addref(data.sceneItem);
|
|
||||||
|
|
||||||
return data.sceneItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
|
|
||||||
{
|
|
||||||
// Create the input
|
|
||||||
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
|
||||||
|
|
||||||
// Check that everything was created properly
|
|
||||||
if (!input)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Apparently not all default input properties actually get applied on creation (smh)
|
|
||||||
uint32_t flags = obs_source_get_output_flags(input);
|
|
||||||
if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0)
|
|
||||||
obs_source_set_monitoring_type(input, OBS_MONITORING_TYPE_MONITOR_ONLY);
|
|
||||||
|
|
||||||
// Create a scene item for the input
|
|
||||||
obs_sceneitem_t *ret = CreateSceneItem(input, scene, sceneItemEnabled);
|
|
||||||
|
|
||||||
// If creation failed, remove the input
|
|
||||||
if (!ret)
|
|
||||||
obs_source_remove(input);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
124
src/utils/Obs.h
124
src/utils/Obs.h
@ -21,33 +21,132 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
|
// Autorelease object definitions
|
||||||
|
inline void ___properties_dummy_addref(obs_properties_t*){}
|
||||||
|
using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t*, ___properties_dummy_addref, obs_properties_destroy>;
|
||||||
|
|
||||||
|
#if !defined(OBS_AUTORELEASE)
|
||||||
|
inline void ___source_dummy_addref(obs_source_t*){}
|
||||||
|
inline void ___scene_dummy_addref(obs_scene_t*){}
|
||||||
|
inline void ___sceneitem_dummy_addref(obs_sceneitem_t*){}
|
||||||
|
inline void ___data_dummy_addref(obs_data_t*){}
|
||||||
|
inline void ___data_array_dummy_addref(obs_data_array_t*){}
|
||||||
|
inline void ___output_dummy_addref(obs_output_t*){}
|
||||||
|
inline void ___encoder_dummy_addref(obs_encoder_t *){}
|
||||||
|
inline void ___service_dummy_addref(obs_service_t *){}
|
||||||
|
|
||||||
|
inline void ___weak_source_dummy_addref(obs_weak_source_t*){}
|
||||||
|
inline void ___weak_output_dummy_addref(obs_weak_output_t *){}
|
||||||
|
inline void ___weak_encoder_dummy_addref(obs_weak_encoder_t *){}
|
||||||
|
inline void ___weak_service_dummy_addref(obs_weak_service_t *){}
|
||||||
|
|
||||||
|
using OBSSourceAutoRelease = OBSRef<obs_source_t*, ___source_dummy_addref, obs_source_release>;
|
||||||
|
using OBSSceneAutoRelease = OBSRef<obs_scene_t*, ___scene_dummy_addref, obs_scene_release>;
|
||||||
|
using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t*, ___sceneitem_dummy_addref, obs_sceneitem_release>;
|
||||||
|
using OBSDataAutoRelease = OBSRef<obs_data_t*, ___data_dummy_addref, obs_data_release>;
|
||||||
|
using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t*, ___data_array_dummy_addref, obs_data_array_release>;
|
||||||
|
using OBSOutputAutoRelease = OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>;
|
||||||
|
using OBSEncoderAutoRelease = OBSRef<obs_encoder_t *, ___encoder_dummy_addref, obs_encoder_release>;
|
||||||
|
using OBSServiceAutoRelease = OBSRef<obs_service_t *, ___service_dummy_addref, obs_service_release>;
|
||||||
|
|
||||||
|
using OBSWeakSourceAutoRelease = OBSRef<obs_weak_source_t*, ___weak_source_dummy_addref, obs_weak_source_release>;
|
||||||
|
using OBSWeakOutputAutoRelease = OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref, obs_weak_output_release>;
|
||||||
|
using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref, obs_weak_encoder_release>;
|
||||||
|
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
|
||||||
|
#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 reinterpret_cast<T*>(ptr);
|
return static_cast<T*>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ObsOutputState {
|
enum ObsOutputState {
|
||||||
|
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_RECONNECTING,
|
OBS_WEBSOCKET_OUTPUT_RECONNECTING,
|
||||||
OBS_WEBSOCKET_OUTPUT_PAUSED,
|
OBS_WEBSOCKET_OUTPUT_PAUSED,
|
||||||
OBS_WEBSOCKET_OUTPUT_RESUMED
|
OBS_WEBSOCKET_OUTPUT_RESUMED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ObsMediaInputAction {
|
enum ObsMediaInputAction {
|
||||||
|
/**
|
||||||
|
* No action.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE,
|
||||||
|
/**
|
||||||
|
* Play the media input.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY,
|
||||||
|
/**
|
||||||
|
* Pause the media input.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE,
|
||||||
|
/**
|
||||||
|
* Stop the media input.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP,
|
||||||
|
/**
|
||||||
|
* Restart the media input.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART,
|
||||||
|
/**
|
||||||
|
* Go to the next playlist item.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT,
|
||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS
|
/**
|
||||||
|
* Go to the previous playlist item.
|
||||||
|
*
|
||||||
|
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS
|
||||||
|
* @enumType ObsMediaInputAction
|
||||||
|
* @rpcVersion 1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api enums
|
||||||
|
*/
|
||||||
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
@ -59,48 +158,61 @@ namespace Utils {
|
|||||||
std::string GetCurrentProfilePath();
|
std::string GetCurrentProfilePath();
|
||||||
std::string GetCurrentRecordOutputPath();
|
std::string GetCurrentRecordOutputPath();
|
||||||
std::string GetSourceType(obs_source_t *source);
|
std::string GetSourceType(obs_source_t *source);
|
||||||
|
std::string GetInputMonitorType(enum obs_monitoring_type monitorType);
|
||||||
std::string GetInputMonitorType(obs_source_t *input);
|
std::string GetInputMonitorType(obs_source_t *input);
|
||||||
std::string GetMediaInputState(obs_source_t *input);
|
std::string GetMediaInputState(obs_source_t *input);
|
||||||
std::string GetLastReplayBufferFilePath();
|
std::string GetLastReplayBufferFilePath();
|
||||||
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
||||||
|
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
|
||||||
std::string DurationToTimecode(uint64_t);
|
std::string DurationToTimecode(uint64_t);
|
||||||
|
std::string GetOutputState(ObsOutputState state);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace EnumHelper {
|
namespace EnumHelper {
|
||||||
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
||||||
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
||||||
|
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ListHelper {
|
namespace ArrayHelper {
|
||||||
std::vector<std::string> GetSceneCollectionList();
|
std::vector<std::string> GetSceneCollectionList();
|
||||||
std::vector<std::string> GetProfileList();
|
std::vector<std::string> GetProfileList();
|
||||||
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> GetTransitionList();
|
|
||||||
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<std::string> GetTransitionKindList();
|
||||||
|
std::vector<json> GetSceneTransitionList();
|
||||||
|
std::vector<json> GetSourceFilterList(obs_source_t *source);
|
||||||
|
std::vector<std::string> GetFilterKindList();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DataHelper {
|
namespace ObjectHelper {
|
||||||
json GetStats();
|
json GetStats();
|
||||||
json GetSceneItemTransform(obs_sceneitem_t *item);
|
json GetSceneItemTransform(obs_sceneitem_t *item);
|
||||||
}
|
}
|
||||||
|
|
||||||
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_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name); // Increments ref. Use OBSSceneItemAutoRelease
|
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ActionHelper {
|
namespace ActionHelper {
|
||||||
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
|
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
|
||||||
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_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
src/utils/Obs_ActionHelper.cpp
Normal file
116
src/utils/Obs_ActionHelper.cpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
struct CreateSceneItemData {
|
||||||
|
obs_source_t *source; // In
|
||||||
|
bool sceneItemEnabled; // In
|
||||||
|
obs_transform_info *sceneItemTransform = nullptr; // In
|
||||||
|
obs_sceneitem_crop *sceneItemCrop = nullptr; // In
|
||||||
|
OBSSceneItem sceneItem; // Out
|
||||||
|
};
|
||||||
|
|
||||||
|
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
||||||
|
{
|
||||||
|
auto *data = static_cast<CreateSceneItemData*>(_data);
|
||||||
|
data->sceneItem = obs_scene_add(scene, data->source);
|
||||||
|
|
||||||
|
if (data->sceneItemTransform)
|
||||||
|
obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
|
||||||
|
|
||||||
|
if (data->sceneItemCrop)
|
||||||
|
obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
|
||||||
|
|
||||||
|
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_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
|
||||||
|
{
|
||||||
|
// Sanity check for valid scene
|
||||||
|
if (!(source && scene))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Create data struct and populate for scene item creation
|
||||||
|
CreateSceneItemData data;
|
||||||
|
data.source = source;
|
||||||
|
data.sceneItemEnabled = sceneItemEnabled;
|
||||||
|
data.sceneItemTransform = sceneItemTransform;
|
||||||
|
data.sceneItemCrop = sceneItemCrop;
|
||||||
|
|
||||||
|
// Enter graphics context and create the scene item
|
||||||
|
obs_enter_graphics();
|
||||||
|
obs_scene_atomic_update(scene, CreateSceneItemHelper, &data);
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
obs_sceneitem_addref(data.sceneItem);
|
||||||
|
|
||||||
|
return data.sceneItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
|
||||||
|
{
|
||||||
|
// Create the input
|
||||||
|
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
||||||
|
|
||||||
|
// Check that everything was created properly
|
||||||
|
if (!input)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Apparently not all default input properties actually get applied on creation (smh)
|
||||||
|
uint32_t flags = obs_source_get_output_flags(input);
|
||||||
|
if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0)
|
||||||
|
obs_source_set_monitoring_type(input, OBS_MONITORING_TYPE_MONITOR_ONLY);
|
||||||
|
|
||||||
|
// Create a scene item for the input
|
||||||
|
obs_sceneitem_t *ret = CreateSceneItem(input, scene, sceneItemEnabled);
|
||||||
|
|
||||||
|
// If creation failed, remove the input
|
||||||
|
if (!ret)
|
||||||
|
obs_source_remove(input);
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
}
|
313
src/utils/Obs_ArrayHelper.cpp
Normal file
313
src/utils/Obs_ArrayHelper.cpp
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
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 <algorithm>
|
||||||
|
|
||||||
|
#include "Obs.h"
|
||||||
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
|
static std::vector<std::string> ConvertStringArray(char **array)
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
if (!array)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
char* value = nullptr;
|
||||||
|
do {
|
||||||
|
value = array[index];
|
||||||
|
if (value)
|
||||||
|
ret.push_back(value);
|
||||||
|
index++;
|
||||||
|
} while (value);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetSceneCollectionList()
|
||||||
|
{
|
||||||
|
char** sceneCollections = obs_frontend_get_scene_collections();
|
||||||
|
auto ret = ConvertStringArray(sceneCollections);
|
||||||
|
bfree(sceneCollections);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetProfileList()
|
||||||
|
{
|
||||||
|
char** profiles = obs_frontend_get_profiles();
|
||||||
|
auto ret = ConvertStringArray(profiles);
|
||||||
|
bfree(profiles);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
|
||||||
|
{
|
||||||
|
std::vector<obs_hotkey_t *> ret;
|
||||||
|
|
||||||
|
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
|
||||||
|
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
||||||
|
|
||||||
|
ret->push_back(hotkey);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, &ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetHotkeyNameList()
|
||||||
|
{
|
||||||
|
auto hotkeys = GetHotkeyList();
|
||||||
|
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
for (auto hotkey : hotkeys)
|
||||||
|
ret.emplace_back(obs_hotkey_get_name(hotkey));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
|
||||||
|
{
|
||||||
|
obs_frontend_source_list sceneList = {};
|
||||||
|
obs_frontend_get_scenes(&sceneList);
|
||||||
|
|
||||||
|
std::vector<json> ret;
|
||||||
|
for (size_t i = 0; i < sceneList.sources.num; i++) {
|
||||||
|
obs_source_t *scene = sceneList.sources.array[i];
|
||||||
|
|
||||||
|
json sceneJson;
|
||||||
|
sceneJson["sceneName"] = obs_source_get_name(scene);
|
||||||
|
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
|
||||||
|
|
||||||
|
ret.push_back(sceneJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_source_list_free(&sceneList);
|
||||||
|
|
||||||
|
// Reverse the vector order to match other array returns
|
||||||
|
std::reverse(ret.begin(), ret.end());
|
||||||
|
|
||||||
|
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::pair<std::vector<json>, bool> enumData;
|
||||||
|
enumData.second = basic;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
json item;
|
||||||
|
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
|
// Should be slightly faster than calling obs_sceneitem_get_order_position()
|
||||||
|
item["sceneItemIndex"] = enumData->first.size();
|
||||||
|
if (!enumData->second) {
|
||||||
|
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||||
|
item["sourceName"] = obs_source_get_name(itemSource);
|
||||||
|
item["sourceType"] = StringHelper::GetSourceType(itemSource);
|
||||||
|
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
||||||
|
item["inputKind"] = obs_source_get_id(itemSource);
|
||||||
|
else
|
||||||
|
item["inputKind"] = nullptr;
|
||||||
|
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_SCENE)
|
||||||
|
item["isGroup"] = obs_source_is_group(itemSource);
|
||||||
|
else
|
||||||
|
item["isGroup"] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
enumData->first.push_back(item);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, &enumData);
|
||||||
|
|
||||||
|
return enumData.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EnumInputInfo {
|
||||||
|
std::string inputKind; // For searching by input kind
|
||||||
|
std::vector<json> inputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
||||||
|
{
|
||||||
|
EnumInputInfo inputInfo;
|
||||||
|
inputInfo.inputKind = inputKind;
|
||||||
|
|
||||||
|
auto inputEnumProc = [](void *param, obs_source_t *input) {
|
||||||
|
// Sanity check in case the API changes
|
||||||
|
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto inputInfo = static_cast<EnumInputInfo*>(param);
|
||||||
|
|
||||||
|
std::string inputKind = obs_source_get_id(input);
|
||||||
|
|
||||||
|
if (!inputInfo->inputKind.empty() && inputInfo->inputKind != inputKind)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
json inputJson;
|
||||||
|
inputJson["inputName"] = obs_source_get_name(input);
|
||||||
|
inputJson["inputKind"] = inputKind;
|
||||||
|
inputJson["unversionedInputKind"] = obs_source_get_unversioned_id(input);
|
||||||
|
|
||||||
|
inputInfo->inputs.push_back(inputJson);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
// Actually enumerates only public inputs, despite the name
|
||||||
|
obs_enum_sources(inputEnumProc, &inputInfo);
|
||||||
|
|
||||||
|
return inputInfo.inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetInputKindList(bool unversioned, bool includeDisabled)
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
const char *kind;
|
||||||
|
const char *unversioned_kind;
|
||||||
|
while (obs_enum_input_types2(idx++, &kind, &unversioned_kind)) {
|
||||||
|
uint32_t caps = obs_get_source_output_flags(kind);
|
||||||
|
|
||||||
|
if (!includeDisabled && (caps & OBS_SOURCE_CAP_DISABLED) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (unversioned)
|
||||||
|
ret.push_back(unversioned_kind);
|
||||||
|
else
|
||||||
|
ret.push_back(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *property)
|
||||||
|
{
|
||||||
|
std::vector<json> ret;
|
||||||
|
|
||||||
|
enum obs_combo_format itemFormat = obs_property_list_format(property);
|
||||||
|
size_t itemCount = obs_property_list_item_count(property);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < itemCount; i++) {
|
||||||
|
json itemData;
|
||||||
|
itemData["itemName"] = obs_property_list_item_name(property, i);
|
||||||
|
itemData["itemEnabled"] = !obs_property_list_item_disabled(property, i);
|
||||||
|
if (itemFormat == OBS_COMBO_FORMAT_INT) {
|
||||||
|
itemData["itemValue"] = obs_property_list_item_int(property, i);
|
||||||
|
} else if (itemFormat == OBS_COMBO_FORMAT_FLOAT) {
|
||||||
|
itemData["itemValue"] = obs_property_list_item_float(property, i);
|
||||||
|
} else if (itemFormat == OBS_COMBO_FORMAT_STRING) {
|
||||||
|
itemData["itemValue"] = obs_property_list_item_string(property, i);
|
||||||
|
} else {
|
||||||
|
itemData["itemValue"] = nullptr;
|
||||||
|
}
|
||||||
|
ret.push_back(itemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Utils::Obs::ArrayHelper::GetTransitionKindList()
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
const char *kind;
|
||||||
|
while (obs_enum_transition_types(idx++, &kind))
|
||||||
|
ret.emplace_back(kind);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
|
||||||
|
{
|
||||||
|
obs_frontend_source_list transitionList = {};
|
||||||
|
obs_frontend_get_transitions(&transitionList);
|
||||||
|
|
||||||
|
std::vector<json> ret;
|
||||||
|
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
||||||
|
obs_source_t *transition = transitionList.sources.array[i];
|
||||||
|
json transitionJson;
|
||||||
|
transitionJson["transitionName"] = obs_source_get_name(transition);
|
||||||
|
transitionJson["transitionKind"] = obs_source_get_id(transition);
|
||||||
|
transitionJson["transitionFixed"] = obs_transition_fixed(transition);
|
||||||
|
transitionJson["transitionConfigurable"] = obs_source_configurable(transition);
|
||||||
|
ret.push_back(transitionJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_source_list_free(&transitionList);
|
||||||
|
|
||||||
|
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 enumFilters = [](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, enumFilters, &filters);
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
60
src/utils/Obs_EnumHelper.cpp
Normal file
60
src/utils/Obs_EnumHelper.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum obs_blending_type Utils::Obs::EnumHelper::GetSceneItemBlendMode(std::string mode)
|
||||||
|
{
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_NORMAL)
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_ADDITIVE)
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_SUBTRACT)
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_SCREEN)
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_MULTIPLY)
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_LIGHTEN)
|
||||||
|
RET_COMPARE(mode, OBS_BLEND_DARKEN)
|
||||||
|
|
||||||
|
return OBS_BLEND_NORMAL;
|
||||||
|
}
|
79
src/utils/Obs_NumberHelper.cpp
Normal file
79
src/utils/Obs_NumberHelper.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
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 <stdint.h>
|
||||||
|
#include <util/util_uint64.h>
|
||||||
|
|
||||||
|
#include "Obs.h"
|
||||||
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
|
uint64_t Utils::Obs::NumberHelper::GetOutputDuration(obs_output_t *output)
|
||||||
|
{
|
||||||
|
if (!output || !obs_output_active(output))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
video_t* video = obs_output_video(output);
|
||||||
|
uint64_t frameTimeNs = video_output_get_frame_time(video);
|
||||||
|
int totalFrames = obs_output_get_total_frames(output);
|
||||||
|
|
||||||
|
return util_mul_div64(totalFrames, frameTimeNs, 1000000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Utils::Obs::NumberHelper::GetSceneCount()
|
||||||
|
{
|
||||||
|
size_t ret;
|
||||||
|
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
||||||
|
auto ret = static_cast<size_t*>(param);
|
||||||
|
|
||||||
|
if (obs_source_is_group(scene))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
(*ret)++;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
obs_enum_scenes(sceneEnumProc, &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;
|
||||||
|
}
|
87
src/utils/Obs_ObjectHelper.cpp
Normal file
87
src/utils/Obs_ObjectHelper.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
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 <util/config-file.h>
|
||||||
|
|
||||||
|
#include "Obs.h"
|
||||||
|
#include "../obs-websocket.h"
|
||||||
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
|
json Utils::Obs::ObjectHelper::GetStats()
|
||||||
|
{
|
||||||
|
json ret;
|
||||||
|
|
||||||
|
std::string outputPath = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
|
||||||
|
|
||||||
|
video_t* video = obs_get_video();
|
||||||
|
|
||||||
|
ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
|
||||||
|
ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
|
||||||
|
ret["availableDiskSpace"] = (double)os_get_free_disk_space(outputPath.c_str()) / (1024.0 * 1024.0);
|
||||||
|
ret["activeFps"] = obs_get_active_fps();
|
||||||
|
ret["averageFrameRenderTime"] = (double)obs_get_average_frame_time_ns() / 1000000.0;
|
||||||
|
ret["renderSkippedFrames"] = obs_get_lagged_frames();
|
||||||
|
ret["renderTotalFrames"] = obs_get_total_frames();
|
||||||
|
ret["outputSkippedFrames"] = video_output_get_skipped_frames(video);
|
||||||
|
ret["outputTotalFrames"] = video_output_get_total_frames(video);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
||||||
|
{
|
||||||
|
json ret;
|
||||||
|
|
||||||
|
obs_transform_info osi;
|
||||||
|
obs_sceneitem_crop crop;
|
||||||
|
obs_sceneitem_get_info(item, &osi);
|
||||||
|
obs_sceneitem_get_crop(item, &crop);
|
||||||
|
|
||||||
|
OBSSource source = obs_sceneitem_get_source(item);
|
||||||
|
float sourceWidth = float(obs_source_get_width(source));
|
||||||
|
float sourceHeight = float(obs_source_get_height(source));
|
||||||
|
|
||||||
|
ret["sourceWidth"] = sourceWidth;
|
||||||
|
ret["sourceHeight"] = sourceHeight;
|
||||||
|
|
||||||
|
ret["positionX"] = osi.pos.x;
|
||||||
|
ret["positionY"] = osi.pos.y;
|
||||||
|
|
||||||
|
ret["rotation"] = osi.rot;
|
||||||
|
|
||||||
|
ret["scaleX"] = osi.scale.x;
|
||||||
|
ret["scaleY"] = osi.scale.y;
|
||||||
|
|
||||||
|
ret["width"] = osi.scale.x * sourceWidth;
|
||||||
|
ret["height"] = osi.scale.y * sourceHeight;
|
||||||
|
|
||||||
|
ret["alignment"] = osi.alignment;
|
||||||
|
|
||||||
|
ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
|
||||||
|
ret["boundsAlignment"] = osi.bounds_alignment;
|
||||||
|
ret["boundsWidth"] = osi.bounds.x;
|
||||||
|
ret["boundsHeight"] = osi.bounds.y;
|
||||||
|
|
||||||
|
ret["cropLeft"] = int(crop.left);
|
||||||
|
ret["cropRight"] = int(crop.right);
|
||||||
|
ret["cropTop"] = int(crop.top);
|
||||||
|
ret["cropBottom"] = int(crop.bottom);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
68
src/utils/Obs_SearchHelper.cpp
Normal file
68
src/utils/Obs_SearchHelper.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto hotkeys = ArrayHelper::GetHotkeyList();
|
||||||
|
|
||||||
|
for (auto hotkey : hotkeys) {
|
||||||
|
if (obs_hotkey_get_name(hotkey) == name)
|
||||||
|
return hotkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increments source ref. Use OBSSourceAutoRelease
|
||||||
|
obs_source_t *Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string name)
|
||||||
|
{
|
||||||
|
obs_frontend_source_list transitionList = {};
|
||||||
|
obs_frontend_get_transitions(&transitionList);
|
||||||
|
|
||||||
|
obs_source_t *ret = nullptr;
|
||||||
|
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
||||||
|
obs_source_t *transition = transitionList.sources.array[i];
|
||||||
|
if (obs_source_get_name(transition) == name) {
|
||||||
|
ret = obs_source_get_ref(transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_source_list_free(&transitionList);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increments item ref. Use OBSSceneItemAutoRelease
|
||||||
|
obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name)
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Finds first matching scene item in scene, search starts at index 0
|
||||||
|
OBSSceneItem ret = obs_scene_find_source(scene, name.c_str());
|
||||||
|
obs_sceneitem_addref(ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
192
src/utils/Obs_StringHelper.cpp
Normal file
192
src/utils/Obs_StringHelper.cpp
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
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 <inttypes.h>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "Obs.h"
|
||||||
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
|
#define CASE(x) case x: return #x;
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetObsVersion()
|
||||||
|
{
|
||||||
|
uint32_t version = obs_get_version();
|
||||||
|
|
||||||
|
uint8_t major, minor, patch;
|
||||||
|
major = (version >> 24) & 0xFF;
|
||||||
|
minor = (version >> 16) & 0xFF;
|
||||||
|
patch = version & 0xFF;
|
||||||
|
|
||||||
|
QString combined = QString("%1.%2.%3").arg(major).arg(minor).arg(patch);
|
||||||
|
return combined.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetCurrentSceneCollection()
|
||||||
|
{
|
||||||
|
char *sceneCollectionName = obs_frontend_get_current_scene_collection();
|
||||||
|
std::string ret = sceneCollectionName;
|
||||||
|
bfree(sceneCollectionName);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetCurrentProfile()
|
||||||
|
{
|
||||||
|
char *profileName = obs_frontend_get_current_profile();
|
||||||
|
std::string ret = profileName;
|
||||||
|
bfree(profileName);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
|
||||||
|
{
|
||||||
|
char *profilePath = obs_frontend_get_current_profile_path();
|
||||||
|
std::string ret = profilePath;
|
||||||
|
bfree(profilePath);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
|
||||||
|
{
|
||||||
|
char *recordOutputPath = obs_frontend_get_current_record_output_path();
|
||||||
|
std::string ret = recordOutputPath;
|
||||||
|
bfree(recordOutputPath);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
|
||||||
|
{
|
||||||
|
obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
|
||||||
|
switch (sourceType) {
|
||||||
|
default:
|
||||||
|
CASE(OBS_SOURCE_TYPE_INPUT)
|
||||||
|
CASE(OBS_SOURCE_TYPE_FILTER)
|
||||||
|
CASE(OBS_SOURCE_TYPE_TRANSITION)
|
||||||
|
CASE(OBS_SOURCE_TYPE_SCENE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetInputMonitorType(enum obs_monitoring_type monitorType)
|
||||||
|
{
|
||||||
|
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::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();
|
||||||
|
if (!output)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
calldata_t cd = {0};
|
||||||
|
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
||||||
|
proc_handler_call(ph, "get_last_replay", &cd);
|
||||||
|
const char *savedReplayPath = calldata_string(&cd, "path");
|
||||||
|
calldata_free(&cd);
|
||||||
|
|
||||||
|
if (!savedReplayPath)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return savedReplayPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
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::GetSceneItemBlendMode(enum obs_blending_type mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
default:
|
||||||
|
CASE(OBS_BLEND_NORMAL)
|
||||||
|
CASE(OBS_BLEND_ADDITIVE)
|
||||||
|
CASE(OBS_BLEND_SUBTRACT)
|
||||||
|
CASE(OBS_BLEND_SCREEN)
|
||||||
|
CASE(OBS_BLEND_MULTIPLY)
|
||||||
|
CASE(OBS_BLEND_LIGHTEN)
|
||||||
|
CASE(OBS_BLEND_DARKEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
||||||
|
{
|
||||||
|
uint64_t secs = ms / 1000ULL;
|
||||||
|
uint64_t minutes = secs / 60ULL;
|
||||||
|
|
||||||
|
uint64_t hoursPart = minutes / 60ULL;
|
||||||
|
uint64_t minutesPart = minutes % 60ULL;
|
||||||
|
uint64_t secsPart = secs % 60ULL;
|
||||||
|
uint64_t msPart = ms % 1000ULL;
|
||||||
|
|
||||||
|
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,355 +1,353 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||||
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
|
||||||
Copyright (C) 2020-2021 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
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
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 <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "ObsVolumeMeter.h"
|
#include "Obs_VolumeMeter.h"
|
||||||
#include "ObsVolumeMeter_Helpers.h"
|
#include "Obs_VolumeMeter_Helpers.h"
|
||||||
|
#include "../obs-websocket.h"
|
||||||
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
|
||||||
PeakMeterType(SAMPLE_PEAK_METER),
|
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
||||||
_input(obs_source_get_weak_source(input)),
|
PeakMeterType(SAMPLE_PEAK_METER),
|
||||||
_channels(0),
|
_input(obs_source_get_weak_source(input)),
|
||||||
_lastUpdate(0),
|
_channels(0),
|
||||||
_volume(obs_source_get_volume(input))
|
_lastUpdate(0),
|
||||||
{
|
_volume(obs_source_get_volume(input))
|
||||||
signal_handler_t *sh = obs_source_get_signal_handler(input);
|
{
|
||||||
signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this);
|
signal_handler_t *sh = obs_source_get_signal_handler(input);
|
||||||
|
signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this);
|
||||||
obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
|
|
||||||
|
obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input));
|
|
||||||
}
|
blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input));
|
||||||
|
}
|
||||||
Utils::Obs::VolumeMeter::Meter::~Meter()
|
|
||||||
{
|
Utils::Obs::VolumeMeter::Meter::~Meter()
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
{
|
||||||
if (!input) {
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
if (!input) {
|
||||||
return;
|
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
signal_handler_t *sh = obs_source_get_signal_handler(input);
|
|
||||||
signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this);
|
signal_handler_t *sh = obs_source_get_signal_handler(input);
|
||||||
|
signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this);
|
||||||
obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
|
|
||||||
|
obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input));
|
|
||||||
}
|
blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input));
|
||||||
|
}
|
||||||
bool Utils::Obs::VolumeMeter::Meter::InputValid()
|
|
||||||
{
|
bool Utils::Obs::VolumeMeter::Meter::InputValid()
|
||||||
// return !obs_weak_source_expired(_input);
|
{
|
||||||
return true;
|
return !obs_weak_source_expired(_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
||||||
{
|
{
|
||||||
json ret;
|
json ret;
|
||||||
|
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<float>> levels;
|
std::vector<std::vector<float>> levels;
|
||||||
const float volume = _muted ? 0.0f : _volume.load();
|
const float volume = _muted ? 0.0f : _volume.load();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> l(_mutex);
|
std::unique_lock<std::mutex> l(_mutex);
|
||||||
|
|
||||||
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
|
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
|
||||||
ResetAudioLevels();
|
ResetAudioLevels();
|
||||||
|
|
||||||
for (int channel = 0; channel < _channels; channel++) {
|
for (int channel = 0; channel < _channels; channel++) {
|
||||||
std::vector<float> level;
|
std::vector<float> level;
|
||||||
level.push_back(_magnitude[channel] * volume);
|
level.push_back(_magnitude[channel] * volume);
|
||||||
level.push_back(_peak[channel] * volume);
|
level.push_back(_peak[channel] * volume);
|
||||||
level.push_back(_peak[channel]);
|
level.push_back(_peak[channel]);
|
||||||
|
|
||||||
levels.push_back(level);
|
levels.push_back(level);
|
||||||
}
|
}
|
||||||
l.unlock();
|
l.unlock();
|
||||||
|
|
||||||
ret["inputName"] = obs_source_get_name(input);
|
ret["inputName"] = obs_source_get_name(input);
|
||||||
ret["inputLevelsMul"] = levels;
|
ret["inputLevelsMul"] = levels;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MUST HOLD LOCK
|
// MUST HOLD LOCK
|
||||||
void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels()
|
void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels()
|
||||||
{
|
{
|
||||||
_lastUpdate = 0;
|
_lastUpdate = 0;
|
||||||
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) {
|
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) {
|
||||||
_magnitude[channelNumber] = 0;
|
_magnitude[channelNumber] = 0;
|
||||||
_peak[channelNumber] = 0;
|
_peak[channelNumber] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MUST HOLD LOCK
|
// MUST HOLD LOCK
|
||||||
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data)
|
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data)
|
||||||
{
|
{
|
||||||
int channels = 0;
|
int channels = 0;
|
||||||
for (int i = 0; i < MAX_AV_PLANES; i++) {
|
for (int i = 0; i < MAX_AV_PLANES; i++) {
|
||||||
if (data->data[i])
|
if (data->data[i])
|
||||||
channels++;
|
channels++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool channelsChanged = _channels != channels;
|
bool channelsChanged = _channels != channels;
|
||||||
_channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS);
|
_channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS);
|
||||||
|
|
||||||
if (channelsChanged)
|
if (channelsChanged)
|
||||||
ResetAudioLevels();
|
ResetAudioLevels();
|
||||||
}
|
}
|
||||||
|
|
||||||
// MUST HOLD LOCK
|
// MUST HOLD LOCK
|
||||||
void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
||||||
{
|
{
|
||||||
size_t sampleCount = data->frames;
|
size_t sampleCount = data->frames;
|
||||||
int channelNumber = 0;
|
int channelNumber = 0;
|
||||||
|
|
||||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||||
float *samples = (float*)data->data[planeNumber];
|
float *samples = (float*)data->data[planeNumber];
|
||||||
if (!samples)
|
if (!samples)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (((uintptr_t)samples & 0xf) > 0) {
|
if (((uintptr_t)samples & 0xf) > 0) {
|
||||||
_peak[channelNumber] = 1.0f;
|
_peak[channelNumber] = 1.0f;
|
||||||
channelNumber++;
|
channelNumber++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
|
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
|
||||||
|
|
||||||
float peak;
|
float peak;
|
||||||
switch (PeakMeterType) {
|
switch (PeakMeterType) {
|
||||||
default:
|
default:
|
||||||
case SAMPLE_PEAK_METER:
|
case SAMPLE_PEAK_METER:
|
||||||
peak = GetSamplePeak(previousSamples, samples, sampleCount);
|
peak = GetSamplePeak(previousSamples, samples, sampleCount);
|
||||||
break;
|
break;
|
||||||
case TRUE_PEAK_METER:
|
case TRUE_PEAK_METER:
|
||||||
peak = GetTruePeak(previousSamples, samples, sampleCount);
|
peak = GetTruePeak(previousSamples, samples, sampleCount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (sampleCount) {
|
switch (sampleCount) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
|
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
|
||||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
|
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
|
||||||
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
|
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
|
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
|
||||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
|
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
|
||||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
|
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
|
||||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
||||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_previousSamples[channelNumber][0] = samples[sampleCount - 4];
|
_previousSamples[channelNumber][0] = samples[sampleCount - 4];
|
||||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
||||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
_peak[channelNumber] = peak;
|
_peak[channelNumber] = peak;
|
||||||
|
|
||||||
channelNumber++;
|
channelNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++)
|
for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++)
|
||||||
_peak[channelNumber] = 0.0;
|
_peak[channelNumber] = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MUST HOLD LOCK
|
// MUST HOLD LOCK
|
||||||
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
|
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
|
||||||
{
|
{
|
||||||
size_t sampleCount = data->frames;
|
size_t sampleCount = data->frames;
|
||||||
|
|
||||||
int channelNumber = 0;
|
int channelNumber = 0;
|
||||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||||
float *samples = (float*)data->data[planeNumber];
|
float *samples = (float*)data->data[planeNumber];
|
||||||
if (!samples)
|
if (!samples)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float sum = 0.0;
|
float sum = 0.0;
|
||||||
for (size_t i = 0; i < sampleCount; i++) {
|
for (size_t i = 0; i < sampleCount; i++) {
|
||||||
float sample = samples[i];
|
float sample = samples[i];
|
||||||
sum += sample * sample;
|
sum += sample * sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
_magnitude[channelNumber] = std::sqrt(sum / sampleCount);
|
_magnitude[channelNumber] = std::sqrt(sum / sampleCount);
|
||||||
|
|
||||||
channelNumber++;
|
channelNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
|
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Meter*>(priv_data);
|
auto c = static_cast<Meter*>(priv_data);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> l(c->_mutex);
|
std::unique_lock<std::mutex> l(c->_mutex);
|
||||||
|
|
||||||
c->_muted = muted;
|
c->_muted = muted;
|
||||||
c->ProcessAudioChannels(data);
|
c->ProcessAudioChannels(data);
|
||||||
c->ProcessPeak(data);
|
c->ProcessPeak(data);
|
||||||
c->ProcessMagnitude(data);
|
c->ProcessMagnitude(data);
|
||||||
|
|
||||||
c->_lastUpdate = os_gettime_ns();
|
c->_lastUpdate = os_gettime_ns();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
|
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Meter*>(priv_data);
|
auto c = static_cast<Meter*>(priv_data);
|
||||||
|
|
||||||
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),
|
_updateCallback(cb),
|
||||||
_updatePeriod(updatePeriod),
|
_updatePeriod(updatePeriod),
|
||||||
_running(false)
|
_running(false)
|
||||||
{
|
{
|
||||||
signal_handler_t *sh = obs_get_signal_handler();
|
signal_handler_t *sh = obs_get_signal_handler();
|
||||||
if (!sh)
|
if (!sh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto enumProc = [](void *priv_data, obs_source_t *input) {
|
auto enumProc = [](void *priv_data, obs_source_t *input) {
|
||||||
auto c = static_cast<Handler*>(priv_data);
|
auto c = static_cast<Handler*>(priv_data);
|
||||||
|
|
||||||
if (!obs_source_active(input))
|
if (!obs_source_active(input))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
uint32_t flags = obs_source_get_output_flags(input);
|
uint32_t flags = obs_source_get_output_flags(input);
|
||||||
if ((flags & OBS_SOURCE_AUDIO) == 0)
|
if ((flags & OBS_SOURCE_AUDIO) == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
c->_meters.emplace_back(std::move(new Meter(input)));
|
c->_meters.emplace_back(std::move(new Meter(input)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
obs_enum_sources(enumProc, this);
|
obs_enum_sources(enumProc, this);
|
||||||
|
|
||||||
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
|
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
|
||||||
signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
||||||
|
|
||||||
_running = true;
|
_running = true;
|
||||||
_updateThread = std::thread(&Handler::UpdateThread, this);
|
_updateThread = std::thread(&Handler::UpdateThread, this);
|
||||||
|
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
|
blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Handler::~Handler()
|
Utils::Obs::VolumeMeter::Handler::~Handler()
|
||||||
{
|
{
|
||||||
signal_handler_t *sh = obs_get_signal_handler();
|
signal_handler_t *sh = obs_get_signal_handler();
|
||||||
if (!sh)
|
if (!sh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
|
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
|
||||||
signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
||||||
|
|
||||||
if (_running) {
|
if (_running) {
|
||||||
_mutex.lock();
|
_running = false;
|
||||||
_running = false;
|
_cond.notify_all();
|
||||||
_mutex.unlock();
|
}
|
||||||
_cond.notify_all();
|
|
||||||
}
|
if (_updateThread.joinable())
|
||||||
|
_updateThread.join();
|
||||||
if (_updateThread.joinable())
|
|
||||||
_updateThread.join();
|
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
|
||||||
|
}
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
|
|
||||||
}
|
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
||||||
|
{
|
||||||
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started.");
|
||||||
{
|
while (_running) {
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started.");
|
{
|
||||||
while (_running) {
|
std::unique_lock<std::mutex> l(_mutex);
|
||||||
{
|
if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; }))
|
||||||
std::unique_lock<std::mutex> l(_mutex);
|
break;
|
||||||
if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; }))
|
}
|
||||||
break;
|
|
||||||
}
|
std::vector<json> inputs;
|
||||||
|
std::unique_lock<std::mutex> l(_meterMutex);
|
||||||
std::vector<json> inputs;
|
for (auto &meter : _meters) {
|
||||||
std::unique_lock<std::mutex> l(_meterMutex);
|
if (meter->InputValid())
|
||||||
for (auto &meter : _meters) {
|
inputs.push_back(meter->GetMeterData());
|
||||||
if (meter->InputValid())
|
}
|
||||||
inputs.push_back(meter->GetMeterData());
|
l.unlock();
|
||||||
}
|
|
||||||
l.unlock();
|
if (_updateCallback)
|
||||||
|
_updateCallback(inputs);
|
||||||
if (_updateCallback)
|
}
|
||||||
_updateCallback(inputs);
|
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
|
||||||
}
|
}
|
||||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
|
|
||||||
}
|
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
|
||||||
|
{
|
||||||
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
|
auto c = static_cast<Handler*>(priv_data);
|
||||||
{
|
|
||||||
auto c = static_cast<Handler*>(priv_data);
|
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||||
|
if (!input)
|
||||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
return;
|
||||||
if (!input)
|
|
||||||
return;
|
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||||
|
return;
|
||||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
|
||||||
return;
|
uint32_t flags = obs_source_get_output_flags(input);
|
||||||
|
if ((flags & OBS_SOURCE_AUDIO) == 0)
|
||||||
uint32_t flags = obs_source_get_output_flags(input);
|
return;
|
||||||
if ((flags & OBS_SOURCE_AUDIO) == 0)
|
|
||||||
return;
|
std::unique_lock<std::mutex> l(c->_meterMutex);
|
||||||
|
c->_meters.emplace_back(std::move(new Meter(input)));
|
||||||
std::unique_lock<std::mutex> l(c->_meterMutex);
|
}
|
||||||
c->_meters.emplace_back(std::move(new Meter(input)));
|
|
||||||
}
|
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd)
|
||||||
|
{
|
||||||
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd)
|
auto c = static_cast<Handler*>(priv_data);
|
||||||
{
|
|
||||||
auto c = static_cast<Handler*>(priv_data);
|
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||||
|
if (!input)
|
||||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
return;
|
||||||
if (!input)
|
|
||||||
return;
|
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||||
|
return;
|
||||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
|
||||||
return;
|
// Don't ask me why, but using std::remove_if segfaults trying this.
|
||||||
|
std::unique_lock<std::mutex> l(c->_meterMutex);
|
||||||
// Don't ask me why, but using std::remove_if segfaults trying this.
|
std::vector<MeterPtr>::iterator iter;
|
||||||
std::unique_lock<std::mutex> l(c->_meterMutex);
|
for (iter = c->_meters.begin(); iter != c->_meters.end();) {
|
||||||
std::vector<MeterPtr>::iterator iter;
|
if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input))
|
||||||
for (iter = c->_meters.begin(); iter != c->_meters.end();) {
|
iter = c->_meters.erase(iter);
|
||||||
if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input))
|
else
|
||||||
iter = c->_meters.erase(iter);
|
++iter;
|
||||||
else
|
}
|
||||||
++iter;
|
}
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +1,99 @@
|
|||||||
/*
|
/*
|
||||||
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-2021 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
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
|
|
||||||
#include "../obs-websocket.h"
|
#include "Obs.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Obs {
|
namespace Obs {
|
||||||
namespace VolumeMeter {
|
namespace VolumeMeter {
|
||||||
// Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c
|
// Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c
|
||||||
// Keeps a running tally of the current audio levels, for a specific input
|
// Keeps a running tally of the current audio levels, for a specific input
|
||||||
class Meter {
|
class Meter {
|
||||||
public:
|
public:
|
||||||
Meter(obs_source_t *input);
|
Meter(obs_source_t *input);
|
||||||
~Meter();
|
~Meter();
|
||||||
|
|
||||||
bool InputValid();
|
bool InputValid();
|
||||||
obs_weak_source_t *GetWeakInput() { return _input; }
|
obs_weak_source_t *GetWeakInput() { return _input; }
|
||||||
json GetMeterData();
|
json GetMeterData();
|
||||||
|
|
||||||
std::atomic<enum obs_peak_meter_type> PeakMeterType;
|
std::atomic<enum obs_peak_meter_type> PeakMeterType;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OBSWeakSourceAutoRelease _input;
|
OBSWeakSourceAutoRelease _input;
|
||||||
|
|
||||||
// All values in mul
|
// All values in mul
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
bool _muted;
|
bool _muted;
|
||||||
int _channels;
|
int _channels;
|
||||||
float _magnitude[MAX_AUDIO_CHANNELS];
|
float _magnitude[MAX_AUDIO_CHANNELS];
|
||||||
float _peak[MAX_AUDIO_CHANNELS];
|
float _peak[MAX_AUDIO_CHANNELS];
|
||||||
float _previousSamples[MAX_AUDIO_CHANNELS][4];
|
float _previousSamples[MAX_AUDIO_CHANNELS][4];
|
||||||
|
|
||||||
std::atomic<uint64_t> _lastUpdate;
|
std::atomic<uint64_t> _lastUpdate;
|
||||||
std::atomic<float> _volume;
|
std::atomic<float> _volume;
|
||||||
|
|
||||||
void ResetAudioLevels();
|
void ResetAudioLevels();
|
||||||
void ProcessAudioChannels(const struct audio_data *data);
|
void ProcessAudioChannels(const struct audio_data *data);
|
||||||
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, const struct audio_data *data, bool muted);
|
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, 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);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maintains an array of active inputs
|
// Maintains an array of active inputs
|
||||||
class Handler {
|
class Handler {
|
||||||
typedef std::function<void(std::vector<json>)> UpdateCallback;
|
typedef std::function<void(std::vector<json>)> UpdateCallback;
|
||||||
typedef std::unique_ptr<Meter> MeterPtr;
|
typedef std::unique_ptr<Meter> MeterPtr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
|
Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
|
||||||
~Handler();
|
~Handler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UpdateCallback _updateCallback;
|
UpdateCallback _updateCallback;
|
||||||
|
|
||||||
std::mutex _meterMutex;
|
std::mutex _meterMutex;
|
||||||
std::vector<MeterPtr> _meters;
|
std::vector<MeterPtr> _meters;
|
||||||
uint64_t _updatePeriod;
|
uint64_t _updatePeriod;
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
std::condition_variable _cond;
|
std::condition_variable _cond;
|
||||||
bool _running;
|
std::atomic<bool> _running;
|
||||||
std::thread _updateThread;
|
std::thread _updateThread;
|
||||||
|
|
||||||
void UpdateThread();
|
void UpdateThread();
|
||||||
static void InputActivateCallback(void *priv_data, calldata_t *cd);
|
static void InputActivateCallback(void *priv_data, calldata_t *cd);
|
||||||
static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
|
static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -56,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)
|
||||||
|
|
||||||
float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
static 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) {
|
||||||
@ -69,7 +69,7 @@ float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleC
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
|
static 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);
|
@ -111,9 +111,9 @@ void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QS
|
|||||||
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
void *systemTrayPtr = obs_frontend_get_system_tray();
|
void *systemTrayPtr = obs_frontend_get_system_tray();
|
||||||
auto systemTray = reinterpret_cast<QSystemTrayIcon*>(systemTrayPtr);
|
auto systemTray = static_cast<QSystemTrayIcon*>(systemTrayPtr);
|
||||||
|
|
||||||
auto notification = reinterpret_cast<SystemTrayNotification*>(param);
|
auto notification = static_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);
|
||||||
|
@ -22,6 +22,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "ObsVolumeMeter.h"
|
#include "Obs_VolumeMeter.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
@ -23,6 +23,7 @@ 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>
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t requestedExecutionType = payloadData["executionType"];
|
int8_t requestedExecutionType = payloadData["executionType"];
|
||||||
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) {
|
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || 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.";
|
||||||
@ -296,7 +296,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
} return;
|
} return;
|
||||||
default:
|
default:
|
||||||
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
||||||
ret.closeReason = std::string("Unknown OpCode: %s") + std::to_string(opCode);
|
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user