mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
50 Commits
5.0.0-alph
...
5.0.0-nota
Author | SHA1 | Date | |
---|---|---|---|
3362d3f998 | |||
7ca8140a34 | |||
eeb7bac4b7 | |||
7113055218 | |||
1844f85e1f | |||
bc0b499944 | |||
ae906bb283 | |||
63dfed1cf9 | |||
873eadec05 | |||
dea0fcd561 | |||
9f7beb1c0d | |||
db9f4b24df | |||
6035294339 | |||
6a2d5968ad | |||
8f2d266dec | |||
fe64620731 | |||
24e43d0276 | |||
c0308d6ce1 | |||
506a9167c3 | |||
043444cad5 | |||
35c8a87def | |||
e451a8d6b0 | |||
02bcc0ac1b | |||
702f88cea8 | |||
6d216e0412 | |||
e6761cf286 | |||
00dd8d7821 | |||
e43ebde794 | |||
4a2654d095 | |||
6291cb1532 | |||
a90dafb971 | |||
3a96b585ce | |||
12c6527442 | |||
31997db509 | |||
85f65952bd | |||
9113ff9021 | |||
1ed095de48 | |||
a94ac24027 | |||
903b7d4171 | |||
a59ce69ba1 | |||
195c4a3ca9 | |||
3b2369ae97 | |||
af634b63fd | |||
444685c89d | |||
05aba45809 | |||
1f1a8926b1 | |||
0c5d4ba3fb | |||
947450ce4e | |||
63cc1f316d | |||
38157579a6 |
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
|
||||
description: What version of obs-websocket are you using?
|
||||
options:
|
||||
- 5.0.0-alpha3
|
||||
- 5.0.0-alpha2
|
||||
- 5.0.0-alpha1
|
||||
- 4.9.1
|
||||
- 4.9.0
|
||||
- Git
|
||||
|
261
.github/workflows/main.yml
vendored
261
.github/workflows/main.yml
vendored
@ -1,11 +1,11 @@
|
||||
name: "CI Multiplatform Build"
|
||||
name: 'CI Multiplatform Build'
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
branches:
|
||||
- master
|
||||
- '*'
|
||||
tags:
|
||||
- '[45].[0-9]+.[0-9]+*'
|
||||
pull_request:
|
||||
@ -25,8 +25,8 @@ jobs:
|
||||
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_VERSION: '2019'
|
||||
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||
CMAKE_SYSTEM_VERSION: "10.0"
|
||||
CMAKE_GENERATOR: 'Visual Studio 16 2019'
|
||||
CMAKE_SYSTEM_VERSION: '10.0'
|
||||
steps:
|
||||
- name: 'Add msbuild to PATH'
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
@ -35,20 +35,20 @@ jobs:
|
||||
with:
|
||||
path: ${{ github.workspace }}/obs-websocket
|
||||
submodules: 'recursive'
|
||||
- name: 'Checkout OBS-Studio'
|
||||
- name: 'Checkout OBS Studio'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
path: ${{ github.workspace }}/obs-studio
|
||||
submodules: 'recursive'
|
||||
- name: 'Get OBS-Studio Git Info'
|
||||
- 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 }})'
|
||||
- name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
run: |
|
||||
@ -78,8 +78,6 @@ jobs:
|
||||
with:
|
||||
path: Qt_${{ env.QT_VERSION }}.7z
|
||||
key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}'
|
||||
restore-keys: |
|
||||
qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}
|
||||
- name: 'Download Prerequisite: Qt'
|
||||
if: steps.qtcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
@ -87,20 +85,18 @@ jobs:
|
||||
- name: 'Extract Prerequisite: Qt'
|
||||
run: |
|
||||
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
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}\cmbuild\deps\**
|
||||
key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
|
||||
restore-keys: |
|
||||
obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}
|
||||
- name: 'Install Prerequisite: Pre-built OBS-Studio dependencies'
|
||||
- name: 'Install Prerequisite: Pre-built OBS Studio dependencies'
|
||||
if: steps.obscache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
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"
|
||||
- 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
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
@ -108,22 +104,20 @@ jobs:
|
||||
with:
|
||||
path: ${{ github.workspace }}/obs-studio/build32
|
||||
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||
- name: 'Configure OBS-Studio 32-bit'
|
||||
- name: 'Configure OBS Studio 32-bit'
|
||||
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
run: |
|
||||
if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\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 ..
|
||||
- name: 'Build OBS-Studio 32-bit'
|
||||
- name: 'Build OBS Studio 32-bit'
|
||||
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
run: |
|
||||
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.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
|
||||
uses: actions/cache@v1
|
||||
env:
|
||||
@ -131,16 +125,14 @@ jobs:
|
||||
with:
|
||||
path: ${{ github.workspace }}/obs-studio/build64
|
||||
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||
- name: 'Configure OBS-Studio 64-bit'
|
||||
- name: 'Configure OBS Studio 64-bit'
|
||||
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
run: |
|
||||
if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\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 ..
|
||||
- name: 'Build OBS-Studio 64-bit'
|
||||
- name: 'Build OBS Studio 64-bit'
|
||||
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
run: |
|
||||
@ -188,7 +180,7 @@ jobs:
|
||||
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer'
|
||||
path: ${{ github.workspace }}/obs-websocket/package/*.exe
|
||||
ubuntu64:
|
||||
name: "Linux/Ubuntu 64-bit"
|
||||
name: 'Linux/Ubuntu 64-bit'
|
||||
runs-on: [ubuntu-latest]
|
||||
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
||||
steps:
|
||||
@ -197,20 +189,20 @@ jobs:
|
||||
with:
|
||||
path: ${{ github.workspace }}/obs-websocket
|
||||
submodules: 'recursive'
|
||||
- name: 'Checkout OBS-Studio'
|
||||
- name: 'Checkout OBS Studio'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
path: ${{ github.workspace }}/obs-studio
|
||||
submodules: 'recursive'
|
||||
- name: 'Get OBS-Studio Git Info'
|
||||
- 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 }})'
|
||||
- name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
run: |
|
||||
@ -284,21 +276,21 @@ jobs:
|
||||
libx11-xcb-dev \
|
||||
libxcb1-dev \
|
||||
libxss-dev \
|
||||
- name: 'Configure OBS-Studio'
|
||||
- name: 'Configure OBS Studio'
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ./build
|
||||
cd ./build
|
||||
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
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
cd ./build
|
||||
make -j4 libobs obs-frontend-api
|
||||
- name: 'Install OBS-Studio'
|
||||
- name: 'Install OBS Studio'
|
||||
working-directory: ${{ github.workspace }}/obs-studio
|
||||
shell: bash
|
||||
run: |
|
||||
@ -316,7 +308,7 @@ jobs:
|
||||
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 .. ; \
|
||||
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
|
||||
- name: 'Build obs-websocket'
|
||||
working-directory: ${{ github.workspace }}/obs-websocket
|
||||
@ -355,3 +347,210 @@ jobs:
|
||||
with:
|
||||
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64'
|
||||
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'
|
||||
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'
|
||||
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'
|
||||
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,5 +0,0 @@
|
||||
brew "jack"
|
||||
brew "speexdsp"
|
||||
brew "cmake"
|
||||
brew "freetype"
|
||||
brew "fdk-aac"
|
@ -1,27 +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 .. \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
||||
-DQTDIR=/tmp/obsdeps \
|
||||
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
|
||||
-DLIBOBS_LIB=../../obs-studio/libobs \
|
||||
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
&& make -j4
|
@ -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>
|
||||
<integer>0</integer>
|
||||
<key>IDENTIFIER</key>
|
||||
<string>fr.palakis.obs-websocket</string>
|
||||
<string>com.obsproject.obs-websocket</string>
|
||||
<key>OVERWRITE_PERMISSIONS</key>
|
||||
<false/>
|
||||
<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
|
@ -114,6 +114,7 @@ set(obs-websocket_SOURCES
|
||||
src/requesthandler/RequestHandler_Transitions.cpp
|
||||
src/requesthandler/RequestHandler_Filters.cpp
|
||||
src/requesthandler/RequestHandler_SceneItems.cpp
|
||||
src/requesthandler/RequestHandler_Outputs.cpp
|
||||
src/requesthandler/RequestHandler_Stream.cpp
|
||||
src/requesthandler/RequestHandler_Record.cpp
|
||||
src/requesthandler/RequestHandler_MediaInputs.cpp
|
||||
|
2
deps/asio
vendored
2
deps/asio
vendored
Submodule deps/asio updated: 08a7029cb1...b73dc1d2c0
@ -1129,6 +1129,55 @@
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the info for a specific source filter.",
|
||||
"requestType": "GetSourceFilter",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "filters",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "sourceName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the source",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
},
|
||||
{
|
||||
"valueName": "filterName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the filter",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "filterEnabled",
|
||||
"valueType": "Boolean",
|
||||
"valueDescription": "Whether the filter is enabled"
|
||||
},
|
||||
{
|
||||
"valueName": "filterIndex",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "Index of the filter in the list, beginning at 0"
|
||||
},
|
||||
{
|
||||
"valueName": "filterKind",
|
||||
"valueType": "String",
|
||||
"valueDescription": "The kind of filter"
|
||||
},
|
||||
{
|
||||
"valueName": "filterSettings",
|
||||
"valueType": "Object",
|
||||
"valueDescription": "Settings object associated with the filter"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Gets data about the current plugin and RPC version.",
|
||||
"requestType": "GetVersion",
|
||||
@ -1472,6 +1521,48 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Gets the names of all special inputs.",
|
||||
"requestType": "GetSpecialInputs",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "inputs",
|
||||
"requestFields": [],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "desktop1",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the Desktop Audio input"
|
||||
},
|
||||
{
|
||||
"valueName": "desktop2",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the Desktop Audio 2 input"
|
||||
},
|
||||
{
|
||||
"valueName": "mic1",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the Mic/Auxiliary Audio input"
|
||||
},
|
||||
{
|
||||
"valueName": "mic2",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the Mic/Auxiliary Audio 2 input"
|
||||
},
|
||||
{
|
||||
"valueName": "mic3",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the Mic/Auxiliary Audio 3 input"
|
||||
},
|
||||
{
|
||||
"valueName": "mic4",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the Mic/Auxiliary Audio 4 input"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Creates a new input, adding it as a scene item to the specified scene.",
|
||||
"requestType": "CreateInput",
|
||||
@ -1811,13 +1902,67 @@
|
||||
"valueName": "inputVolumeDb",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "Volume setting in dB",
|
||||
"valueRestrictions": ">= -100, <= -26",
|
||||
"valueRestrictions": ">= -100, <= 26",
|
||||
"valueOptional": true,
|
||||
"valueOptionalBehavior": "`inputVolumeMul` should be specified"
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the audio balance of an input.",
|
||||
"requestType": "GetInputAudioBalance",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "inputs",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input to get the audio balance of",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "inputAudioBalance",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "Audio balance value from 0.0-1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Sets the audio balance of an input.",
|
||||
"requestType": "SetInputAudioBalance",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "inputs",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input to set the audio balance of",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
},
|
||||
{
|
||||
"valueName": "inputAudioBalance",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "New audio balance value",
|
||||
"valueRestrictions": ">= 0.0, <= 1.0",
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the audio sync offset of an input.\n\nNote: The audio sync offset can be negative too!",
|
||||
"requestType": "GetInputAudioSyncOffset",
|
||||
@ -1926,6 +2071,60 @@
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the enable state of all audio tracks of an input.",
|
||||
"requestType": "GetInputAudioTracks",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "inputs",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "inputAudioTracks",
|
||||
"valueType": "Object",
|
||||
"valueDescription": "Object of audio tracks and associated enable states"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Sets the enable state of audio tracks of an input.",
|
||||
"requestType": "SetInputAudioTracks",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "inputs",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
},
|
||||
{
|
||||
"valueName": "inputAudioTracks",
|
||||
"valueType": "Object",
|
||||
"valueDescription": "Track settings to apply",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the items of a list property from an input's properties.\n\nNote: Use this in cases where an input provides a dynamic, selectable list of items. For example, display capture, where it provides a list of available displays.",
|
||||
"requestType": "GetInputPropertiesListPropertyItems",
|
||||
@ -2108,6 +2307,62 @@
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the status of the virtualcam output.",
|
||||
"requestType": "GetVirtualCamStatus",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "outputs",
|
||||
"requestFields": [],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "outputActive",
|
||||
"valueType": "Boolean",
|
||||
"valueDescription": "Whether the output is active"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Toggles the state of the virtualcam output.",
|
||||
"requestType": "ToggleVirtualCam",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "outputs",
|
||||
"requestFields": [],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "outputActive",
|
||||
"valueType": "Boolean",
|
||||
"valueDescription": "Whether the output is active"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Starts the virtualcam output.",
|
||||
"requestType": "StartVirtualCam",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "outputs",
|
||||
"requestFields": [],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Stops the virtualcam output.",
|
||||
"requestType": "StopVirtualCam",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "outputs",
|
||||
"requestFields": [],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the status of the record output.",
|
||||
"requestType": "GetRecordStatus",
|
||||
@ -2706,6 +2961,76 @@
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets the blend mode of a scene item.\n\nBlend modes:\n\n- `OBS_BLEND_NORMAL`\n- `OBS_BLEND_ADDITIVE`\n- `OBS_BLEND_SUBTRACT`\n- `OBS_BLEND_SCREEN`\n- `OBS_BLEND_MULTIPLY`\n- `OBS_BLEND_LIGHTEN`\n- `OBS_BLEND_DARKEN`\n\nScenes and Groups",
|
||||
"requestType": "GetSceneItemBlendMode",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "scene items",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "sceneName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the scene the item is in",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
},
|
||||
{
|
||||
"valueName": "sceneItemId",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "Numeric ID of the scene item",
|
||||
"valueRestrictions": ">= 0",
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "sceneItemBlendMode",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Current blend mode"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Sets the blend mode of a scene item.\n\nScenes and Groups",
|
||||
"requestType": "SetSceneItemBlendMode",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "scene items",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "sceneName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the scene the item is in",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
},
|
||||
{
|
||||
"valueName": "sceneItemId",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "Numeric ID of the scene item",
|
||||
"valueRestrictions": ">= 0",
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
},
|
||||
{
|
||||
"valueName": "sceneItemBlendMode",
|
||||
"valueType": "String",
|
||||
"valueDescription": "New blend mode",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Gets an array of all scenes in OBS.",
|
||||
"requestType": "GetSceneList",
|
||||
@ -2729,7 +3054,24 @@
|
||||
{
|
||||
"valueName": "scenes",
|
||||
"valueType": "Array<Object>",
|
||||
"valueDescription": "Array of scenes in OBS"
|
||||
"valueDescription": "Array of scenes"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Gets an array of all groups in OBS.\n\nGroups in OBS are actually scenes, but renamed and modified. In obs-websocket, we treat them as scenes where we can.",
|
||||
"requestType": "GetGroupList",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "scenes",
|
||||
"requestFields": [],
|
||||
"responseFields": [
|
||||
{
|
||||
"valueName": "groups",
|
||||
"valueType": "Array<String>",
|
||||
"valueDescription": "Array of group names"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -3317,6 +3659,66 @@
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Opens the properties dialog of an input.",
|
||||
"requestType": "OpenInputPropertiesDialog",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "ui",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input to open the dialog of",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Opens the filters dialog of an input.",
|
||||
"requestType": "OpenInputFiltersDialog",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "ui",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input to open the dialog of",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
},
|
||||
{
|
||||
"description": "Opens the interact dialog of an input.",
|
||||
"requestType": "OpenInputInteractDialog",
|
||||
"complexity": 1,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "ui",
|
||||
"requestFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the input to open the dialog of",
|
||||
"valueRestrictions": null,
|
||||
"valueOptional": false,
|
||||
"valueOptionalBehavior": null
|
||||
}
|
||||
],
|
||||
"responseFields": []
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@ -3602,6 +4004,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "The audio balance value of an input has changed.",
|
||||
"eventType": "InputAudioBalanceChanged",
|
||||
"eventSubscription": "Inputs",
|
||||
"complexity": 2,
|
||||
"rpcVersion": "1",
|
||||
"deprecated": false,
|
||||
"initialVersion": "5.0.0",
|
||||
"category": "inputs",
|
||||
"dataFields": [
|
||||
{
|
||||
"valueName": "inputName",
|
||||
"valueType": "String",
|
||||
"valueDescription": "Name of the affected input"
|
||||
},
|
||||
{
|
||||
"valueName": "inputAudioBalance",
|
||||
"valueType": "Number",
|
||||
"valueDescription": "New audio balance value of the input"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "The sync offset of an input has changed.",
|
||||
"eventType": "InputAudioSyncOffsetChanged",
|
||||
@ -3641,8 +4065,8 @@
|
||||
},
|
||||
{
|
||||
"valueName": "inputAudioTracks",
|
||||
"valueType": "Array<Boolean>",
|
||||
"valueDescription": "Array of audio tracks along with their associated enable states"
|
||||
"valueType": "Object",
|
||||
"valueDescription": "Object of audio tracks along with their associated enable states"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1263,6 +1263,7 @@ Subscription value to receive the `SceneItemTransformChanged` high-volume event.
|
||||
- [InputShowStateChanged](#inputshowstatechanged)
|
||||
- [InputMuteStateChanged](#inputmutestatechanged)
|
||||
- [InputVolumeChanged](#inputvolumechanged)
|
||||
- [InputAudioBalanceChanged](#inputaudiobalancechanged)
|
||||
- [InputAudioSyncOffsetChanged](#inputaudiosyncoffsetchanged)
|
||||
- [InputAudioTracksChanged](#inputaudiotrackschanged)
|
||||
- [InputAudioMonitorTypeChanged](#inputaudiomonitortypechanged)
|
||||
@ -1666,6 +1667,24 @@ An input's volume level has changed.
|
||||
|
||||
---
|
||||
|
||||
### InputAudioBalanceChanged
|
||||
|
||||
The audio balance value of an input has changed.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Data Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| inputName | String | Name of the affected input |
|
||||
| inputAudioBalance | Number | New audio balance value of the input |
|
||||
|
||||
---
|
||||
|
||||
### InputAudioSyncOffsetChanged
|
||||
|
||||
The sync offset of an input has changed.
|
||||
@ -1698,7 +1717,7 @@ The audio tracks of an input have changed.
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| inputName | String | Name of the input |
|
||||
| inputAudioTracks | Array<Boolean> | Array of audio tracks along with their associated enable states |
|
||||
| inputAudioTracks | Object | Object of audio tracks along with their associated enable states |
|
||||
|
||||
---
|
||||
|
||||
@ -2045,6 +2064,7 @@ Studio mode has been enabled or disabled.
|
||||
- [SaveSourceScreenshot](#savesourcescreenshot)
|
||||
- [Scenes](#scenes-1)
|
||||
- [GetSceneList](#getscenelist)
|
||||
- [GetGroupList](#getgrouplist)
|
||||
- [GetCurrentProgramScene](#getcurrentprogramscene)
|
||||
- [SetCurrentProgramScene](#setcurrentprogramscene)
|
||||
- [GetCurrentPreviewScene](#getcurrentpreviewscene)
|
||||
@ -2055,6 +2075,7 @@ Studio mode has been enabled or disabled.
|
||||
- [Inputs](#inputs-1)
|
||||
- [GetInputList](#getinputlist)
|
||||
- [GetInputKindList](#getinputkindlist)
|
||||
- [GetSpecialInputs](#getspecialinputs)
|
||||
- [CreateInput](#createinput)
|
||||
- [RemoveInput](#removeinput)
|
||||
- [SetInputName](#setinputname)
|
||||
@ -2066,10 +2087,14 @@ Studio mode has been enabled or disabled.
|
||||
- [ToggleInputMute](#toggleinputmute)
|
||||
- [GetInputVolume](#getinputvolume)
|
||||
- [SetInputVolume](#setinputvolume)
|
||||
- [GetInputAudioBalance](#getinputaudiobalance)
|
||||
- [SetInputAudioBalance](#setinputaudiobalance)
|
||||
- [GetInputAudioSyncOffset](#getinputaudiosyncoffset)
|
||||
- [SetInputAudioSyncOffset](#setinputaudiosyncoffset)
|
||||
- [GetInputAudioMonitorType](#getinputaudiomonitortype)
|
||||
- [SetInputAudioMonitorType](#setinputaudiomonitortype)
|
||||
- [GetInputAudioTracks](#getinputaudiotracks)
|
||||
- [SetInputAudioTracks](#setinputaudiotracks)
|
||||
- [GetInputPropertiesListPropertyItems](#getinputpropertieslistpropertyitems)
|
||||
- [PressInputPropertiesButton](#pressinputpropertiesbutton)
|
||||
- [Transitions](#transitions)
|
||||
@ -2080,6 +2105,8 @@ Studio mode has been enabled or disabled.
|
||||
- [SetCurrentSceneTransitionDuration](#setcurrentscenetransitionduration)
|
||||
- [SetCurrentSceneTransitionSettings](#setcurrentscenetransitionsettings)
|
||||
- [TriggerStudioModeTransition](#triggerstudiomodetransition)
|
||||
- [Filters](#filters)
|
||||
- [GetSourceFilter](#getsourcefilter)
|
||||
- [Scene Items](#scene-items-1)
|
||||
- [GetSceneItemList](#getsceneitemlist)
|
||||
- [GetGroupItemList](#getgroupitemlist)
|
||||
@ -2095,6 +2122,13 @@ Studio mode has been enabled or disabled.
|
||||
- [SetSceneItemLocked](#setsceneitemlocked)
|
||||
- [GetSceneItemIndex](#getsceneitemindex)
|
||||
- [SetSceneItemIndex](#setsceneitemindex)
|
||||
- [GetSceneItemBlendMode](#getsceneitemblendmode)
|
||||
- [SetSceneItemBlendMode](#setsceneitemblendmode)
|
||||
- [Outputs](#outputs-1)
|
||||
- [GetVirtualCamStatus](#getvirtualcamstatus)
|
||||
- [ToggleVirtualCam](#togglevirtualcam)
|
||||
- [StartVirtualCam](#startvirtualcam)
|
||||
- [StopVirtualCam](#stopvirtualcam)
|
||||
- [Stream](#stream)
|
||||
- [GetStreamStatus](#getstreamstatus)
|
||||
- [ToggleStream](#togglestream)
|
||||
@ -2117,6 +2151,9 @@ Studio mode has been enabled or disabled.
|
||||
- [Ui](#ui-1)
|
||||
- [GetStudioModeEnabled](#getstudiomodeenabled)
|
||||
- [SetStudioModeEnabled](#setstudiomodeenabled)
|
||||
- [OpenInputPropertiesDialog](#openinputpropertiesdialog)
|
||||
- [OpenInputFiltersDialog](#openinputfiltersdialog)
|
||||
- [OpenInputInteractDialog](#openinputinteractdialog)
|
||||
|
||||
|
||||
|
||||
@ -2703,7 +2740,26 @@ Gets an array of all scenes in OBS.
|
||||
| ---- | :---: | ----------- |
|
||||
| currentProgramSceneName | String | Current program scene |
|
||||
| currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode |
|
||||
| scenes | Array<Object> | Array of scenes in OBS |
|
||||
| scenes | Array<Object> | Array of scenes |
|
||||
|
||||
---
|
||||
|
||||
### GetGroupList
|
||||
|
||||
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.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| groups | Array<String> | Array of group names |
|
||||
|
||||
---
|
||||
|
||||
@ -2880,6 +2936,28 @@ Gets an array of all available input kinds in OBS.
|
||||
|
||||
---
|
||||
|
||||
### GetSpecialInputs
|
||||
|
||||
Gets the names of all special inputs.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| desktop1 | String | Name of the Desktop Audio input |
|
||||
| desktop2 | String | Name of the Desktop Audio 2 input |
|
||||
| mic1 | String | Name of the Mic/Auxiliary Audio input |
|
||||
| mic2 | String | Name of the Mic/Auxiliary Audio 2 input |
|
||||
| mic3 | String | Name of the Mic/Auxiliary Audio 3 input |
|
||||
| mic4 | String | Name of the Mic/Auxiliary Audio 4 input |
|
||||
|
||||
---
|
||||
|
||||
### CreateInput
|
||||
|
||||
Creates a new input, adding it as a scene item to the specified scene.
|
||||
@ -3121,7 +3199,49 @@ Sets the volume setting of an input.
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input to set the volume of | None | N/A |
|
||||
| ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified |
|
||||
| ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= -26 | `inputVolumeMul` should be specified |
|
||||
| ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= 26 | `inputVolumeMul` should be specified |
|
||||
|
||||
---
|
||||
|
||||
### GetInputAudioBalance
|
||||
|
||||
Gets the audio balance of an input.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input to get the audio balance of | None | N/A |
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| inputAudioBalance | Number | Audio balance value from 0.0-1.0 |
|
||||
|
||||
---
|
||||
|
||||
### SetInputAudioBalance
|
||||
|
||||
Sets the audio balance of an input.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input to set the audio balance of | None | N/A |
|
||||
| inputAudioBalance | Number | New audio balance value | >= 0.0, <= 1.0 | N/A |
|
||||
|
||||
---
|
||||
|
||||
@ -3216,6 +3336,48 @@ Sets the audio monitor type of an input.
|
||||
|
||||
---
|
||||
|
||||
### GetInputAudioTracks
|
||||
|
||||
Gets the enable state of all audio tracks of an input.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input | None | N/A |
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| inputAudioTracks | Object | Object of audio tracks and associated enable states |
|
||||
|
||||
---
|
||||
|
||||
### SetInputAudioTracks
|
||||
|
||||
Sets the enable state of audio tracks of an input.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input | None | N/A |
|
||||
| inputAudioTracks | Object | Track settings to apply | None | N/A |
|
||||
|
||||
---
|
||||
|
||||
### GetInputPropertiesListPropertyItems
|
||||
|
||||
Gets the items of a list property from an input's properties.
|
||||
@ -3387,6 +3549,35 @@ Triggers the current scene transition. Same functionality as the `Transition` bu
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
## Filters
|
||||
|
||||
### GetSourceFilter
|
||||
|
||||
Gets the info for a specific source filter.
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| sourceName | String | Name of the source | None | N/A |
|
||||
| filterName | String | Name of the filter | None | N/A |
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| filterEnabled | Boolean | Whether the filter is enabled |
|
||||
| filterIndex | Number | Index of the filter in the list, beginning at 0 |
|
||||
| filterKind | String | The kind of filter |
|
||||
| filterSettings | Object | Settings object associated with the filter |
|
||||
|
||||
|
||||
## Scene Items
|
||||
|
||||
### GetSceneItemList
|
||||
@ -3736,6 +3927,119 @@ Scenes and Groups
|
||||
| sceneItemId | Number | Numeric ID of the scene item | >= 0 | N/A |
|
||||
| sceneItemIndex | Number | New index position of the scene item | >= 0 | N/A |
|
||||
|
||||
---
|
||||
|
||||
### GetSceneItemBlendMode
|
||||
|
||||
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
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| sceneName | String | Name of the scene the item is in | None | N/A |
|
||||
| sceneItemId | Number | Numeric ID of the scene item | >= 0 | N/A |
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| sceneItemBlendMode | String | Current blend mode |
|
||||
|
||||
---
|
||||
|
||||
### SetSceneItemBlendMode
|
||||
|
||||
Sets the blend mode of a scene item.
|
||||
|
||||
Scenes and Groups
|
||||
|
||||
- Complexity Rating: `2/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| sceneName | String | Name of the scene the item is in | None | N/A |
|
||||
| sceneItemId | Number | Numeric ID of the scene item | >= 0 | N/A |
|
||||
| sceneItemBlendMode | String | New blend mode | None | N/A |
|
||||
|
||||
|
||||
## Outputs
|
||||
|
||||
### GetVirtualCamStatus
|
||||
|
||||
Gets the status of the virtualcam output.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| outputActive | Boolean | Whether the output is active |
|
||||
|
||||
---
|
||||
|
||||
### ToggleVirtualCam
|
||||
|
||||
Toggles the state of the virtualcam output.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | :---: | ----------- |
|
||||
| outputActive | Boolean | Whether the output is active |
|
||||
|
||||
---
|
||||
|
||||
### StartVirtualCam
|
||||
|
||||
Starts the virtualcam output.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
---
|
||||
|
||||
### StopVirtualCam
|
||||
|
||||
Stops the virtualcam output.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
## Stream
|
||||
|
||||
@ -4026,4 +4330,55 @@ Enables or disables studio mode
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| studioModeEnabled | Boolean | True == Enabled, False == Disabled | None | N/A |
|
||||
|
||||
---
|
||||
|
||||
### OpenInputPropertiesDialog
|
||||
|
||||
Opens the properties dialog of an input.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input to open the dialog of | None | N/A |
|
||||
|
||||
---
|
||||
|
||||
### OpenInputFiltersDialog
|
||||
|
||||
Opens the filters dialog of an input.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input to open the dialog of | None | N/A |
|
||||
|
||||
---
|
||||
|
||||
### OpenInputInteractDialog
|
||||
|
||||
Opens the interact dialog of an input.
|
||||
|
||||
- Complexity Rating: `1/5`
|
||||
- Latest Supported RPC Version: `1`
|
||||
- Added in v5.0.0
|
||||
|
||||
|
||||
**Request Fields:**
|
||||
|
||||
| Name | Type | Description | Value Restrictions | ?Default Behavior |
|
||||
| ---- | :---: | ----------- | :----------------: | ----------------- |
|
||||
| inputName | String | Name of the input to open the dialog of | None | N/A |
|
||||
|
||||
|
||||
|
@ -22,6 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
|
||||
#include <obs.h>
|
||||
|
||||
#define OBS_WEBSOCKET_API_VERSION 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -134,9 +134,10 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
|
||||
signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this);
|
||||
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, 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_mixers", HandleInputAudioTracksChanged, this);
|
||||
//signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
|
||||
signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
|
||||
|
||||
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
|
||||
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
|
||||
@ -174,9 +175,10 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
||||
signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
|
||||
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, 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_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_ended", HandleMediaInputPlaybackEnded, this);
|
||||
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
|
||||
@ -197,7 +199,7 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
|
||||
|
||||
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 (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
||||
@ -208,14 +210,14 @@ 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()`
|
||||
// Enumerate inputs and connect each one
|
||||
obs_enum_sources([](void* param, obs_source_t* source) {
|
||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||
auto eventHandler = static_cast<EventHandler*>(param);
|
||||
eventHandler->ConnectSourceSignals(source);
|
||||
return true;
|
||||
}, private_data);
|
||||
|
||||
// Enumerate scenes and connect each one
|
||||
obs_enum_scenes([](void* param, obs_source_t* source) {
|
||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||
auto eventHandler = static_cast<EventHandler*>(param);
|
||||
eventHandler->ConnectSourceSignals(source);
|
||||
return true;
|
||||
}, private_data);
|
||||
@ -241,14 +243,14 @@ 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()`
|
||||
// Enumerate inputs and disconnect each one
|
||||
obs_enum_sources([](void* param, obs_source_t* source) {
|
||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||
auto eventHandler = static_cast<EventHandler*>(param);
|
||||
eventHandler->DisconnectSourceSignals(source);
|
||||
return true;
|
||||
}, private_data);
|
||||
|
||||
// Enumerate scenes and disconnect each one
|
||||
obs_enum_scenes([](void* param, obs_source_t* source) {
|
||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||
auto eventHandler = static_cast<EventHandler*>(param);
|
||||
eventHandler->DisconnectSourceSignals(source);
|
||||
return true;
|
||||
}, private_data);
|
||||
@ -264,18 +266,18 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
||||
break;
|
||||
|
||||
// Config
|
||||
//case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
|
||||
// eventHandler->HandleCurrentSceneCollectionChanging();
|
||||
// break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
|
||||
eventHandler->HandleCurrentSceneCollectionChanging();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
|
||||
eventHandler->HandleCurrentSceneCollectionChanged();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
|
||||
eventHandler->HandleSceneCollectionListChanged();
|
||||
break;
|
||||
//case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
|
||||
// eventHandler->HandleCurrentProfileChanging();
|
||||
// break;
|
||||
case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
|
||||
eventHandler->HandleCurrentProfileChanging();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
|
||||
eventHandler->HandleCurrentProfileChanged();
|
||||
break;
|
||||
@ -363,7 +365,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
||||
// Only called for creation of a public source
|
||||
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
|
||||
if (!eventHandler->_obsLoaded.load())
|
||||
@ -390,7 +392,7 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
|
||||
// Only called for destruction of a public source
|
||||
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
|
||||
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
|
||||
@ -418,7 +420,7 @@ void EventHandler::SourceDestroyedMultiHandler(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())
|
||||
return;
|
||||
@ -441,7 +443,7 @@ void EventHandler::SourceRemovedMultiHandler(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())
|
||||
return;
|
||||
|
@ -107,6 +107,7 @@ class EventHandler
|
||||
static void HandleInputShowStateChanged(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 HandleInputAudioBalanceChanged(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 HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
|
||||
|
@ -111,7 +111,7 @@ void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputNa
|
||||
*/
|
||||
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
|
||||
{
|
||||
auto eventHandler = reinterpret_cast<EventHandler*>(param);
|
||||
auto eventHandler = static_cast<EventHandler*>(param);
|
||||
|
||||
if (!eventHandler->_inputActiveStateChangedRef.load())
|
||||
return;
|
||||
@ -147,7 +147,7 @@ void EventHandler::HandleInputActiveStateChanged(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())
|
||||
return;
|
||||
@ -181,7 +181,7 @@ void EventHandler::HandleInputShowStateChanged(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");
|
||||
if (!source)
|
||||
@ -213,7 +213,7 @@ void EventHandler::HandleInputMuteStateChanged(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");
|
||||
if (!source)
|
||||
@ -236,6 +236,39 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
|
||||
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.
|
||||
*
|
||||
@ -252,7 +285,7 @@ void EventHandler::HandleInputVolumeChanged(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");
|
||||
if (!source)
|
||||
@ -272,8 +305,8 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
||||
/**
|
||||
* The audio tracks of an input have changed.
|
||||
*
|
||||
* @dataField inputName | String | Name of the input
|
||||
* @dataField inputAudioTracks | Array<Boolean> | Array of audio tracks along with their associated enable states
|
||||
* @dataField inputName | String | Name of the input
|
||||
* @dataField inputAudioTracks | Object | Object of audio tracks along with their associated enable states
|
||||
*
|
||||
* @eventType InputAudioTracksChanged
|
||||
* @eventSubscription Inputs
|
||||
@ -285,7 +318,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
|
||||
*/
|
||||
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");
|
||||
if (!source)
|
||||
@ -328,7 +361,7 @@ void EventHandler::HandleInputAudioTracksChanged(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");
|
||||
if (!source)
|
||||
|
@ -35,7 +35,7 @@ std::string GetMediaInputActionString(ObsMediaInputAction action) {
|
||||
|
||||
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");
|
||||
if (!source)
|
||||
@ -49,7 +49,7 @@ void EventHandler::SourceMediaPauseMultiHandler(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");
|
||||
if (!source)
|
||||
@ -63,7 +63,7 @@ void EventHandler::SourceMediaPlayMultiHandler(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");
|
||||
if (!source)
|
||||
@ -77,7 +77,7 @@ void EventHandler::SourceMediaRestartMultiHandler(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");
|
||||
if (!source)
|
||||
@ -91,7 +91,7 @@ void EventHandler::SourceMediaStopMultiHandler(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");
|
||||
if (!source)
|
||||
@ -105,7 +105,7 @@ void EventHandler::SourceMediaNextMultiHandler(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");
|
||||
if (!source)
|
||||
@ -132,7 +132,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(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");
|
||||
if (!source)
|
||||
@ -161,7 +161,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(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");
|
||||
if (!source)
|
||||
|
@ -37,7 +37,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
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");
|
||||
if (!scene)
|
||||
@ -74,7 +74,7 @@ void EventHandler::HandleSceneItemCreated(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");
|
||||
if (!scene)
|
||||
@ -107,7 +107,7 @@ void EventHandler::HandleSceneItemRemoved(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");
|
||||
if (!scene)
|
||||
@ -136,7 +136,7 @@ void EventHandler::HandleSceneItemListReindexed(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");
|
||||
if (!scene)
|
||||
@ -172,7 +172,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
|
||||
*/
|
||||
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");
|
||||
if (!scene)
|
||||
@ -208,7 +208,7 @@ void EventHandler::HandleSceneItemLockStateChanged(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())
|
||||
return;
|
||||
|
@ -48,6 +48,7 @@ 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] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
|
||||
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
|
||||
|
||||
// Initialize the cpu stats
|
||||
_cpuUsageInfo = os_cpu_usage_info_start();
|
||||
@ -68,7 +69,7 @@ bool obs_module_load(void)
|
||||
|
||||
// Initialize the settings dialog
|
||||
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);
|
||||
obs_frontend_pop_ui_translation();
|
||||
|
||||
|
@ -21,13 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
|
||||
#include <memory>
|
||||
#include <obs.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma push_macro("strtoll")
|
||||
#endif
|
||||
#include <util/platform.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma pop_macro("strtoll")
|
||||
#endif
|
||||
|
||||
#include "utils/Obs.h"
|
||||
#include "plugin-macros.generated.h"
|
||||
|
@ -109,7 +109,7 @@ static void ObsTickCallback(void *param, float)
|
||||
{
|
||||
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
|
||||
|
||||
auto serialFrameBatch = reinterpret_cast<SerialFrameBatch*>(param);
|
||||
auto serialFrameBatch = static_cast<SerialFrameBatch*>(param);
|
||||
|
||||
// Increment frame count
|
||||
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/>
|
||||
*/
|
||||
|
||||
#ifdef PLUGIN_TESTS
|
||||
#include <util/profiler.hpp>
|
||||
#endif
|
||||
|
||||
#include "RequestHandler.h"
|
||||
|
||||
const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
{
|
||||
// General
|
||||
{"GetVersion", &RequestHandler::GetVersion},
|
||||
@ -52,9 +56,12 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
{"GetSourceActive", &RequestHandler::GetSourceActive},
|
||||
{"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot},
|
||||
{"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot},
|
||||
{"GetSourcePrivateSettings", &RequestHandler::GetSourcePrivateSettings},
|
||||
{"SetSourcePrivateSettings", &RequestHandler::SetSourcePrivateSettings},
|
||||
|
||||
// Scenes
|
||||
{"GetSceneList", &RequestHandler::GetSceneList},
|
||||
{"GetGroupList", &RequestHandler::GetGroupList},
|
||||
{"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene},
|
||||
{"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene},
|
||||
{"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene},
|
||||
@ -66,8 +73,9 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
// Inputs
|
||||
{"GetInputList", &RequestHandler::GetInputList},
|
||||
{"GetInputKindList", &RequestHandler::GetInputKindList},
|
||||
{"GetSpecialInputs", &RequestHandler::GetSpecialInputs},
|
||||
{"CreateInput", &RequestHandler::CreateInput},
|
||||
//{"RemoveInput", &RequestHandler::RemoveInput}, // Disabled for now. Pending obs-studio#5276
|
||||
{"RemoveInput", &RequestHandler::RemoveInput},
|
||||
{"SetInputName", &RequestHandler::SetInputName},
|
||||
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
|
||||
{"GetInputSettings", &RequestHandler::GetInputSettings},
|
||||
@ -77,10 +85,14 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
{"ToggleInputMute", &RequestHandler::ToggleInputMute},
|
||||
{"GetInputVolume", &RequestHandler::GetInputVolume},
|
||||
{"SetInputVolume", &RequestHandler::SetInputVolume},
|
||||
{"GetInputAudioBalance", &RequestHandler::GetInputAudioBalance},
|
||||
{"SetInputAudioBalance", &RequestHandler::SetInputAudioBalance},
|
||||
{"GetInputAudioSyncOffset", &RequestHandler::GetInputAudioSyncOffset},
|
||||
{"SetInputAudioSyncOffset", &RequestHandler::SetInputAudioSyncOffset},
|
||||
{"GetInputAudioMonitorType", &RequestHandler::GetInputAudioMonitorType},
|
||||
{"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType},
|
||||
{"GetInputAudioTracks", &RequestHandler::GetInputAudioTracks},
|
||||
{"SetInputAudioTracks", &RequestHandler::SetInputAudioTracks},
|
||||
{"GetInputPropertiesListPropertyItems", &RequestHandler::GetInputPropertiesListPropertyItems},
|
||||
{"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton},
|
||||
|
||||
@ -93,6 +105,9 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
|
||||
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
|
||||
|
||||
// Filters
|
||||
{"GetSourceFilter", &RequestHandler::GetSourceFilter},
|
||||
|
||||
// Scene Items
|
||||
{"GetSceneItemList", &RequestHandler::GetSceneItemList},
|
||||
{"GetGroupSceneItemList", &RequestHandler::GetGroupSceneItemList},
|
||||
@ -108,6 +123,14 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
|
||||
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
|
||||
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
|
||||
{"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode},
|
||||
{"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode},
|
||||
|
||||
// Outputs
|
||||
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
|
||||
{"ToggleVirtualCam", &RequestHandler::ToggleVirtualCam},
|
||||
{"StartVirtualCam", &RequestHandler::StartVirtualCam},
|
||||
{"StopVirtualCam", &RequestHandler::StopVirtualCam},
|
||||
|
||||
// Stream
|
||||
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
||||
@ -123,7 +146,7 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
|
||||
{"PauseRecord", &RequestHandler::PauseRecord},
|
||||
{"ResumeRecord", &RequestHandler::ResumeRecord},
|
||||
//{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
|
||||
{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
|
||||
|
||||
// Media Inputs
|
||||
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},
|
||||
@ -134,6 +157,9 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
||||
// Ui
|
||||
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
|
||||
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
|
||||
{"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog},
|
||||
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
||||
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
||||
};
|
||||
|
||||
RequestHandler::RequestHandler(SessionPtr session) :
|
||||
@ -143,6 +169,10 @@ RequestHandler::RequestHandler(SessionPtr session) :
|
||||
|
||||
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())
|
||||
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
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <obs.hpp>
|
||||
#include <obs-frontend-api.h>
|
||||
|
||||
@ -74,9 +74,12 @@ class RequestHandler {
|
||||
RequestResult GetSourceActive(const Request&);
|
||||
RequestResult GetSourceScreenshot(const Request&);
|
||||
RequestResult SaveSourceScreenshot(const Request&);
|
||||
RequestResult GetSourcePrivateSettings(const Request&);
|
||||
RequestResult SetSourcePrivateSettings(const Request&);
|
||||
|
||||
// Scenes
|
||||
RequestResult GetSceneList(const Request&);
|
||||
RequestResult GetGroupList(const Request&);
|
||||
RequestResult GetCurrentProgramScene(const Request&);
|
||||
RequestResult SetCurrentProgramScene(const Request&);
|
||||
RequestResult GetCurrentPreviewScene(const Request&);
|
||||
@ -88,6 +91,7 @@ class RequestHandler {
|
||||
// Inputs
|
||||
RequestResult GetInputList(const Request&);
|
||||
RequestResult GetInputKindList(const Request&);
|
||||
RequestResult GetSpecialInputs(const Request&);
|
||||
RequestResult CreateInput(const Request&);
|
||||
RequestResult RemoveInput(const Request&);
|
||||
RequestResult SetInputName(const Request&);
|
||||
@ -99,10 +103,14 @@ class RequestHandler {
|
||||
RequestResult ToggleInputMute(const Request&);
|
||||
RequestResult GetInputVolume(const Request&);
|
||||
RequestResult SetInputVolume(const Request&);
|
||||
RequestResult GetInputAudioBalance(const Request&);
|
||||
RequestResult SetInputAudioBalance(const Request&);
|
||||
RequestResult GetInputAudioSyncOffset(const Request&);
|
||||
RequestResult SetInputAudioSyncOffset(const Request&);
|
||||
RequestResult GetInputAudioMonitorType(const Request&);
|
||||
RequestResult SetInputAudioMonitorType(const Request&);
|
||||
RequestResult GetInputAudioTracks(const Request&);
|
||||
RequestResult SetInputAudioTracks(const Request&);
|
||||
RequestResult GetInputPropertiesListPropertyItems(const Request&);
|
||||
RequestResult PressInputPropertiesButton(const Request&);
|
||||
|
||||
@ -115,6 +123,9 @@ class RequestHandler {
|
||||
RequestResult SetCurrentSceneTransitionSettings(const Request&);
|
||||
RequestResult TriggerStudioModeTransition(const Request&);
|
||||
|
||||
// Filters
|
||||
RequestResult GetSourceFilter(const Request&);
|
||||
|
||||
// Scene Items
|
||||
RequestResult GetSceneItemList(const Request&);
|
||||
RequestResult GetGroupSceneItemList(const Request&);
|
||||
@ -130,6 +141,14 @@ class RequestHandler {
|
||||
RequestResult SetSceneItemLocked(const Request&);
|
||||
RequestResult GetSceneItemIndex(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&);
|
||||
|
||||
// Stream
|
||||
RequestResult GetStreamStatus(const Request&);
|
||||
@ -156,7 +175,10 @@ class RequestHandler {
|
||||
// Ui
|
||||
RequestResult GetStudioModeEnabled(const Request&);
|
||||
RequestResult SetStudioModeEnabled(const Request&);
|
||||
RequestResult OpenInputPropertiesDialog(const Request&);
|
||||
RequestResult OpenInputFiltersDialog(const Request&);
|
||||
RequestResult OpenInputInteractDialog(const Request&);
|
||||
|
||||
SessionPtr _session;
|
||||
static const std::map<std::string, RequestMethodHandler> _handlerMap;
|
||||
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
||||
};
|
||||
|
@ -159,7 +159,7 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
|
||||
// Avoid queueing tasks if nothing will change
|
||||
if (currentSceneCollectionName != sceneCollectionName) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -193,7 +193,7 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
|
||||
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
|
||||
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;
|
||||
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
||||
if (!success)
|
||||
@ -252,7 +252,7 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
|
||||
// Avoid queueing tasks if nothing will change
|
||||
if (currentProfileName != profileName) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -284,7 +284,7 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
|
||||
if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end())
|
||||
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)));
|
||||
|
||||
return RequestResult::Success();
|
||||
@ -318,7 +318,7 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
|
||||
if (profiles.size() < 2)
|
||||
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)));
|
||||
|
||||
return RequestResult::Success();
|
||||
|
@ -18,3 +18,40 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "RequestHandler.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
@ -83,6 +83,43 @@ RequestResult RequestHandler::GetInputKindList(const Request& request)
|
||||
return RequestResult::Success(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all special inputs.
|
||||
*
|
||||
* @responseField desktop1 | String | Name of the Desktop Audio input
|
||||
* @responseField desktop2 | String | Name of the Desktop Audio 2 input
|
||||
* @responseField mic1 | String | Name of the Mic/Auxiliary Audio input
|
||||
* @responseField mic2 | String | Name of the Mic/Auxiliary Audio 2 input
|
||||
* @responseField mic3 | String | Name of the Mic/Auxiliary Audio 3 input
|
||||
* @responseField mic4 | String | Name of the Mic/Auxiliary Audio 4 input
|
||||
*
|
||||
* @requestType GetSpecialInputs
|
||||
* @complexity 2
|
||||
* @rpcVersion -1
|
||||
* @initialVersion 5.0.0
|
||||
* @api requests
|
||||
* @category inputs
|
||||
*/
|
||||
RequestResult RequestHandler::GetSpecialInputs(const Request&)
|
||||
{
|
||||
json responseData;
|
||||
|
||||
std::vector<std::string> channels = {"desktop1", "desktop2", "mic1", "mic2", "mic3", "mic4"};
|
||||
|
||||
size_t channelId = 1;
|
||||
for (auto &channel : channels) {
|
||||
OBSSourceAutoRelease input = obs_get_output_source(channelId);
|
||||
if (!input)
|
||||
responseData[channel] = nullptr;
|
||||
else
|
||||
responseData[channel] = obs_source_get_name(input);
|
||||
|
||||
channelId++;
|
||||
}
|
||||
|
||||
return RequestResult::Success(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new input, adding it as a scene item to the specified scene.
|
||||
*
|
||||
@ -346,6 +383,9 @@ RequestResult RequestHandler::GetInputMute(const Request& request)
|
||||
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["inputMuted"] = obs_source_muted(input);
|
||||
return RequestResult::Success(responseData);
|
||||
@ -372,6 +412,9 @@ RequestResult RequestHandler::SetInputMute(const Request& request)
|
||||
if (!(input && request.ValidateBoolean("inputMuted", 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"]);
|
||||
|
||||
return RequestResult::Success();
|
||||
@ -399,6 +442,9 @@ RequestResult RequestHandler::ToggleInputMute(const Request& request)
|
||||
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.");
|
||||
|
||||
bool inputMuted = !obs_source_muted(input);
|
||||
obs_source_set_muted(input, inputMuted);
|
||||
|
||||
@ -430,6 +476,9 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
|
||||
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.");
|
||||
|
||||
float inputVolumeMul = obs_source_get_volume(input);
|
||||
float inputVolumeDb = obs_mul_to_db(inputVolumeMul);
|
||||
if (inputVolumeDb == -INFINITY)
|
||||
@ -446,7 +495,7 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
|
||||
*
|
||||
* @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 ?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
|
||||
* @complexity 3
|
||||
@ -463,6 +512,9 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
|
||||
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.");
|
||||
|
||||
bool hasMul = request.Contains("inputVolumeMul");
|
||||
if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
@ -488,6 +540,67 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
|
||||
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.
|
||||
*
|
||||
@ -512,6 +625,9 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request)
|
||||
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;
|
||||
// Offset is stored in nanoseconds in OBS.
|
||||
responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
|
||||
@ -540,6 +656,9 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request)
|
||||
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
|
||||
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"];
|
||||
obs_source_set_sync_offset(input, syncOffset * 1000000);
|
||||
|
||||
@ -573,6 +692,9 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
|
||||
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["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
|
||||
|
||||
@ -600,6 +722,12 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
||||
if (!(input && request.ValidateString("monitorType", 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;
|
||||
std::string monitorTypeString = request.RequestData["monitorType"];
|
||||
if (monitorTypeString == "OBS_MONITORING_TYPE_NONE")
|
||||
@ -616,6 +744,95 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
||||
return RequestResult::Success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enable state of all audio tracks of an input.
|
||||
*
|
||||
* @requestField inputName | String | Name of the input
|
||||
*
|
||||
* @responseField inputAudioTracks | Object | Object of audio tracks and associated enable states
|
||||
*
|
||||
* @requestType GetInputAudioTracks
|
||||
* @complexity 2
|
||||
* @rpcVersion -1
|
||||
* @initialVersion 5.0.0
|
||||
* @api requests
|
||||
* @category inputs
|
||||
*/
|
||||
RequestResult RequestHandler::GetInputAudioTracks(const Request& request)
|
||||
{
|
||||
RequestStatus::RequestStatus statusCode;
|
||||
std::string comment;
|
||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
||||
if (!input)
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
|
||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
||||
|
||||
long long tracks = obs_source_get_audio_mixers(input);
|
||||
|
||||
json inputAudioTracks;
|
||||
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
|
||||
inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1);
|
||||
}
|
||||
|
||||
json responseData;
|
||||
responseData["inputAudioTracks"] = inputAudioTracks;
|
||||
|
||||
return RequestResult::Success(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enable state of audio tracks of an input.
|
||||
*
|
||||
* @requestField inputName | String | Name of the input
|
||||
* @requestField inputAudioTracks | Object | Track settings to apply
|
||||
*
|
||||
* @requestType SetInputAudioTracks
|
||||
* @complexity 2
|
||||
* @rpcVersion -1
|
||||
* @initialVersion 5.0.0
|
||||
* @api requests
|
||||
* @category inputs
|
||||
*/
|
||||
RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
|
||||
{
|
||||
RequestStatus::RequestStatus statusCode;
|
||||
std::string comment;
|
||||
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
|
||||
if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment))
|
||||
return RequestResult::Error(statusCode, comment);
|
||||
|
||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
|
||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
||||
|
||||
json inputAudioTracks = request.RequestData["inputAudioTracks"];
|
||||
|
||||
long long mixers = obs_source_get_audio_mixers(input);
|
||||
|
||||
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
|
||||
std::string track = std::to_string(i + 1);
|
||||
|
||||
if (!Utils::Json::Contains(inputAudioTracks, track))
|
||||
continue;
|
||||
|
||||
if (!inputAudioTracks[track].is_boolean())
|
||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean.");
|
||||
|
||||
bool enabled = inputAudioTracks[track];
|
||||
|
||||
if (enabled)
|
||||
mixers |= (1 << i);
|
||||
else
|
||||
mixers &= ~(1 << i);
|
||||
}
|
||||
|
||||
// Decided that checking if tracks have actually changed is unnecessary
|
||||
obs_source_set_audio_mixers(input, mixers);
|
||||
|
||||
return RequestResult::Success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the items of a list property from an input's properties.
|
||||
*
|
||||
|
126
src/requesthandler/RequestHandler_Outputs.cpp
Normal file
126
src/requesthandler/RequestHandler_Outputs.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
@ -631,3 +631,81 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* @responseField currentProgramSceneName | String | Current program scene
|
||||
* @responseField currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode
|
||||
* @responseField scenes | Array<Object> | Array of scenes in OBS
|
||||
* @responseField scenes | Array<Object> | Array of scenes
|
||||
*
|
||||
* @requestType GetSceneList
|
||||
* @complexity 2
|
||||
@ -54,6 +54,29 @@ RequestResult RequestHandler::GetSceneList(const Request&)
|
||||
return RequestResult::Success(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of all groups in OBS.
|
||||
*
|
||||
* Groups in OBS are actually scenes, but renamed and modified. In obs-websocket, we treat them as scenes where we can.
|
||||
*
|
||||
* @responseField groups | Array<String> | Array of group names
|
||||
*
|
||||
* @requestType GetGroupList
|
||||
* @complexity 2
|
||||
* @rpcVersion -1
|
||||
* @initialVersion 5.0.0
|
||||
* @api requests
|
||||
* @category scenes
|
||||
*/
|
||||
RequestResult RequestHandler::GetGroupList(const Request&)
|
||||
{
|
||||
json responseData;
|
||||
|
||||
responseData["groups"] = Utils::Obs::ArrayHelper::GetGroupList();
|
||||
|
||||
return RequestResult::Success(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current program scene.
|
||||
*
|
||||
|
@ -312,3 +312,39 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -70,3 +70,81 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -295,6 +295,27 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
|
||||
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
|
||||
{
|
||||
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
||||
|
@ -29,6 +29,12 @@ enum ObsWebSocketSceneFilter {
|
||||
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
|
||||
{
|
||||
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_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;
|
||||
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;
|
||||
|
||||
std::string RequestType;
|
||||
|
@ -21,7 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
#include "Platform.h"
|
||||
#include "../plugin-macros.generated.h"
|
||||
|
||||
bool Utils::Json::JsonArrayIsValidObsArray(json j)
|
||||
bool Utils::Json::JsonArrayIsValidObsArray(const json &j)
|
||||
{
|
||||
for (auto it : j) {
|
||||
if (!it.is_object())
|
||||
@ -191,7 +191,7 @@ bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
|
||||
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);
|
||||
return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);
|
||||
|
@ -27,10 +27,11 @@ using json = nlohmann::json;
|
||||
|
||||
namespace Utils {
|
||||
namespace Json {
|
||||
bool JsonArrayIsValidObsArray(json j);
|
||||
bool JsonArrayIsValidObsArray(const json &j);
|
||||
obs_data_t *JsonToObsData(json j);
|
||||
json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_d
|
||||
template <typename T> T* GetCalldataPointer(const calldata_t *data, const char* name) {
|
||||
void *ptr = nullptr;
|
||||
calldata_get_ptr(data, name, &ptr);
|
||||
return reinterpret_cast<T*>(ptr);
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
enum ObsOutputState {
|
||||
@ -163,6 +163,7 @@ namespace Utils {
|
||||
std::string GetMediaInputState(obs_source_t *input);
|
||||
std::string GetLastReplayBufferFilePath();
|
||||
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
||||
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
|
||||
std::string DurationToTimecode(uint64_t);
|
||||
std::string GetOutputState(ObsOutputState state);
|
||||
}
|
||||
@ -170,11 +171,13 @@ namespace Utils {
|
||||
namespace EnumHelper {
|
||||
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
||||
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
||||
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
|
||||
}
|
||||
|
||||
namespace NumberHelper {
|
||||
uint64_t GetOutputDuration(obs_output_t *output);
|
||||
size_t GetSceneCount();
|
||||
size_t GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter);
|
||||
}
|
||||
|
||||
namespace ArrayHelper {
|
||||
@ -183,6 +186,7 @@ namespace Utils {
|
||||
std::vector<obs_hotkey_t *> GetHotkeyList();
|
||||
std::vector<std::string> GetHotkeyNameList();
|
||||
std::vector<json> GetSceneList();
|
||||
std::vector<std::string> GetGroupList();
|
||||
std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false);
|
||||
std::vector<json> GetInputList(std::string inputKind = "");
|
||||
std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false);
|
||||
|
@ -29,7 +29,7 @@ struct CreateSceneItemData {
|
||||
|
||||
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
||||
{
|
||||
auto *data = reinterpret_cast<CreateSceneItemData*>(_data);
|
||||
auto *data = static_cast<CreateSceneItemData*>(_data);
|
||||
data->sceneItem = obs_scene_add(scene, data->source);
|
||||
|
||||
if (data->sceneItemTransform)
|
||||
|
@ -61,7 +61,7 @@ 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 = reinterpret_cast<std::vector<obs_hotkey_t *> *>(data);
|
||||
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
||||
|
||||
ret->push_back(hotkey);
|
||||
|
||||
@ -91,9 +91,6 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
|
||||
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;
|
||||
@ -109,13 +106,33 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
|
||||
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 = reinterpret_cast<std::pair<std::vector<json>, bool>*>(param);
|
||||
auto enumData = static_cast<std::pair<std::vector<json>, bool>*>(param);
|
||||
|
||||
json item;
|
||||
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||
@ -158,7 +175,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||
return true;
|
||||
|
||||
auto inputInfo = reinterpret_cast<EnumInputInfo*>(param);
|
||||
auto inputInfo = static_cast<EnumInputInfo*>(param);
|
||||
|
||||
std::string inputKind = obs_source_get_id(input);
|
||||
|
||||
|
@ -23,25 +23,38 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
|
||||
{
|
||||
size_t ret;
|
||||
auto sceneEnumProc = [](void *param, obs_source_t *scene) {
|
||||
auto ret = reinterpret_cast<size_t*>(param);
|
||||
auto ret = static_cast<size_t*>(param);
|
||||
|
||||
if (obs_source_is_group(scene))
|
||||
return true;
|
||||
@ -52,3 +52,28 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -64,12 +64,10 @@ std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
|
||||
|
||||
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
|
||||
{
|
||||
//char *recordOutputPath = obs_frontend_get_current_record_output_path();
|
||||
//std::string ret = recordOutputPath;
|
||||
//bfree(recordOutputPath);
|
||||
//return ret;
|
||||
|
||||
return "";
|
||||
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)
|
||||
@ -144,6 +142,20 @@ std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_typ
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -1,354 +1,353 @@
|
||||
/*
|
||||
obs-websocket
|
||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||
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 <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Obs.h"
|
||||
#include "Obs_VolumeMeter.h"
|
||||
#include "Obs_VolumeMeter_Helpers.h"
|
||||
#include "../obs-websocket.h"
|
||||
|
||||
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
||||
PeakMeterType(SAMPLE_PEAK_METER),
|
||||
_input(obs_source_get_weak_source(input)),
|
||||
_channels(0),
|
||||
_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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Utils::Obs::VolumeMeter::Meter::~Meter()
|
||||
{
|
||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||
if (!input) {
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool Utils::Obs::VolumeMeter::Meter::InputValid()
|
||||
{
|
||||
// return !obs_weak_source_expired(_input);
|
||||
return true;
|
||||
}
|
||||
|
||||
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
||||
{
|
||||
json ret;
|
||||
|
||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||
if (!input) {
|
||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> levels;
|
||||
const float volume = _muted ? 0.0f : _volume.load();
|
||||
|
||||
std::unique_lock<std::mutex> l(_mutex);
|
||||
|
||||
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
|
||||
ResetAudioLevels();
|
||||
|
||||
for (int channel = 0; channel < _channels; channel++) {
|
||||
std::vector<float> level;
|
||||
level.push_back(_magnitude[channel] * volume);
|
||||
level.push_back(_peak[channel] * volume);
|
||||
level.push_back(_peak[channel]);
|
||||
|
||||
levels.push_back(level);
|
||||
}
|
||||
l.unlock();
|
||||
|
||||
ret["inputName"] = obs_source_get_name(input);
|
||||
ret["inputLevelsMul"] = levels;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels()
|
||||
{
|
||||
_lastUpdate = 0;
|
||||
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) {
|
||||
_magnitude[channelNumber] = 0;
|
||||
_peak[channelNumber] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data)
|
||||
{
|
||||
int channels = 0;
|
||||
for (int i = 0; i < MAX_AV_PLANES; i++) {
|
||||
if (data->data[i])
|
||||
channels++;
|
||||
}
|
||||
|
||||
bool channelsChanged = _channels != channels;
|
||||
_channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS);
|
||||
|
||||
if (channelsChanged)
|
||||
ResetAudioLevels();
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
||||
{
|
||||
size_t sampleCount = data->frames;
|
||||
int channelNumber = 0;
|
||||
|
||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||
float *samples = (float*)data->data[planeNumber];
|
||||
if (!samples)
|
||||
continue;
|
||||
|
||||
if (((uintptr_t)samples & 0xf) > 0) {
|
||||
_peak[channelNumber] = 1.0f;
|
||||
channelNumber++;
|
||||
continue;
|
||||
}
|
||||
|
||||
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
|
||||
|
||||
float peak;
|
||||
switch (PeakMeterType) {
|
||||
default:
|
||||
case SAMPLE_PEAK_METER:
|
||||
peak = GetSamplePeak(previousSamples, samples, sampleCount);
|
||||
break;
|
||||
case TRUE_PEAK_METER:
|
||||
peak = GetTruePeak(previousSamples, samples, sampleCount);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (sampleCount) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
|
||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
|
||||
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
break;
|
||||
case 2:
|
||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
|
||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
|
||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
break;
|
||||
case 3:
|
||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
|
||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
break;
|
||||
default:
|
||||
_previousSamples[channelNumber][0] = samples[sampleCount - 4];
|
||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
}
|
||||
|
||||
_peak[channelNumber] = peak;
|
||||
|
||||
channelNumber++;
|
||||
}
|
||||
|
||||
for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++)
|
||||
_peak[channelNumber] = 0.0;
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
|
||||
{
|
||||
size_t sampleCount = data->frames;
|
||||
|
||||
int channelNumber = 0;
|
||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||
float *samples = (float*)data->data[planeNumber];
|
||||
if (!samples)
|
||||
continue;
|
||||
|
||||
float sum = 0.0;
|
||||
for (size_t i = 0; i < sampleCount; i++) {
|
||||
float sample = samples[i];
|
||||
sum += sample * sample;
|
||||
}
|
||||
|
||||
_magnitude[channelNumber] = std::sqrt(sum / sampleCount);
|
||||
|
||||
channelNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::unique_lock<std::mutex> l(c->_mutex);
|
||||
|
||||
c->_muted = muted;
|
||||
c->ProcessAudioChannels(data);
|
||||
c->ProcessPeak(data);
|
||||
c->ProcessMagnitude(data);
|
||||
|
||||
c->_lastUpdate = os_gettime_ns();
|
||||
}
|
||||
|
||||
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
|
||||
{
|
||||
auto c = static_cast<Meter*>(priv_data);
|
||||
|
||||
c->_volume = (float)calldata_float(cd, "volume");
|
||||
}
|
||||
|
||||
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) :
|
||||
_updateCallback(cb),
|
||||
_updatePeriod(updatePeriod),
|
||||
_running(false)
|
||||
{
|
||||
signal_handler_t *sh = obs_get_signal_handler();
|
||||
if (!sh)
|
||||
return;
|
||||
|
||||
auto enumProc = [](void *priv_data, obs_source_t *input) {
|
||||
auto c = static_cast<Handler*>(priv_data);
|
||||
|
||||
if (!obs_source_active(input))
|
||||
return true;
|
||||
|
||||
uint32_t flags = obs_source_get_output_flags(input);
|
||||
if ((flags & OBS_SOURCE_AUDIO) == 0)
|
||||
return true;
|
||||
|
||||
c->_meters.emplace_back(std::move(new Meter(input)));
|
||||
|
||||
return true;
|
||||
};
|
||||
obs_enum_sources(enumProc, this);
|
||||
|
||||
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
|
||||
signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
||||
|
||||
_running = true;
|
||||
_updateThread = std::thread(&Handler::UpdateThread, this);
|
||||
|
||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
|
||||
}
|
||||
|
||||
Utils::Obs::VolumeMeter::Handler::~Handler()
|
||||
{
|
||||
signal_handler_t *sh = obs_get_signal_handler();
|
||||
if (!sh)
|
||||
return;
|
||||
|
||||
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
|
||||
signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
||||
|
||||
if (_running) {
|
||||
_running = false;
|
||||
_cond.notify_all();
|
||||
}
|
||||
|
||||
if (_updateThread.joinable())
|
||||
_updateThread.join();
|
||||
|
||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
|
||||
}
|
||||
|
||||
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
||||
{
|
||||
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; }))
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<json> inputs;
|
||||
std::unique_lock<std::mutex> l(_meterMutex);
|
||||
for (auto &meter : _meters) {
|
||||
if (meter->InputValid())
|
||||
inputs.push_back(meter->GetMeterData());
|
||||
}
|
||||
l.unlock();
|
||||
|
||||
if (_updateCallback)
|
||||
_updateCallback(inputs);
|
||||
}
|
||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
|
||||
}
|
||||
|
||||
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
|
||||
{
|
||||
auto c = static_cast<Handler*>(priv_data);
|
||||
|
||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||
if (!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)
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
auto c = static_cast<Handler*>(priv_data);
|
||||
|
||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||
if (!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);
|
||||
std::vector<MeterPtr>::iterator iter;
|
||||
for (iter = c->_meters.begin(); iter != c->_meters.end();) {
|
||||
if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input))
|
||||
iter = c->_meters.erase(iter);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
/*
|
||||
obs-websocket
|
||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||
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 <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Obs.h"
|
||||
#include "Obs_VolumeMeter.h"
|
||||
#include "Obs_VolumeMeter_Helpers.h"
|
||||
#include "../obs-websocket.h"
|
||||
|
||||
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
||||
PeakMeterType(SAMPLE_PEAK_METER),
|
||||
_input(obs_source_get_weak_source(input)),
|
||||
_channels(0),
|
||||
_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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Utils::Obs::VolumeMeter::Meter::~Meter()
|
||||
{
|
||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||
if (!input) {
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool Utils::Obs::VolumeMeter::Meter::InputValid()
|
||||
{
|
||||
return !obs_weak_source_expired(_input);
|
||||
}
|
||||
|
||||
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
||||
{
|
||||
json ret;
|
||||
|
||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||
if (!input) {
|
||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> levels;
|
||||
const float volume = _muted ? 0.0f : _volume.load();
|
||||
|
||||
std::unique_lock<std::mutex> l(_mutex);
|
||||
|
||||
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
|
||||
ResetAudioLevels();
|
||||
|
||||
for (int channel = 0; channel < _channels; channel++) {
|
||||
std::vector<float> level;
|
||||
level.push_back(_magnitude[channel] * volume);
|
||||
level.push_back(_peak[channel] * volume);
|
||||
level.push_back(_peak[channel]);
|
||||
|
||||
levels.push_back(level);
|
||||
}
|
||||
l.unlock();
|
||||
|
||||
ret["inputName"] = obs_source_get_name(input);
|
||||
ret["inputLevelsMul"] = levels;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels()
|
||||
{
|
||||
_lastUpdate = 0;
|
||||
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) {
|
||||
_magnitude[channelNumber] = 0;
|
||||
_peak[channelNumber] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data)
|
||||
{
|
||||
int channels = 0;
|
||||
for (int i = 0; i < MAX_AV_PLANES; i++) {
|
||||
if (data->data[i])
|
||||
channels++;
|
||||
}
|
||||
|
||||
bool channelsChanged = _channels != channels;
|
||||
_channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS);
|
||||
|
||||
if (channelsChanged)
|
||||
ResetAudioLevels();
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
|
||||
{
|
||||
size_t sampleCount = data->frames;
|
||||
int channelNumber = 0;
|
||||
|
||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||
float *samples = (float*)data->data[planeNumber];
|
||||
if (!samples)
|
||||
continue;
|
||||
|
||||
if (((uintptr_t)samples & 0xf) > 0) {
|
||||
_peak[channelNumber] = 1.0f;
|
||||
channelNumber++;
|
||||
continue;
|
||||
}
|
||||
|
||||
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
|
||||
|
||||
float peak;
|
||||
switch (PeakMeterType) {
|
||||
default:
|
||||
case SAMPLE_PEAK_METER:
|
||||
peak = GetSamplePeak(previousSamples, samples, sampleCount);
|
||||
break;
|
||||
case TRUE_PEAK_METER:
|
||||
peak = GetTruePeak(previousSamples, samples, sampleCount);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (sampleCount) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
|
||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
|
||||
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
break;
|
||||
case 2:
|
||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
|
||||
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
|
||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
break;
|
||||
case 3:
|
||||
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
|
||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
break;
|
||||
default:
|
||||
_previousSamples[channelNumber][0] = samples[sampleCount - 4];
|
||||
_previousSamples[channelNumber][1] = samples[sampleCount - 3];
|
||||
_previousSamples[channelNumber][2] = samples[sampleCount - 2];
|
||||
_previousSamples[channelNumber][3] = samples[sampleCount - 1];
|
||||
}
|
||||
|
||||
_peak[channelNumber] = peak;
|
||||
|
||||
channelNumber++;
|
||||
}
|
||||
|
||||
for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++)
|
||||
_peak[channelNumber] = 0.0;
|
||||
}
|
||||
|
||||
// MUST HOLD LOCK
|
||||
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
|
||||
{
|
||||
size_t sampleCount = data->frames;
|
||||
|
||||
int channelNumber = 0;
|
||||
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
|
||||
float *samples = (float*)data->data[planeNumber];
|
||||
if (!samples)
|
||||
continue;
|
||||
|
||||
float sum = 0.0;
|
||||
for (size_t i = 0; i < sampleCount; i++) {
|
||||
float sample = samples[i];
|
||||
sum += sample * sample;
|
||||
}
|
||||
|
||||
_magnitude[channelNumber] = std::sqrt(sum / sampleCount);
|
||||
|
||||
channelNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::unique_lock<std::mutex> l(c->_mutex);
|
||||
|
||||
c->_muted = muted;
|
||||
c->ProcessAudioChannels(data);
|
||||
c->ProcessPeak(data);
|
||||
c->ProcessMagnitude(data);
|
||||
|
||||
c->_lastUpdate = os_gettime_ns();
|
||||
}
|
||||
|
||||
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
|
||||
{
|
||||
auto c = static_cast<Meter*>(priv_data);
|
||||
|
||||
c->_volume = (float)calldata_float(cd, "volume");
|
||||
}
|
||||
|
||||
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) :
|
||||
_updateCallback(cb),
|
||||
_updatePeriod(updatePeriod),
|
||||
_running(false)
|
||||
{
|
||||
signal_handler_t *sh = obs_get_signal_handler();
|
||||
if (!sh)
|
||||
return;
|
||||
|
||||
auto enumProc = [](void *priv_data, obs_source_t *input) {
|
||||
auto c = static_cast<Handler*>(priv_data);
|
||||
|
||||
if (!obs_source_active(input))
|
||||
return true;
|
||||
|
||||
uint32_t flags = obs_source_get_output_flags(input);
|
||||
if ((flags & OBS_SOURCE_AUDIO) == 0)
|
||||
return true;
|
||||
|
||||
c->_meters.emplace_back(std::move(new Meter(input)));
|
||||
|
||||
return true;
|
||||
};
|
||||
obs_enum_sources(enumProc, this);
|
||||
|
||||
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
|
||||
signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
||||
|
||||
_running = true;
|
||||
_updateThread = std::thread(&Handler::UpdateThread, this);
|
||||
|
||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
|
||||
}
|
||||
|
||||
Utils::Obs::VolumeMeter::Handler::~Handler()
|
||||
{
|
||||
signal_handler_t *sh = obs_get_signal_handler();
|
||||
if (!sh)
|
||||
return;
|
||||
|
||||
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
|
||||
signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
|
||||
|
||||
if (_running) {
|
||||
_running = false;
|
||||
_cond.notify_all();
|
||||
}
|
||||
|
||||
if (_updateThread.joinable())
|
||||
_updateThread.join();
|
||||
|
||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
|
||||
}
|
||||
|
||||
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
|
||||
{
|
||||
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; }))
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<json> inputs;
|
||||
std::unique_lock<std::mutex> l(_meterMutex);
|
||||
for (auto &meter : _meters) {
|
||||
if (meter->InputValid())
|
||||
inputs.push_back(meter->GetMeterData());
|
||||
}
|
||||
l.unlock();
|
||||
|
||||
if (_updateCallback)
|
||||
_updateCallback(inputs);
|
||||
}
|
||||
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
|
||||
}
|
||||
|
||||
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
|
||||
{
|
||||
auto c = static_cast<Handler*>(priv_data);
|
||||
|
||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||
if (!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)
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
auto c = static_cast<Handler*>(priv_data);
|
||||
|
||||
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
|
||||
if (!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);
|
||||
std::vector<MeterPtr>::iterator iter;
|
||||
for (iter = c->_meters.begin(); iter != c->_meters.end();) {
|
||||
if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input))
|
||||
iter = c->_meters.erase(iter);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
@ -1,99 +1,99 @@
|
||||
/*
|
||||
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/>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <obs.hpp>
|
||||
|
||||
#include "Obs.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace Utils {
|
||||
namespace Obs {
|
||||
namespace VolumeMeter {
|
||||
// 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
|
||||
class Meter {
|
||||
public:
|
||||
Meter(obs_source_t *input);
|
||||
~Meter();
|
||||
|
||||
bool InputValid();
|
||||
obs_weak_source_t *GetWeakInput() { return _input; }
|
||||
json GetMeterData();
|
||||
|
||||
std::atomic<enum obs_peak_meter_type> PeakMeterType;
|
||||
|
||||
private:
|
||||
OBSWeakSourceAutoRelease _input;
|
||||
|
||||
// All values in mul
|
||||
std::mutex _mutex;
|
||||
bool _muted;
|
||||
int _channels;
|
||||
float _magnitude[MAX_AUDIO_CHANNELS];
|
||||
float _peak[MAX_AUDIO_CHANNELS];
|
||||
float _previousSamples[MAX_AUDIO_CHANNELS][4];
|
||||
|
||||
std::atomic<uint64_t> _lastUpdate;
|
||||
std::atomic<float> _volume;
|
||||
|
||||
void ResetAudioLevels();
|
||||
void ProcessAudioChannels(const struct audio_data *data);
|
||||
void ProcessPeak(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 InputVolumeCallback(void *priv_data, calldata_t *cd);
|
||||
};
|
||||
|
||||
// Maintains an array of active inputs
|
||||
class Handler {
|
||||
typedef std::function<void(std::vector<json>)> UpdateCallback;
|
||||
typedef std::unique_ptr<Meter> MeterPtr;
|
||||
|
||||
public:
|
||||
Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
|
||||
~Handler();
|
||||
|
||||
private:
|
||||
UpdateCallback _updateCallback;
|
||||
|
||||
std::mutex _meterMutex;
|
||||
std::vector<MeterPtr> _meters;
|
||||
uint64_t _updatePeriod;
|
||||
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _cond;
|
||||
std::atomic<bool> _running;
|
||||
std::thread _updateThread;
|
||||
|
||||
void UpdateThread();
|
||||
static void InputActivateCallback(void *priv_data, calldata_t *cd);
|
||||
static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
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/>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <obs.hpp>
|
||||
|
||||
#include "Obs.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace Utils {
|
||||
namespace Obs {
|
||||
namespace VolumeMeter {
|
||||
// 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
|
||||
class Meter {
|
||||
public:
|
||||
Meter(obs_source_t *input);
|
||||
~Meter();
|
||||
|
||||
bool InputValid();
|
||||
obs_weak_source_t *GetWeakInput() { return _input; }
|
||||
json GetMeterData();
|
||||
|
||||
std::atomic<enum obs_peak_meter_type> PeakMeterType;
|
||||
|
||||
private:
|
||||
OBSWeakSourceAutoRelease _input;
|
||||
|
||||
// All values in mul
|
||||
std::mutex _mutex;
|
||||
bool _muted;
|
||||
int _channels;
|
||||
float _magnitude[MAX_AUDIO_CHANNELS];
|
||||
float _peak[MAX_AUDIO_CHANNELS];
|
||||
float _previousSamples[MAX_AUDIO_CHANNELS][4];
|
||||
|
||||
std::atomic<uint64_t> _lastUpdate;
|
||||
std::atomic<float> _volume;
|
||||
|
||||
void ResetAudioLevels();
|
||||
void ProcessAudioChannels(const struct audio_data *data);
|
||||
void ProcessPeak(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 InputVolumeCallback(void *priv_data, calldata_t *cd);
|
||||
};
|
||||
|
||||
// Maintains an array of active inputs
|
||||
class Handler {
|
||||
typedef std::function<void(std::vector<json>)> UpdateCallback;
|
||||
typedef std::unique_ptr<Meter> MeterPtr;
|
||||
|
||||
public:
|
||||
Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
|
||||
~Handler();
|
||||
|
||||
private:
|
||||
UpdateCallback _updateCallback;
|
||||
|
||||
std::mutex _meterMutex;
|
||||
std::vector<MeterPtr> _meters;
|
||||
uint64_t _updatePeriod;
|
||||
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _cond;
|
||||
std::atomic<bool> _running;
|
||||
std::thread _updateThread;
|
||||
|
||||
void UpdateThread();
|
||||
static void InputActivateCallback(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]); \
|
||||
} 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;
|
||||
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;
|
||||
}
|
||||
|
||||
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 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) {
|
||||
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);
|
||||
delete notification;
|
||||
}, (void*)notification, false);
|
||||
|
@ -23,6 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
#include <QObject>
|
||||
#include <QThreadPool>
|
||||
#include <QString>
|
||||
#include <asio.hpp>
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
|
||||
|
Reference in New Issue
Block a user