Compare commits

...

50 Commits

Author SHA1 Message Date
3362d3f998 ci(macos): bump Packages version 2022-02-13 23:51:39 +01:00
7ca8140a34 ci(macos): use a common password for keychain import steps 2022-02-13 23:47:32 +01:00
eeb7bac4b7 ci(macos): import installer certificate in existing keychain 2022-02-01 10:19:36 +01:00
7113055218 ci(macos): configure productsign with installer certificate 2022-02-01 10:12:42 +01:00
1844f85e1f CI: Add MacOS CI
Add MacOS CI for `master`

Co-authored-by: tt2468 <tt2468@gmail.com>
2022-01-18 19:26:42 -08:00
bc0b499944 docs(ci): Update generated docs - ae906bb [skip ci] 2022-01-19 03:24:00 +00:00
ae906bb283 RequestHandler: Add VirtualCam requests 2022-01-18 19:23:06 -08:00
63dfed1cf9 docs(ci): Update generated docs - 873eade [skip ci] 2022-01-09 06:14:13 +00:00
873eadec05 requesthandler: Fix documentation of dB value input
Max dB value is 26dB, not -26dB.
2022-01-08 22:13:53 -08:00
dea0fcd561 Base: Add logging for compile time ASIO version 2022-01-07 23:00:48 -08:00
9f7beb1c0d deps: Downgrade asio to 1.12.1
Even though we statically link ASIO, it has issues with the
kqueue_reactor() on macos segfaulting when other plugins using
different ASIO versions are installed. So that means we're stuck using
1.12.1 until we can find some kind of fix for the crash issue.
2022-01-04 00:42:09 -08:00
db9f4b24df docs(ci): Update generated docs - 6035294 [skip ci] 2022-01-03 21:54:54 +00:00
6035294339 requesthandler: Add GetSourceFilter 2022-01-03 13:54:27 -08:00
6a2d5968ad requesthandler: Add private source settings get/set requests
It was requested via Discord to be able to modify the private settings
of any private source, since that functionality is used by some client
software to store stateful data. As private settings are in territory
that no normal user should ever tread into, these requests will be left
undocumented.
2022-01-01 17:43:26 -08:00
8f2d266dec docs(ci): Update generated docs - fe64620 [skip ci] 2022-01-01 02:06:35 +00:00
fe64620731 requesthandler: Add scene item blend mode requests 2021-12-31 18:05:05 -08:00
24e43d0276 requesthandler: Add GetSpecialInputs 2021-12-31 16:49:18 -08:00
c0308d6ce1 docs(ci): Update generated docs - 506a916 [skip ci] 2021-12-31 23:27:15 +00:00
506a9167c3 requesthandler: Add SetInputAudioTracks 2021-12-31 15:26:54 -08:00
043444cad5 workflows: Enable plugin tests on nightly linux builds 2021-12-31 15:26:54 -08:00
35c8a87def requesthandler: Profile requests if PLUGIN_TESTS is enabled 2021-12-31 15:26:54 -08:00
e451a8d6b0 requesthandler: Use unordered_map for request table
Shaves like 0.0005ms off of request time, but still worth noting.
2021-12-31 15:26:54 -08:00
02bcc0ac1b docs(ci): Update generated docs - 702f88c [skip ci] 2021-12-31 22:08:43 +00:00
702f88cea8 requesthandler: Add GetInputAudioTracks 2021-12-31 14:08:22 -08:00
6d216e0412 docs: Fix docs of InputAudioTracksChanged 2021-12-31 14:08:22 -08:00
e6761cf286 docs(ci): Update generated docs - 00dd8d7 [skip ci] 2021-12-30 09:19:59 +00:00
00dd8d7821 lib: Add version define 2021-12-30 01:15:54 -08:00
e43ebde794 Base: Use static_cast in place of reinterpret_cast
static_cast is a much safer cast method
2021-12-30 00:21:29 -08:00
4a2654d095 RequestHandler: Add GetGroupList 2021-12-30 00:12:41 -08:00
6291cb1532 docs(ci): Update generated docs - a90dafb [skip ci] 2021-12-30 05:12:42 +00:00
a90dafb971 Merge pull request #885 from obsproject/feature/input-audio-requests-events
Input audio requests and events
2021-12-29 21:12:23 -08:00
3a96b585ce docs(ci): Update generated docs - 12c6527 [skip ci] 2021-12-30 05:09:00 +00:00
12c6527442 Merge pull request #884 from obsproject/feature/ui-dialog-requests
RequestHandler: Add input open dialog requests
2021-12-29 21:08:41 -08:00
31997db509 EventHandler: Uncomment audio_monitoring signal 2021-12-29 21:05:28 -08:00
85f65952bd Base: Update issue template 2021-12-29 21:04:54 -08:00
9113ff9021 RequestHandler: Add audio balance requests 2021-12-29 21:03:16 -08:00
1ed095de48 EventHandler: Add InputAudioBalanceChanged 2021-12-29 21:03:16 -08:00
a94ac24027 RequestHandler: Add input open dialog requests
Adds
- `OpenInputPropertiesDialog`
- `OpenInputFiltersDialog`
- `OpenInputInteractDialog`
2021-12-29 21:00:11 -08:00
903b7d4171 Merge pull request #860 from obsproject/fix/audio_monitoring_check
Requests: Add support check for monitoring in `SetInputAudioMonitoringType`
2021-12-29 20:59:38 -08:00
a59ce69ba1 Merge pull request #857 from obsproject/fix/remove-old-ifdefs
Base: Remove old ifdefs
2021-12-29 20:54:24 -08:00
195c4a3ca9 Merge pull request #873 from obsproject/fix/inputvolumemeters-check
ObsVolumeMeter: Reenable check for valid input
2021-12-29 20:54:00 -08:00
3b2369ae97 Requests: Add support check for SetInputAudioMonitorType 2021-12-29 20:50:27 -08:00
af634b63fd Merge pull request #854 from obsproject/request/removeinput
Requests: Enable RemoveInput
2021-12-29 20:49:09 -08:00
444685c89d Utils: Reenable check for valid input in volumemeter 2021-12-29 20:45:20 -08:00
05aba45809 Base: Remove old ifdefs
It was a very cool method to save our precious std::strtoll method,
but will no longer be needed on the next OBS release.
2021-12-29 20:40:49 -08:00
1f1a8926b1 Requests: Enable RemoveInput 2021-12-29 20:40:14 -08:00
0c5d4ba3fb Merge pull request #852 from obsproject/event/changing_events
Events: Reenable *Changing events (second time)
2021-12-29 20:39:33 -08:00
947450ce4e Revert "Revert "Events: Re-enable *Changing events""
This reverts commit c60d09246c.
2021-12-29 20:29:45 -08:00
63cc1f316d Merge pull request #850 from obsproject/request/getrecorddirectory
Requests: Enable GetRecordDirectory
2021-12-29 20:28:44 -08:00
38157579a6 Requests: Enable GetRecordDirectory 2021-12-29 19:53:10 -08:00
46 changed files with 2349 additions and 811 deletions

View File

@ -69,8 +69,8 @@ body:
label: obs-websocket Version label: obs-websocket Version
description: What version of obs-websocket are you using? description: What version of obs-websocket are you using?
options: options:
- 5.0.0-alpha3
- 5.0.0-alpha2 - 5.0.0-alpha2
- 5.0.0-alpha1
- 4.9.1 - 4.9.1
- 4.9.0 - 4.9.0
- Git - Git

View File

@ -1,11 +1,11 @@
name: "CI Multiplatform Build" name: 'CI Multiplatform Build'
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
branches: branches:
- master - '*'
tags: tags:
- '[45].[0-9]+.[0-9]+*' - '[45].[0-9]+.[0-9]+*'
pull_request: pull_request:
@ -25,8 +25,8 @@ jobs:
QT_VERSION: '5.15.2' QT_VERSION: '5.15.2'
WINDOWS_DEPS_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset WINDOWS_DEPS_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset
WINDOWS_DEPS_VERSION: '2019' WINDOWS_DEPS_VERSION: '2019'
CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_GENERATOR: 'Visual Studio 16 2019'
CMAKE_SYSTEM_VERSION: "10.0" CMAKE_SYSTEM_VERSION: '10.0'
steps: steps:
- name: 'Add msbuild to PATH' - name: 'Add msbuild to PATH'
uses: microsoft/setup-msbuild@v1.0.2 uses: microsoft/setup-msbuild@v1.0.2
@ -35,20 +35,20 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-websocket path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive' submodules: 'recursive'
- name: 'Checkout OBS-Studio' - name: 'Checkout OBS Studio'
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio path: ${{ github.workspace }}/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: 'Get OBS-Studio Git Info' - name: 'Get OBS Studio Git Info'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
git fetch --prune --unshallow git fetch --prune --unshallow
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' - name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
@ -78,8 +78,6 @@ jobs:
with: with:
path: Qt_${{ env.QT_VERSION }}.7z path: Qt_${{ env.QT_VERSION }}.7z
key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}' key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}'
restore-keys: |
qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}
- name: 'Download Prerequisite: Qt' - name: 'Download Prerequisite: Qt'
if: steps.qtcache.outputs.cache-hit != 'true' if: steps.qtcache.outputs.cache-hit != 'true'
run: | run: |
@ -87,20 +85,18 @@ jobs:
- name: 'Extract Prerequisite: Qt' - name: 'Extract Prerequisite: Qt'
run: | run: |
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT" 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
- name: 'Restore Cached OBS-Studio Dependencies' - name: 'Restore Cached OBS Studio Dependencies'
id: obscache id: obscache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ${{ github.workspace }}\cmbuild\deps\** path: ${{ github.workspace }}\cmbuild\deps\**
key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}' key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
restore-keys: | - name: 'Install Prerequisite: Pre-built OBS Studio dependencies'
obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}
- name: 'Install Prerequisite: Pre-built OBS-Studio dependencies'
if: steps.obscache.outputs.cache-hit != 'true' if: steps.obscache.outputs.cache-hit != 'true'
run: | run: |
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps" 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
- name: 'Restore OBS-Studio 32-bit Build v${{ env.OBS_GIT_TAG }} from Cache' - name: 'Restore OBS Studio 32-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
id: build-cache-obs-32 id: build-cache-obs-32
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
@ -108,22 +104,20 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-studio/build32 path: ${{ github.workspace }}/obs-studio/build32
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: | - name: 'Configure OBS Studio 32-bit'
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS-Studio 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true' if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\build32} if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\build32}
cd .\build32 cd .\build32
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF .. cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
- name: 'Build OBS-Studio 32-bit' - name: 'Build OBS Studio 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true' if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
- name: 'Restore OBS-Studio 64-bit Build v${{ env.OBS_GIT_TAG }} from Cache' - name: 'Restore OBS Studio 64-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
id: build-cache-obs-64 id: build-cache-obs-64
uses: actions/cache@v1 uses: actions/cache@v1
env: env:
@ -131,16 +125,14 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-studio/build64 path: ${{ github.workspace }}/obs-studio/build64
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: | - name: 'Configure OBS Studio 64-bit'
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS-Studio 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true' if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\build64} if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\build64}
cd .\build64 cd .\build64
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF .. cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
- name: 'Build OBS-Studio 64-bit' - name: 'Build OBS Studio 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true' if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
@ -188,7 +180,7 @@ jobs:
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer' name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer'
path: ${{ github.workspace }}/obs-websocket/package/*.exe path: ${{ github.workspace }}/obs-websocket/package/*.exe
ubuntu64: ubuntu64:
name: "Linux/Ubuntu 64-bit" name: 'Linux/Ubuntu 64-bit'
runs-on: [ubuntu-latest] runs-on: [ubuntu-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true if: contains(github.event.head_commit.message, '[skip ci]') != true
steps: steps:
@ -197,20 +189,20 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-websocket path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive' submodules: 'recursive'
- name: 'Checkout OBS-Studio' - name: 'Checkout OBS Studio'
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio path: ${{ github.workspace }}/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: 'Get OBS-Studio Git Info' - name: 'Get OBS Studio Git Info'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
git fetch --prune --unshallow git fetch --prune --unshallow
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' - name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
@ -284,21 +276,21 @@ jobs:
libx11-xcb-dev \ libx11-xcb-dev \
libxcb1-dev \ libxcb1-dev \
libxss-dev \ libxss-dev \
- name: 'Configure OBS-Studio' - name: 'Configure OBS Studio'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
shell: bash shell: bash
run: | run: |
mkdir ./build mkdir ./build
cd ./build cd ./build
cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build OBS-Studio' - name: 'Build OBS Studio'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
shell: bash shell: bash
run: | run: |
set -e set -e
cd ./build cd ./build
make -j4 libobs obs-frontend-api make -j4 libobs obs-frontend-api
- name: 'Install OBS-Studio' - name: 'Install OBS Studio'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
shell: bash shell: bash
run: | run: |
@ -316,7 +308,7 @@ jobs:
if [ "${{ env.GIT_TAG }}" ] ; then \ if [ "${{ env.GIT_TAG }}" ] ; then \
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DCMAKE_BUILD_TYPE=Release .. ; \ cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DCMAKE_BUILD_TYPE=Release .. ; \
else \ else \
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" .. ; \ cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DPLUGIN_TESTS=TRUE .. ; \
fi fi
- name: 'Build obs-websocket' - name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket working-directory: ${{ github.workspace }}/obs-websocket
@ -355,3 +347,210 @@ jobs:
with: with:
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64' name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64'
path: '${{ github.workspace }}/obs-websocket/package/*.deb' path: '${{ github.workspace }}/obs-websocket/package/*.deb'
macOS:
name: 'macOS 64-bit'
runs-on: [macos-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true
env:
MACOS_DEPS_VERSION: '2022-01-01'
MACOS_DEPS_CACHE_VERSION: '2' # Change whenever updating dependencies version, in order to force a cache reset
steps:
- name: 'Checkout obs-websocket'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS Studio'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Install Prerequisite: Binary Signing Certificate'
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'

View File

@ -1,5 +0,0 @@
brew "jack"
brew "speexdsp"
brew "cmake"
brew "freetype"
brew "fdk-aac"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -514,7 +514,7 @@
<key>CONCLUSION_ACTION</key> <key>CONCLUSION_ACTION</key>
<integer>0</integer> <integer>0</integer>
<key>IDENTIFIER</key> <key>IDENTIFIER</key>
<string>fr.palakis.obs-websocket</string> <string>com.obsproject.obs-websocket</string>
<key>OVERWRITE_PERMISSIONS</key> <key>OVERWRITE_PERMISSIONS</key>
<false/> <false/>
<key>VERSION</key> <key>VERSION</key>

View File

@ -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

View File

@ -114,6 +114,7 @@ set(obs-websocket_SOURCES
src/requesthandler/RequestHandler_Transitions.cpp src/requesthandler/RequestHandler_Transitions.cpp
src/requesthandler/RequestHandler_Filters.cpp src/requesthandler/RequestHandler_Filters.cpp
src/requesthandler/RequestHandler_SceneItems.cpp src/requesthandler/RequestHandler_SceneItems.cpp
src/requesthandler/RequestHandler_Outputs.cpp
src/requesthandler/RequestHandler_Stream.cpp src/requesthandler/RequestHandler_Stream.cpp
src/requesthandler/RequestHandler_Record.cpp src/requesthandler/RequestHandler_Record.cpp
src/requesthandler/RequestHandler_MediaInputs.cpp src/requesthandler/RequestHandler_MediaInputs.cpp

2
deps/asio vendored

Submodule deps/asio updated: 08a7029cb1...b73dc1d2c0

View File

@ -1129,6 +1129,55 @@
], ],
"responseFields": [] "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.", "description": "Gets data about the current plugin and RPC version.",
"requestType": "GetVersion", "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.", "description": "Creates a new input, adding it as a scene item to the specified scene.",
"requestType": "CreateInput", "requestType": "CreateInput",
@ -1811,13 +1902,67 @@
"valueName": "inputVolumeDb", "valueName": "inputVolumeDb",
"valueType": "Number", "valueType": "Number",
"valueDescription": "Volume setting in dB", "valueDescription": "Volume setting in dB",
"valueRestrictions": ">= -100, <= -26", "valueRestrictions": ">= -100, <= 26",
"valueOptional": true, "valueOptional": true,
"valueOptionalBehavior": "`inputVolumeMul` should be specified" "valueOptionalBehavior": "`inputVolumeMul` should be specified"
} }
], ],
"responseFields": [] "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!", "description": "Gets the audio sync offset of an input.\n\nNote: The audio sync offset can be negative too!",
"requestType": "GetInputAudioSyncOffset", "requestType": "GetInputAudioSyncOffset",
@ -1926,6 +2071,60 @@
], ],
"responseFields": [] "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.", "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", "requestType": "GetInputPropertiesListPropertyItems",
@ -2108,6 +2307,62 @@
], ],
"responseFields": [] "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.", "description": "Gets the status of the record output.",
"requestType": "GetRecordStatus", "requestType": "GetRecordStatus",
@ -2706,6 +2961,76 @@
], ],
"responseFields": [] "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.", "description": "Gets an array of all scenes in OBS.",
"requestType": "GetSceneList", "requestType": "GetSceneList",
@ -2729,7 +3054,24 @@
{ {
"valueName": "scenes", "valueName": "scenes",
"valueType": "Array<Object>", "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": [] "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": [ "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.", "description": "The sync offset of an input has changed.",
"eventType": "InputAudioSyncOffsetChanged", "eventType": "InputAudioSyncOffsetChanged",
@ -3641,8 +4065,8 @@
}, },
{ {
"valueName": "inputAudioTracks", "valueName": "inputAudioTracks",
"valueType": "Array<Boolean>", "valueType": "Object",
"valueDescription": "Array of audio tracks along with their associated enable states" "valueDescription": "Object of audio tracks along with their associated enable states"
} }
] ]
}, },

View File

@ -1263,6 +1263,7 @@ Subscription value to receive the `SceneItemTransformChanged` high-volume event.
- [InputShowStateChanged](#inputshowstatechanged) - [InputShowStateChanged](#inputshowstatechanged)
- [InputMuteStateChanged](#inputmutestatechanged) - [InputMuteStateChanged](#inputmutestatechanged)
- [InputVolumeChanged](#inputvolumechanged) - [InputVolumeChanged](#inputvolumechanged)
- [InputAudioBalanceChanged](#inputaudiobalancechanged)
- [InputAudioSyncOffsetChanged](#inputaudiosyncoffsetchanged) - [InputAudioSyncOffsetChanged](#inputaudiosyncoffsetchanged)
- [InputAudioTracksChanged](#inputaudiotrackschanged) - [InputAudioTracksChanged](#inputaudiotrackschanged)
- [InputAudioMonitorTypeChanged](#inputaudiomonitortypechanged) - [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 ### InputAudioSyncOffsetChanged
The sync offset of an input has changed. The sync offset of an input has changed.
@ -1698,7 +1717,7 @@ The audio tracks of an input have changed.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ----------- | | ---- | :---: | ----------- |
| inputName | String | Name of the input | | inputName | String | Name of the input |
| inputAudioTracks | Array&lt;Boolean&gt; | 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) - [SaveSourceScreenshot](#savesourcescreenshot)
- [Scenes](#scenes-1) - [Scenes](#scenes-1)
- [GetSceneList](#getscenelist) - [GetSceneList](#getscenelist)
- [GetGroupList](#getgrouplist)
- [GetCurrentProgramScene](#getcurrentprogramscene) - [GetCurrentProgramScene](#getcurrentprogramscene)
- [SetCurrentProgramScene](#setcurrentprogramscene) - [SetCurrentProgramScene](#setcurrentprogramscene)
- [GetCurrentPreviewScene](#getcurrentpreviewscene) - [GetCurrentPreviewScene](#getcurrentpreviewscene)
@ -2055,6 +2075,7 @@ Studio mode has been enabled or disabled.
- [Inputs](#inputs-1) - [Inputs](#inputs-1)
- [GetInputList](#getinputlist) - [GetInputList](#getinputlist)
- [GetInputKindList](#getinputkindlist) - [GetInputKindList](#getinputkindlist)
- [GetSpecialInputs](#getspecialinputs)
- [CreateInput](#createinput) - [CreateInput](#createinput)
- [RemoveInput](#removeinput) - [RemoveInput](#removeinput)
- [SetInputName](#setinputname) - [SetInputName](#setinputname)
@ -2066,10 +2087,14 @@ Studio mode has been enabled or disabled.
- [ToggleInputMute](#toggleinputmute) - [ToggleInputMute](#toggleinputmute)
- [GetInputVolume](#getinputvolume) - [GetInputVolume](#getinputvolume)
- [SetInputVolume](#setinputvolume) - [SetInputVolume](#setinputvolume)
- [GetInputAudioBalance](#getinputaudiobalance)
- [SetInputAudioBalance](#setinputaudiobalance)
- [GetInputAudioSyncOffset](#getinputaudiosyncoffset) - [GetInputAudioSyncOffset](#getinputaudiosyncoffset)
- [SetInputAudioSyncOffset](#setinputaudiosyncoffset) - [SetInputAudioSyncOffset](#setinputaudiosyncoffset)
- [GetInputAudioMonitorType](#getinputaudiomonitortype) - [GetInputAudioMonitorType](#getinputaudiomonitortype)
- [SetInputAudioMonitorType](#setinputaudiomonitortype) - [SetInputAudioMonitorType](#setinputaudiomonitortype)
- [GetInputAudioTracks](#getinputaudiotracks)
- [SetInputAudioTracks](#setinputaudiotracks)
- [GetInputPropertiesListPropertyItems](#getinputpropertieslistpropertyitems) - [GetInputPropertiesListPropertyItems](#getinputpropertieslistpropertyitems)
- [PressInputPropertiesButton](#pressinputpropertiesbutton) - [PressInputPropertiesButton](#pressinputpropertiesbutton)
- [Transitions](#transitions) - [Transitions](#transitions)
@ -2080,6 +2105,8 @@ Studio mode has been enabled or disabled.
- [SetCurrentSceneTransitionDuration](#setcurrentscenetransitionduration) - [SetCurrentSceneTransitionDuration](#setcurrentscenetransitionduration)
- [SetCurrentSceneTransitionSettings](#setcurrentscenetransitionsettings) - [SetCurrentSceneTransitionSettings](#setcurrentscenetransitionsettings)
- [TriggerStudioModeTransition](#triggerstudiomodetransition) - [TriggerStudioModeTransition](#triggerstudiomodetransition)
- [Filters](#filters)
- [GetSourceFilter](#getsourcefilter)
- [Scene Items](#scene-items-1) - [Scene Items](#scene-items-1)
- [GetSceneItemList](#getsceneitemlist) - [GetSceneItemList](#getsceneitemlist)
- [GetGroupItemList](#getgroupitemlist) - [GetGroupItemList](#getgroupitemlist)
@ -2095,6 +2122,13 @@ Studio mode has been enabled or disabled.
- [SetSceneItemLocked](#setsceneitemlocked) - [SetSceneItemLocked](#setsceneitemlocked)
- [GetSceneItemIndex](#getsceneitemindex) - [GetSceneItemIndex](#getsceneitemindex)
- [SetSceneItemIndex](#setsceneitemindex) - [SetSceneItemIndex](#setsceneitemindex)
- [GetSceneItemBlendMode](#getsceneitemblendmode)
- [SetSceneItemBlendMode](#setsceneitemblendmode)
- [Outputs](#outputs-1)
- [GetVirtualCamStatus](#getvirtualcamstatus)
- [ToggleVirtualCam](#togglevirtualcam)
- [StartVirtualCam](#startvirtualcam)
- [StopVirtualCam](#stopvirtualcam)
- [Stream](#stream) - [Stream](#stream)
- [GetStreamStatus](#getstreamstatus) - [GetStreamStatus](#getstreamstatus)
- [ToggleStream](#togglestream) - [ToggleStream](#togglestream)
@ -2117,6 +2151,9 @@ Studio mode has been enabled or disabled.
- [Ui](#ui-1) - [Ui](#ui-1)
- [GetStudioModeEnabled](#getstudiomodeenabled) - [GetStudioModeEnabled](#getstudiomodeenabled)
- [SetStudioModeEnabled](#setstudiomodeenabled) - [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 | | currentProgramSceneName | String | Current program scene |
| currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode | | currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode |
| scenes | Array&lt;Object&gt; | Array of scenes in OBS | | scenes | Array&lt;Object&gt; | 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&lt;String&gt; | 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 ### CreateInput
Creates a new input, adding it as a scene item to the specified scene. Creates a new input, adding it as a scene item to the specified scene.
@ -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 | | 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 | | ?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 ### GetInputPropertiesListPropertyItems
Gets the items of a list property from an input's properties. 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 - 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 ## Scene Items
### GetSceneItemList ### GetSceneItemList
@ -3736,6 +3927,119 @@ Scenes and Groups
| sceneItemId | Number | Numeric ID of the scene item | >= 0 | N/A | | sceneItemId | Number | Numeric ID of the scene item | >= 0 | N/A |
| sceneItemIndex | Number | New index position 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 ## Stream
@ -4026,4 +4330,55 @@ Enables or disables studio mode
| ---- | :---: | ----------- | :----------------: | ----------------- | | ---- | :---: | ----------- | :----------------: | ----------------- |
| studioModeEnabled | Boolean | True == Enabled, False == Disabled | None | N/A | | 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 |

View File

@ -22,6 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs.h> #include <obs.h>
#define OBS_WEBSOCKET_API_VERSION 1
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@ -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, "hide", HandleInputShowStateChanged, this);
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this); signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this); signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_connect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
//signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
if (sourceType == OBS_SOURCE_TYPE_INPUT) { if (sourceType == OBS_SOURCE_TYPE_INPUT) {
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this); 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, "hide", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this); signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this); signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
//signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this); signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this); signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this); signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
@ -197,7 +199,7 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data) void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(private_data); auto eventHandler = static_cast<EventHandler*>(private_data);
if (!eventHandler->_obsLoaded.load()) { if (!eventHandler->_obsLoaded.load()) {
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
@ -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()` // In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()`
// Enumerate inputs and connect each one // Enumerate inputs and connect each one
obs_enum_sources([](void* param, obs_source_t* source) { obs_enum_sources([](void* param, obs_source_t* source) {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(source); eventHandler->ConnectSourceSignals(source);
return true; return true;
}, private_data); }, private_data);
// Enumerate scenes and connect each one // Enumerate scenes and connect each one
obs_enum_scenes([](void* param, obs_source_t* source) { obs_enum_scenes([](void* param, obs_source_t* source) {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(source); eventHandler->ConnectSourceSignals(source);
return true; return true;
}, private_data); }, 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()` // In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()`
// Enumerate inputs and disconnect each one // Enumerate inputs and disconnect each one
obs_enum_sources([](void* param, obs_source_t* source) { obs_enum_sources([](void* param, obs_source_t* source) {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source); eventHandler->DisconnectSourceSignals(source);
return true; return true;
}, private_data); }, private_data);
// Enumerate scenes and disconnect each one // Enumerate scenes and disconnect each one
obs_enum_scenes([](void* param, obs_source_t* source) { obs_enum_scenes([](void* param, obs_source_t* source) {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source); eventHandler->DisconnectSourceSignals(source);
return true; return true;
}, private_data); }, private_data);
@ -264,18 +266,18 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
break; break;
// Config // Config
//case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
// eventHandler->HandleCurrentSceneCollectionChanging(); eventHandler->HandleCurrentSceneCollectionChanging();
// break; break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
eventHandler->HandleCurrentSceneCollectionChanged(); eventHandler->HandleCurrentSceneCollectionChanged();
break; break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
eventHandler->HandleSceneCollectionListChanged(); eventHandler->HandleSceneCollectionListChanged();
break; break;
//case OBS_FRONTEND_EVENT_PROFILE_CHANGING: case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
// eventHandler->HandleCurrentProfileChanging(); eventHandler->HandleCurrentProfileChanging();
// break; break;
case OBS_FRONTEND_EVENT_PROFILE_CHANGED: case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
eventHandler->HandleCurrentProfileChanged(); eventHandler->HandleCurrentProfileChanged();
break; break;
@ -363,7 +365,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
// Only called for creation of a public source // Only called for creation of a public source
void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
// Don't react to signals until OBS has finished loading // Don't react to signals until OBS has finished loading
if (!eventHandler->_obsLoaded.load()) if (!eventHandler->_obsLoaded.load())
@ -390,7 +392,7 @@ void EventHandler::SourceCreatedMultiHandler(void *param, calldata_t *data)
// Only called for destruction of a public source // Only called for destruction of a public source
void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
// We can't use any smart types here because releasing the source will cause infinite recursion // We can't use any smart types here because releasing the source will cause infinite recursion
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
@ -418,7 +420,7 @@ void EventHandler::SourceDestroyedMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
if (!eventHandler->_obsLoaded.load()) if (!eventHandler->_obsLoaded.load())
return; return;
@ -441,7 +443,7 @@ void EventHandler::SourceRemovedMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data) void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
if (!eventHandler->_obsLoaded.load()) if (!eventHandler->_obsLoaded.load())
return; return;

View File

@ -107,6 +107,7 @@ class EventHandler
static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioBalanceChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback

View File

@ -111,7 +111,7 @@ void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputNa
*/ */
void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data) void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
if (!eventHandler->_inputActiveStateChangedRef.load()) if (!eventHandler->_inputActiveStateChangedRef.load())
return; return;
@ -147,7 +147,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data) void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
if (!eventHandler->_inputShowStateChangedRef.load()) if (!eventHandler->_inputShowStateChangedRef.load())
return; return;
@ -181,7 +181,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data) void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -213,7 +213,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data) void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -236,6 +236,39 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputVolumeChanged", eventData); eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputVolumeChanged", eventData);
} }
/**
* The audio balance value of an input has changed.
*
* @dataField inputName | String | Name of the affected input
* @dataField inputAudioBalance | Number | New audio balance value of the input
*
* @eventType InputAudioBalanceChanged
* @eventSubscription Inputs
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @category inputs
* @api events
*/
void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source)
return;
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT)
return;
float inputAudioBalance = (float)calldata_float(data, "balance");
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputAudioBalance"] = inputAudioBalance;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioBalanceChanged", eventData);
}
/** /**
* The sync offset of an input has changed. * The sync offset of an input has changed.
* *
@ -252,7 +285,7 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -272,8 +305,8 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
/** /**
* The audio tracks of an input have changed. * The audio tracks of an input have changed.
* *
* @dataField inputName | String | Name of the input * @dataField inputName | String | Name of the input
* @dataField inputAudioTracks | Array<Boolean> | Array of audio tracks along with their associated enable states * @dataField inputAudioTracks | Object | Object of audio tracks along with their associated enable states
* *
* @eventType InputAudioTracksChanged * @eventType InputAudioTracksChanged
* @eventSubscription Inputs * @eventSubscription Inputs
@ -285,7 +318,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
*/ */
void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -328,7 +361,7 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
*/ */
void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data) void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)

View File

@ -35,7 +35,7 @@ std::string GetMediaInputActionString(ObsMediaInputAction action) {
void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -49,7 +49,7 @@ void EventHandler::SourceMediaPauseMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -63,7 +63,7 @@ void EventHandler::SourceMediaPlayMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -77,7 +77,7 @@ void EventHandler::SourceMediaRestartMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -91,7 +91,7 @@ void EventHandler::SourceMediaStopMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -105,7 +105,7 @@ void EventHandler::SourceMediaNextMultiHandler(void *param, calldata_t *data)
void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data) void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -132,7 +132,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data
*/ */
void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data) void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)
@ -161,7 +161,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
*/ */
void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data) void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source"); obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source) if (!source)

View File

@ -37,7 +37,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data) void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -74,7 +74,7 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data) void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -107,7 +107,7 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data) void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -136,7 +136,7 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
*/ */
void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -172,7 +172,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
*/ */
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene"); obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene) if (!scene)
@ -208,7 +208,7 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
*/ */
void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto eventHandler = static_cast<EventHandler*>(param);
if (!eventHandler->_sceneItemTransformChangedRef.load()) if (!eventHandler->_sceneItemTransformChangedRef.load())
return; return;

View File

@ -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] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION, OBS_WEBSOCKET_RPC_VERSION);
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion()); blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
// Initialize the cpu stats // Initialize the cpu stats
_cpuUsageInfo = os_cpu_usage_info_start(); _cpuUsageInfo = os_cpu_usage_info_start();
@ -68,7 +69,7 @@ bool obs_module_load(void)
// Initialize the settings dialog // Initialize the settings dialog
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
_settingsDialog = new SettingsDialog(mainWindow); _settingsDialog = new SettingsDialog(mainWindow);
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();

View File

@ -21,13 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <memory> #include <memory>
#include <obs.hpp> #include <obs.hpp>
#ifdef _MSC_VER
#pragma push_macro("strtoll")
#endif
#include <util/platform.h> #include <util/platform.h>
#ifdef _MSC_VER
#pragma pop_macro("strtoll")
#endif
#include "utils/Obs.h" #include "utils/Obs.h"
#include "plugin-macros.generated.h" #include "plugin-macros.generated.h"

View File

@ -109,7 +109,7 @@ static void ObsTickCallback(void *param, float)
{ {
ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"}; ScopeProfiler prof{"obs_websocket_request_batch_frame_tick"};
auto serialFrameBatch = reinterpret_cast<SerialFrameBatch*>(param); auto serialFrameBatch = static_cast<SerialFrameBatch*>(param);
// Increment frame count // Increment frame count
serialFrameBatch->frameCount++; serialFrameBatch->frameCount++;

View File

@ -17,9 +17,13 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#ifdef PLUGIN_TESTS
#include <util/profiler.hpp>
#endif
#include "RequestHandler.h" #include "RequestHandler.h"
const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{ {
// General // General
{"GetVersion", &RequestHandler::GetVersion}, {"GetVersion", &RequestHandler::GetVersion},
@ -52,9 +56,12 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"GetSourceActive", &RequestHandler::GetSourceActive}, {"GetSourceActive", &RequestHandler::GetSourceActive},
{"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot}, {"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot},
{"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot}, {"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot},
{"GetSourcePrivateSettings", &RequestHandler::GetSourcePrivateSettings},
{"SetSourcePrivateSettings", &RequestHandler::SetSourcePrivateSettings},
// Scenes // Scenes
{"GetSceneList", &RequestHandler::GetSceneList}, {"GetSceneList", &RequestHandler::GetSceneList},
{"GetGroupList", &RequestHandler::GetGroupList},
{"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene}, {"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene},
{"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene}, {"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene},
{"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene}, {"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene},
@ -66,8 +73,9 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
// Inputs // Inputs
{"GetInputList", &RequestHandler::GetInputList}, {"GetInputList", &RequestHandler::GetInputList},
{"GetInputKindList", &RequestHandler::GetInputKindList}, {"GetInputKindList", &RequestHandler::GetInputKindList},
{"GetSpecialInputs", &RequestHandler::GetSpecialInputs},
{"CreateInput", &RequestHandler::CreateInput}, {"CreateInput", &RequestHandler::CreateInput},
//{"RemoveInput", &RequestHandler::RemoveInput}, // Disabled for now. Pending obs-studio#5276 {"RemoveInput", &RequestHandler::RemoveInput},
{"SetInputName", &RequestHandler::SetInputName}, {"SetInputName", &RequestHandler::SetInputName},
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings}, {"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
{"GetInputSettings", &RequestHandler::GetInputSettings}, {"GetInputSettings", &RequestHandler::GetInputSettings},
@ -77,10 +85,14 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"ToggleInputMute", &RequestHandler::ToggleInputMute}, {"ToggleInputMute", &RequestHandler::ToggleInputMute},
{"GetInputVolume", &RequestHandler::GetInputVolume}, {"GetInputVolume", &RequestHandler::GetInputVolume},
{"SetInputVolume", &RequestHandler::SetInputVolume}, {"SetInputVolume", &RequestHandler::SetInputVolume},
{"GetInputAudioBalance", &RequestHandler::GetInputAudioBalance},
{"SetInputAudioBalance", &RequestHandler::SetInputAudioBalance},
{"GetInputAudioSyncOffset", &RequestHandler::GetInputAudioSyncOffset}, {"GetInputAudioSyncOffset", &RequestHandler::GetInputAudioSyncOffset},
{"SetInputAudioSyncOffset", &RequestHandler::SetInputAudioSyncOffset}, {"SetInputAudioSyncOffset", &RequestHandler::SetInputAudioSyncOffset},
{"GetInputAudioMonitorType", &RequestHandler::GetInputAudioMonitorType}, {"GetInputAudioMonitorType", &RequestHandler::GetInputAudioMonitorType},
{"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType}, {"SetInputAudioMonitorType", &RequestHandler::SetInputAudioMonitorType},
{"GetInputAudioTracks", &RequestHandler::GetInputAudioTracks},
{"SetInputAudioTracks", &RequestHandler::SetInputAudioTracks},
{"GetInputPropertiesListPropertyItems", &RequestHandler::GetInputPropertiesListPropertyItems}, {"GetInputPropertiesListPropertyItems", &RequestHandler::GetInputPropertiesListPropertyItems},
{"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton}, {"PressInputPropertiesButton", &RequestHandler::PressInputPropertiesButton},
@ -93,6 +105,9 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings}, {"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition}, {"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
// Filters
{"GetSourceFilter", &RequestHandler::GetSourceFilter},
// Scene Items // Scene Items
{"GetSceneItemList", &RequestHandler::GetSceneItemList}, {"GetSceneItemList", &RequestHandler::GetSceneItemList},
{"GetGroupSceneItemList", &RequestHandler::GetGroupSceneItemList}, {"GetGroupSceneItemList", &RequestHandler::GetGroupSceneItemList},
@ -108,6 +123,14 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked}, {"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex}, {"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex}, {"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
{"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode},
{"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode},
// Outputs
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
{"ToggleVirtualCam", &RequestHandler::ToggleVirtualCam},
{"StartVirtualCam", &RequestHandler::StartVirtualCam},
{"StopVirtualCam", &RequestHandler::StopVirtualCam},
// Stream // Stream
{"GetStreamStatus", &RequestHandler::GetStreamStatus}, {"GetStreamStatus", &RequestHandler::GetStreamStatus},
@ -123,7 +146,7 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause}, {"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
{"PauseRecord", &RequestHandler::PauseRecord}, {"PauseRecord", &RequestHandler::PauseRecord},
{"ResumeRecord", &RequestHandler::ResumeRecord}, {"ResumeRecord", &RequestHandler::ResumeRecord},
//{"GetRecordDirectory", &RequestHandler::GetRecordDirectory}, {"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
// Media Inputs // Media Inputs
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus}, {"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},
@ -134,6 +157,9 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
// Ui // Ui
{"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled}, {"GetStudioModeEnabled", &RequestHandler::GetStudioModeEnabled},
{"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled}, {"SetStudioModeEnabled", &RequestHandler::SetStudioModeEnabled},
{"OpenInputPropertiesDialog", &RequestHandler::OpenInputPropertiesDialog},
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
}; };
RequestHandler::RequestHandler(SessionPtr session) : RequestHandler::RequestHandler(SessionPtr session) :
@ -143,6 +169,10 @@ RequestHandler::RequestHandler(SessionPtr session) :
RequestResult RequestHandler::ProcessRequest(const Request& request) RequestResult RequestHandler::ProcessRequest(const Request& request)
{ {
#ifdef PLUGIN_TESTS
ScopeProfiler prof{"obs_websocket_request_processing"};
#endif
if (!request.RequestData.is_object() && !request.RequestData.is_null()) if (!request.RequestData.is_object() && !request.RequestData.is_null())
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object."); return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");

View File

@ -19,7 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#pragma once #pragma once
#include <map> #include <unordered_map>
#include <obs.hpp> #include <obs.hpp>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
@ -74,9 +74,12 @@ class RequestHandler {
RequestResult GetSourceActive(const Request&); RequestResult GetSourceActive(const Request&);
RequestResult GetSourceScreenshot(const Request&); RequestResult GetSourceScreenshot(const Request&);
RequestResult SaveSourceScreenshot(const Request&); RequestResult SaveSourceScreenshot(const Request&);
RequestResult GetSourcePrivateSettings(const Request&);
RequestResult SetSourcePrivateSettings(const Request&);
// Scenes // Scenes
RequestResult GetSceneList(const Request&); RequestResult GetSceneList(const Request&);
RequestResult GetGroupList(const Request&);
RequestResult GetCurrentProgramScene(const Request&); RequestResult GetCurrentProgramScene(const Request&);
RequestResult SetCurrentProgramScene(const Request&); RequestResult SetCurrentProgramScene(const Request&);
RequestResult GetCurrentPreviewScene(const Request&); RequestResult GetCurrentPreviewScene(const Request&);
@ -88,6 +91,7 @@ class RequestHandler {
// Inputs // Inputs
RequestResult GetInputList(const Request&); RequestResult GetInputList(const Request&);
RequestResult GetInputKindList(const Request&); RequestResult GetInputKindList(const Request&);
RequestResult GetSpecialInputs(const Request&);
RequestResult CreateInput(const Request&); RequestResult CreateInput(const Request&);
RequestResult RemoveInput(const Request&); RequestResult RemoveInput(const Request&);
RequestResult SetInputName(const Request&); RequestResult SetInputName(const Request&);
@ -99,10 +103,14 @@ class RequestHandler {
RequestResult ToggleInputMute(const Request&); RequestResult ToggleInputMute(const Request&);
RequestResult GetInputVolume(const Request&); RequestResult GetInputVolume(const Request&);
RequestResult SetInputVolume(const Request&); RequestResult SetInputVolume(const Request&);
RequestResult GetInputAudioBalance(const Request&);
RequestResult SetInputAudioBalance(const Request&);
RequestResult GetInputAudioSyncOffset(const Request&); RequestResult GetInputAudioSyncOffset(const Request&);
RequestResult SetInputAudioSyncOffset(const Request&); RequestResult SetInputAudioSyncOffset(const Request&);
RequestResult GetInputAudioMonitorType(const Request&); RequestResult GetInputAudioMonitorType(const Request&);
RequestResult SetInputAudioMonitorType(const Request&); RequestResult SetInputAudioMonitorType(const Request&);
RequestResult GetInputAudioTracks(const Request&);
RequestResult SetInputAudioTracks(const Request&);
RequestResult GetInputPropertiesListPropertyItems(const Request&); RequestResult GetInputPropertiesListPropertyItems(const Request&);
RequestResult PressInputPropertiesButton(const Request&); RequestResult PressInputPropertiesButton(const Request&);
@ -115,6 +123,9 @@ class RequestHandler {
RequestResult SetCurrentSceneTransitionSettings(const Request&); RequestResult SetCurrentSceneTransitionSettings(const Request&);
RequestResult TriggerStudioModeTransition(const Request&); RequestResult TriggerStudioModeTransition(const Request&);
// Filters
RequestResult GetSourceFilter(const Request&);
// Scene Items // Scene Items
RequestResult GetSceneItemList(const Request&); RequestResult GetSceneItemList(const Request&);
RequestResult GetGroupSceneItemList(const Request&); RequestResult GetGroupSceneItemList(const Request&);
@ -130,6 +141,14 @@ class RequestHandler {
RequestResult SetSceneItemLocked(const Request&); RequestResult SetSceneItemLocked(const Request&);
RequestResult GetSceneItemIndex(const Request&); RequestResult GetSceneItemIndex(const Request&);
RequestResult SetSceneItemIndex(const Request&); RequestResult SetSceneItemIndex(const Request&);
RequestResult GetSceneItemBlendMode(const Request&);
RequestResult SetSceneItemBlendMode(const Request&);
// Outputs
RequestResult GetVirtualCamStatus(const Request&);
RequestResult ToggleVirtualCam(const Request&);
RequestResult StartVirtualCam(const Request&);
RequestResult StopVirtualCam(const Request&);
// Stream // Stream
RequestResult GetStreamStatus(const Request&); RequestResult GetStreamStatus(const Request&);
@ -156,7 +175,10 @@ class RequestHandler {
// Ui // Ui
RequestResult GetStudioModeEnabled(const Request&); RequestResult GetStudioModeEnabled(const Request&);
RequestResult SetStudioModeEnabled(const Request&); RequestResult SetStudioModeEnabled(const Request&);
RequestResult OpenInputPropertiesDialog(const Request&);
RequestResult OpenInputFiltersDialog(const Request&);
RequestResult OpenInputInteractDialog(const Request&);
SessionPtr _session; SessionPtr _session;
static const std::map<std::string, RequestMethodHandler> _handlerMap; static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
}; };

View File

@ -159,7 +159,7 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
// Avoid queueing tasks if nothing will change // Avoid queueing tasks if nothing will change
if (currentSceneCollectionName != sceneCollectionName) { if (currentSceneCollectionName != sceneCollectionName) {
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(OBS_TASK_UI, [](void* param) {
obs_frontend_set_current_scene_collection(reinterpret_cast<const char*>(param)); obs_frontend_set_current_scene_collection(static_cast<const char*>(param));
}, (void*)sceneCollectionName.c_str(), true); }, (void*)sceneCollectionName.c_str(), true);
} }
@ -193,7 +193,7 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end()) if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
return RequestResult::Error(RequestStatus::ResourceAlreadyExists); return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
bool success = false; bool success = false;
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName))); QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
if (!success) if (!success)
@ -252,7 +252,7 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
// Avoid queueing tasks if nothing will change // Avoid queueing tasks if nothing will change
if (currentProfileName != profileName) { if (currentProfileName != profileName) {
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(OBS_TASK_UI, [](void* param) {
obs_frontend_set_current_profile(reinterpret_cast<const char*>(param)); obs_frontend_set_current_profile(static_cast<const char*>(param));
}, (void*)profileName.c_str(), true); }, (void*)profileName.c_str(), true);
} }
@ -284,7 +284,7 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end()) if (std::find(profiles.begin(), profiles.end(), profileName) != profiles.end())
return RequestResult::Error(RequestStatus::ResourceAlreadyExists); return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName))); QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
return RequestResult::Success(); return RequestResult::Success();
@ -318,7 +318,7 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
if (profiles.size() < 2) if (profiles.size() < 2)
return RequestResult::Error(RequestStatus::NotEnoughResources); return RequestResult::Error(RequestStatus::NotEnoughResources);
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow* mainWindow = static_cast<QMainWindow*>(obs_frontend_get_main_window());
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName))); QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
return RequestResult::Success(); return RequestResult::Success();

View File

@ -18,3 +18,40 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include "RequestHandler.h" #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);
}

View File

@ -83,6 +83,43 @@ RequestResult RequestHandler::GetInputKindList(const Request& request)
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
/**
* Gets the names of all special inputs.
*
* @responseField desktop1 | String | Name of the Desktop Audio input
* @responseField desktop2 | String | Name of the Desktop Audio 2 input
* @responseField mic1 | String | Name of the Mic/Auxiliary Audio input
* @responseField mic2 | String | Name of the Mic/Auxiliary Audio 2 input
* @responseField mic3 | String | Name of the Mic/Auxiliary Audio 3 input
* @responseField mic4 | String | Name of the Mic/Auxiliary Audio 4 input
*
* @requestType GetSpecialInputs
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::GetSpecialInputs(const Request&)
{
json responseData;
std::vector<std::string> channels = {"desktop1", "desktop2", "mic1", "mic2", "mic3", "mic4"};
size_t channelId = 1;
for (auto &channel : channels) {
OBSSourceAutoRelease input = obs_get_output_source(channelId);
if (!input)
responseData[channel] = nullptr;
else
responseData[channel] = obs_source_get_name(input);
channelId++;
}
return RequestResult::Success(responseData);
}
/** /**
* Creates a new input, adding it as a scene item to the specified scene. * Creates a new input, adding it as a scene item to the specified scene.
* *
@ -346,6 +383,9 @@ RequestResult RequestHandler::GetInputMute(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData; json responseData;
responseData["inputMuted"] = obs_source_muted(input); responseData["inputMuted"] = obs_source_muted(input);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
@ -372,6 +412,9 @@ RequestResult RequestHandler::SetInputMute(const Request& request)
if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment))) if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
obs_source_set_muted(input, request.RequestData["inputMuted"]); obs_source_set_muted(input, request.RequestData["inputMuted"]);
return RequestResult::Success(); return RequestResult::Success();
@ -399,6 +442,9 @@ RequestResult RequestHandler::ToggleInputMute(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
bool inputMuted = !obs_source_muted(input); bool inputMuted = !obs_source_muted(input);
obs_source_set_muted(input, inputMuted); obs_source_set_muted(input, inputMuted);
@ -430,6 +476,9 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
float inputVolumeMul = obs_source_get_volume(input); float inputVolumeMul = obs_source_get_volume(input);
float inputVolumeDb = obs_mul_to_db(inputVolumeMul); float inputVolumeDb = obs_mul_to_db(inputVolumeMul);
if (inputVolumeDb == -INFINITY) if (inputVolumeDb == -INFINITY)
@ -446,7 +495,7 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
* *
* @requestField inputName | String | Name of the input to set the volume of * @requestField inputName | String | Name of the input to set the volume of
* @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified * @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified
* @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= -26 | `inputVolumeMul` should be specified * @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= 26 | `inputVolumeMul` should be specified
* *
* @requestType SetInputVolume * @requestType SetInputVolume
* @complexity 3 * @complexity 3
@ -463,6 +512,9 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
bool hasMul = request.Contains("inputVolumeMul"); bool hasMul = request.Contains("inputVolumeMul");
if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20)) if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -488,6 +540,67 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the audio balance of an input.
*
* @requestField inputName | String | Name of the input to get the audio balance of
*
* @responseField inputAudioBalance | Number | Audio balance value from 0.0-1.0
*
* @requestType GetInputAudioBalance
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::GetInputAudioBalance(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData;
responseData["inputAudioBalance"] = obs_source_get_balance_value(input);
return RequestResult::Success(responseData);
}
/**
* Sets the audio balance of an input.
*
* @requestField inputName | String | Name of the input to set the audio balance of
* @requestField inputAudioBalance | Number | New audio balance value | >= 0.0, <= 1.0
*
* @requestType SetInputAudioBalance
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::SetInputAudioBalance(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0)))
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
float inputAudioBalance = request.RequestData["inputAudioBalance"];
obs_source_set_balance_value(input, inputAudioBalance);
return RequestResult::Success();
}
/** /**
* Gets the audio sync offset of an input. * Gets the audio sync offset of an input.
* *
@ -512,6 +625,9 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData; json responseData;
// Offset is stored in nanoseconds in OBS. // Offset is stored in nanoseconds in OBS.
responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000; responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
@ -540,6 +656,9 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request)
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000))) if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
int64_t syncOffset = request.RequestData["inputAudioSyncOffset"]; int64_t syncOffset = request.RequestData["inputAudioSyncOffset"];
obs_source_set_sync_offset(input, syncOffset * 1000000); obs_source_set_sync_offset(input, syncOffset * 1000000);
@ -573,6 +692,9 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData; json responseData;
responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input); responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
@ -600,6 +722,12 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
if (!(input && request.ValidateString("monitorType", statusCode, comment))) if (!(input && request.ValidateString("monitorType", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
if (!obs_audio_monitoring_available())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Audio monitoring is not available on this platform.");
enum obs_monitoring_type monitorType; enum obs_monitoring_type monitorType;
std::string monitorTypeString = request.RequestData["monitorType"]; std::string monitorTypeString = request.RequestData["monitorType"];
if (monitorTypeString == "OBS_MONITORING_TYPE_NONE") if (monitorTypeString == "OBS_MONITORING_TYPE_NONE")
@ -616,6 +744,95 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the enable state of all audio tracks of an input.
*
* @requestField inputName | String | Name of the input
*
* @responseField inputAudioTracks | Object | Object of audio tracks and associated enable states
*
* @requestType GetInputAudioTracks
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::GetInputAudioTracks(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
long long tracks = obs_source_get_audio_mixers(input);
json inputAudioTracks;
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1);
}
json responseData;
responseData["inputAudioTracks"] = inputAudioTracks;
return RequestResult::Success(responseData);
}
/**
* Sets the enable state of audio tracks of an input.
*
* @requestField inputName | String | Name of the input
* @requestField inputAudioTracks | Object | Track settings to apply
*
* @requestType SetInputAudioTracks
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment))
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json inputAudioTracks = request.RequestData["inputAudioTracks"];
long long mixers = obs_source_get_audio_mixers(input);
for (long long i = 0; i < MAX_AUDIO_MIXES; i++) {
std::string track = std::to_string(i + 1);
if (!Utils::Json::Contains(inputAudioTracks, track))
continue;
if (!inputAudioTracks[track].is_boolean())
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean.");
bool enabled = inputAudioTracks[track];
if (enabled)
mixers |= (1 << i);
else
mixers &= ~(1 << i);
}
// Decided that checking if tracks have actually changed is unnecessary
obs_source_set_audio_mixers(input, mixers);
return RequestResult::Success();
}
/** /**
* Gets the items of a list property from an input's properties. * Gets the items of a list property from an input's properties.
* *

View 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();
}

View File

@ -631,3 +631,81 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the blend mode of a scene item.
*
* Blend modes:
*
* - `OBS_BLEND_NORMAL`
* - `OBS_BLEND_ADDITIVE`
* - `OBS_BLEND_SUBTRACT`
* - `OBS_BLEND_SCREEN`
* - `OBS_BLEND_MULTIPLY`
* - `OBS_BLEND_LIGHTEN`
* - `OBS_BLEND_DARKEN`
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sceneItemBlendMode | String | Current blend mode
*
* @requestType GetSceneItemBlendMode
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category scene items
*/
RequestResult RequestHandler::GetSceneItemBlendMode(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
json responseData;
responseData["sceneItemBlendMode"] = Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode);
return RequestResult::Success(responseData);
}
/**
* Sets the blend mode of a scene item.
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField sceneItemBlendMode | String | New blend mode
*
* @requestType SetSceneItemBlendMode
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category scene items
*/
RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
std::string blendModeString = request.RequestData["sceneItemBlendMode"];
auto blendMode = Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString);
if (blendMode == OBS_BLEND_NORMAL && blendModeString != "OBS_BLEND_NORMAL")
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field sceneItemBlendMode has an invalid value.");
obs_sceneitem_set_blending_mode(sceneItem, blendMode);
return RequestResult::Success();
}

View File

@ -24,7 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* *
* @responseField currentProgramSceneName | String | Current program scene * @responseField currentProgramSceneName | String | Current program scene
* @responseField currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode * @responseField currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode
* @responseField scenes | Array<Object> | Array of scenes in OBS * @responseField scenes | Array<Object> | Array of scenes
* *
* @requestType GetSceneList * @requestType GetSceneList
* @complexity 2 * @complexity 2
@ -54,6 +54,29 @@ RequestResult RequestHandler::GetSceneList(const Request&)
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
} }
/**
* Gets an array of all groups in OBS.
*
* Groups in OBS are actually scenes, but renamed and modified. In obs-websocket, we treat them as scenes where we can.
*
* @responseField groups | Array<String> | Array of group names
*
* @requestType GetGroupList
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category scenes
*/
RequestResult RequestHandler::GetGroupList(const Request&)
{
json responseData;
responseData["groups"] = Utils::Obs::ArrayHelper::GetGroupList();
return RequestResult::Success(responseData);
}
/** /**
* Gets the current program scene. * Gets the current program scene.
* *

View File

@ -312,3 +312,39 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
// Intentionally undocumented
RequestResult RequestHandler::GetSourcePrivateSettings(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
json responseData;
responseData["sourceSettings"] = Utils::Json::ObsDataToJson(privateSettings);
return RequestResult::Success(responseData);
}
// Intentionally undocumented
RequestResult RequestHandler::SetSourcePrivateSettings(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!source || !request.ValidateObject("sourceSettings", statusCode, comment))
return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(source);
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["sourceSettings"]);
// Always overlays to prevent destroying internal source data unintentionally
obs_data_apply(privateSettings, newSettings);
return RequestResult::Success();
}

View File

@ -70,3 +70,81 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Opens the properties dialog of an input.
*
* @requestField inputName | String | Name of the input to open the dialog of
*
* @requestType OpenInputPropertiesDialog
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category ui
* @api requests
*/
RequestResult RequestHandler::OpenInputPropertiesDialog(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
obs_frontend_open_source_properties(input);
return RequestResult::Success();
}
/**
* Opens the filters dialog of an input.
*
* @requestField inputName | String | Name of the input to open the dialog of
*
* @requestType OpenInputFiltersDialog
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category ui
* @api requests
*/
RequestResult RequestHandler::OpenInputFiltersDialog(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
obs_frontend_open_source_filters(input);
return RequestResult::Success();
}
/**
* Opens the interact dialog of an input.
*
* @requestField inputName | String | Name of the input to open the dialog of
*
* @requestType OpenInputInteractDialog
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category ui
* @api requests
*/
RequestResult RequestHandler::OpenInputInteractDialog(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_INTERACTION))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support interaction.");
obs_frontend_open_source_interaction(input);
return RequestResult::Success();
}

View File

@ -295,6 +295,27 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
return ret; return ret;
} }
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
if (!source)
return FilterPair{source, nullptr};
if (!ValidateString(filterKeyName, statusCode, comment))
return FilterPair{source, nullptr};
std::string filterName = RequestData[filterKeyName];
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
if (!filter) {
statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() + "` with the name `" + filterName + "`.";
return FilterPair{source, nullptr};
}
return FilterPair{source, filter};
}
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
{ {
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter); OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);

View File

@ -29,6 +29,12 @@ enum ObsWebSocketSceneFilter {
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP,
}; };
// We return filters as a pair because `obs_filter_get_parent()` is apparently volatile
struct FilterPair {
OBSSourceAutoRelease source;
OBSSourceAutoRelease filter;
};
struct Request struct Request
{ {
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None); Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
@ -53,6 +59,7 @@ struct Request
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const; obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const; obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const; obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
std::string RequestType; std::string RequestType;

View File

@ -21,7 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Platform.h" #include "Platform.h"
#include "../plugin-macros.generated.h" #include "../plugin-macros.generated.h"
bool Utils::Json::JsonArrayIsValidObsArray(json j) bool Utils::Json::JsonArrayIsValidObsArray(const json &j)
{ {
for (auto it : j) { for (auto it : j) {
if (!it.is_object()) if (!it.is_object())
@ -191,7 +191,7 @@ bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
return true; return true;
} }
bool Utils::Json::SetJsonFileContent(std::string fileName, json content, bool createNew) bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool createNew)
{ {
std::string textContent = content.dump(2); std::string textContent = content.dump(2);
return Utils::Platform::SetTextFileContent(fileName, textContent, createNew); return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);

View File

@ -27,10 +27,11 @@ using json = nlohmann::json;
namespace Utils { namespace Utils {
namespace Json { namespace Json {
bool JsonArrayIsValidObsArray(json j); bool JsonArrayIsValidObsArray(const json &j);
obs_data_t *JsonToObsData(json j); obs_data_t *JsonToObsData(json j);
json ObsDataToJson(obs_data_t *d, bool includeDefault = false); json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
bool GetJsonFileContent(std::string fileName, json &content); bool GetJsonFileContent(std::string fileName, json &content);
bool SetJsonFileContent(std::string fileName, json content, bool createNew = true); bool SetJsonFileContent(std::string fileName, const json &content, bool createNew = true);
static inline bool Contains(const json &j, std::string key) { return j.contains(key) && !j[key].is_null(); }
} }
} }

View File

@ -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) { template <typename T> T* GetCalldataPointer(const calldata_t *data, const char* name) {
void *ptr = nullptr; void *ptr = nullptr;
calldata_get_ptr(data, name, &ptr); calldata_get_ptr(data, name, &ptr);
return reinterpret_cast<T*>(ptr); return static_cast<T*>(ptr);
} }
enum ObsOutputState { enum ObsOutputState {
@ -163,6 +163,7 @@ namespace Utils {
std::string GetMediaInputState(obs_source_t *input); std::string GetMediaInputState(obs_source_t *input);
std::string GetLastReplayBufferFilePath(); std::string GetLastReplayBufferFilePath();
std::string GetSceneItemBoundsType(enum obs_bounds_type type); std::string GetSceneItemBoundsType(enum obs_bounds_type type);
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
std::string DurationToTimecode(uint64_t); std::string DurationToTimecode(uint64_t);
std::string GetOutputState(ObsOutputState state); std::string GetOutputState(ObsOutputState state);
} }
@ -170,11 +171,13 @@ namespace Utils {
namespace EnumHelper { namespace EnumHelper {
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType); enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction); enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
} }
namespace NumberHelper { namespace NumberHelper {
uint64_t GetOutputDuration(obs_output_t *output); uint64_t GetOutputDuration(obs_output_t *output);
size_t GetSceneCount(); size_t GetSceneCount();
size_t GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter);
} }
namespace ArrayHelper { namespace ArrayHelper {
@ -183,6 +186,7 @@ namespace Utils {
std::vector<obs_hotkey_t *> GetHotkeyList(); std::vector<obs_hotkey_t *> GetHotkeyList();
std::vector<std::string> GetHotkeyNameList(); std::vector<std::string> GetHotkeyNameList();
std::vector<json> GetSceneList(); std::vector<json> GetSceneList();
std::vector<std::string> GetGroupList();
std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false); std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false);
std::vector<json> GetInputList(std::string inputKind = ""); std::vector<json> GetInputList(std::string inputKind = "");
std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false); std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false);

View File

@ -29,7 +29,7 @@ struct CreateSceneItemData {
void CreateSceneItemHelper(void *_data, obs_scene_t *scene) 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); data->sceneItem = obs_scene_add(scene, data->source);
if (data->sceneItemTransform) if (data->sceneItemTransform)

View File

@ -61,7 +61,7 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
std::vector<obs_hotkey_t *> ret; std::vector<obs_hotkey_t *> ret;
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) { 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); 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++) { for (size_t i = 0; i < sceneList.sources.num; i++) {
obs_source_t *scene = sceneList.sources.array[i]; obs_source_t *scene = sceneList.sources.array[i];
if (obs_source_is_group(scene))
continue;
json sceneJson; json sceneJson;
sceneJson["sceneName"] = obs_source_get_name(scene); sceneJson["sceneName"] = obs_source_get_name(scene);
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1; sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
@ -109,13 +106,33 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
return ret; return ret;
} }
std::vector<std::string> Utils::Obs::ArrayHelper::GetGroupList()
{
std::vector<std::string> ret;
auto cb = [](void *priv_data, obs_source_t *scene) {
auto ret = static_cast<std::vector<std::string>*>(priv_data);
if (!obs_source_is_group(scene))
return true;
ret->emplace_back(obs_source_get_name(scene));
return true;
};
obs_enum_scenes(cb, &ret);
return ret;
}
std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, bool basic) std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
{ {
std::pair<std::vector<json>, bool> enumData; std::pair<std::vector<json>, bool> enumData;
enumData.second = basic; enumData.second = basic;
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) { obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
auto enumData = reinterpret_cast<std::pair<std::vector<json>, bool>*>(param); auto enumData = static_cast<std::pair<std::vector<json>, bool>*>(param);
json item; json item;
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem); 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) if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
return true; return true;
auto inputInfo = reinterpret_cast<EnumInputInfo*>(param); auto inputInfo = static_cast<EnumInputInfo*>(param);
std::string inputKind = obs_source_get_id(input); std::string inputKind = obs_source_get_id(input);

View File

@ -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) enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
{ {
RET_COMPARE(boundsType, OBS_BOUNDS_NONE); RET_COMPARE(boundsType, OBS_BOUNDS_NONE)
RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH); RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER); RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER); RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH); RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH)
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT); RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT)
RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY); RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY)
return OBS_BOUNDS_NONE; return OBS_BOUNDS_NONE;
} }
enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction) 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_PLAY)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE); 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_STOP)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART); 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_NEXT)
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS); RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS)
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE; 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;
}

View File

@ -39,7 +39,7 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
{ {
size_t ret; size_t ret;
auto sceneEnumProc = [](void *param, obs_source_t *scene) { auto sceneEnumProc = [](void *param, obs_source_t *scene) {
auto ret = reinterpret_cast<size_t*>(param); auto ret = static_cast<size_t*>(param);
if (obs_source_is_group(scene)) if (obs_source_is_group(scene))
return true; return true;
@ -52,3 +52,28 @@ size_t Utils::Obs::NumberHelper::GetSceneCount()
return ret; return ret;
} }
size_t Utils::Obs::NumberHelper::GetSourceFilterIndex(obs_source_t *source, obs_source_t *filter)
{
struct FilterSearch {
obs_source_t *filter;
bool found;
size_t index;
};
auto search = [](obs_source_t *, obs_source_t *filter, void *priv_data) {
auto filterSearch = static_cast<FilterSearch*>(priv_data);
if (filter == filterSearch->filter)
filterSearch->found = true;
if (!filterSearch->found)
filterSearch->index++;
};
FilterSearch filterSearch = {filter, 0, 0};
obs_source_enum_filters(source, search, &filterSearch);
return filterSearch.index;
}

View File

@ -64,12 +64,10 @@ std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath() std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
{ {
//char *recordOutputPath = obs_frontend_get_current_record_output_path(); char *recordOutputPath = obs_frontend_get_current_record_output_path();
//std::string ret = recordOutputPath; std::string ret = recordOutputPath;
//bfree(recordOutputPath); bfree(recordOutputPath);
//return ret; return ret;
return "";
} }
std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source) 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) std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
{ {
uint64_t secs = ms / 1000ULL; uint64_t secs = ms / 1000ULL;

View File

@ -1,354 +1,353 @@
/* /*
obs-websocket obs-websocket
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de> Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com> Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com> Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
#include "Obs.h" #include "Obs.h"
#include "Obs_VolumeMeter.h" #include "Obs_VolumeMeter.h"
#include "Obs_VolumeMeter_Helpers.h" #include "Obs_VolumeMeter_Helpers.h"
#include "../obs-websocket.h" #include "../obs-websocket.h"
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) : Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
PeakMeterType(SAMPLE_PEAK_METER), PeakMeterType(SAMPLE_PEAK_METER),
_input(obs_source_get_weak_source(input)), _input(obs_source_get_weak_source(input)),
_channels(0), _channels(0),
_lastUpdate(0), _lastUpdate(0),
_volume(obs_source_get_volume(input)) _volume(obs_source_get_volume(input))
{ {
signal_handler_t *sh = obs_source_get_signal_handler(input); signal_handler_t *sh = obs_source_get_signal_handler(input);
signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this); signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this);
obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this); obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input)); blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input));
} }
Utils::Obs::VolumeMeter::Meter::~Meter() Utils::Obs::VolumeMeter::Meter::~Meter()
{ {
OBSSourceAutoRelease input = obs_weak_source_get_source(_input); OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
if (!input) { if (!input) {
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?"); blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
return; return;
} }
signal_handler_t *sh = obs_source_get_signal_handler(input); signal_handler_t *sh = obs_source_get_signal_handler(input);
signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this); signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this);
obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this); obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this);
blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input)); blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input));
} }
bool Utils::Obs::VolumeMeter::Meter::InputValid() bool Utils::Obs::VolumeMeter::Meter::InputValid()
{ {
// return !obs_weak_source_expired(_input); return !obs_weak_source_expired(_input);
return true; }
}
json Utils::Obs::VolumeMeter::Meter::GetMeterData()
json Utils::Obs::VolumeMeter::Meter::GetMeterData() {
{ json ret;
json ret;
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
OBSSourceAutoRelease input = obs_weak_source_get_source(_input); if (!input) {
if (!input) { blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?"); return ret;
return ret; }
}
std::vector<std::vector<float>> levels;
std::vector<std::vector<float>> levels; const float volume = _muted ? 0.0f : _volume.load();
const float volume = _muted ? 0.0f : _volume.load();
std::unique_lock<std::mutex> l(_mutex);
std::unique_lock<std::mutex> l(_mutex);
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3)
if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3) ResetAudioLevels();
ResetAudioLevels();
for (int channel = 0; channel < _channels; channel++) {
for (int channel = 0; channel < _channels; channel++) { std::vector<float> level;
std::vector<float> level; level.push_back(_magnitude[channel] * volume);
level.push_back(_magnitude[channel] * volume); level.push_back(_peak[channel] * volume);
level.push_back(_peak[channel] * volume); level.push_back(_peak[channel]);
level.push_back(_peak[channel]);
levels.push_back(level);
levels.push_back(level); }
} l.unlock();
l.unlock();
ret["inputName"] = obs_source_get_name(input);
ret["inputName"] = obs_source_get_name(input); ret["inputLevelsMul"] = levels;
ret["inputLevelsMul"] = levels;
return ret;
return ret; }
}
// MUST HOLD LOCK
// MUST HOLD LOCK void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels()
void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels() {
{ _lastUpdate = 0;
_lastUpdate = 0; for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) {
for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) { _magnitude[channelNumber] = 0;
_magnitude[channelNumber] = 0; _peak[channelNumber] = 0;
_peak[channelNumber] = 0; }
} }
}
// MUST HOLD LOCK
// MUST HOLD LOCK void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data)
void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data) {
{ int channels = 0;
int channels = 0; for (int i = 0; i < MAX_AV_PLANES; i++) {
for (int i = 0; i < MAX_AV_PLANES; i++) { if (data->data[i])
if (data->data[i]) channels++;
channels++; }
}
bool channelsChanged = _channels != channels;
bool channelsChanged = _channels != channels; _channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS);
_channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS);
if (channelsChanged)
if (channelsChanged) ResetAudioLevels();
ResetAudioLevels(); }
}
// MUST HOLD LOCK
// MUST HOLD LOCK void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data)
void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data) {
{ size_t sampleCount = data->frames;
size_t sampleCount = data->frames; int channelNumber = 0;
int channelNumber = 0;
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { float *samples = (float*)data->data[planeNumber];
float *samples = (float*)data->data[planeNumber]; if (!samples)
if (!samples) continue;
continue;
if (((uintptr_t)samples & 0xf) > 0) {
if (((uintptr_t)samples & 0xf) > 0) { _peak[channelNumber] = 1.0f;
_peak[channelNumber] = 1.0f; channelNumber++;
channelNumber++; continue;
continue; }
}
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
__m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]);
float peak;
float peak; switch (PeakMeterType) {
switch (PeakMeterType) { default:
default: case SAMPLE_PEAK_METER:
case SAMPLE_PEAK_METER: peak = GetSamplePeak(previousSamples, samples, sampleCount);
peak = GetSamplePeak(previousSamples, samples, sampleCount); break;
break; case TRUE_PEAK_METER:
case TRUE_PEAK_METER: peak = GetTruePeak(previousSamples, samples, sampleCount);
peak = GetTruePeak(previousSamples, samples, sampleCount); break;
break; }
}
switch (sampleCount) {
switch (sampleCount) { case 0:
case 0: break;
break; case 1:
case 1: _previousSamples[channelNumber][0] = _previousSamples[channelNumber][1];
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][1]; _previousSamples[channelNumber][1] = _previousSamples[channelNumber][2];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][2]; _previousSamples[channelNumber][2] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][2] = _previousSamples[channelNumber][3]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; break;
break; case 2:
case 2: _previousSamples[channelNumber][0] = _previousSamples[channelNumber][2];
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][2]; _previousSamples[channelNumber][1] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][1] = _previousSamples[channelNumber][3]; _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][2] = samples[sampleCount - 2]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; break;
break; case 3:
case 3: _previousSamples[channelNumber][0] = _previousSamples[channelNumber][3];
_previousSamples[channelNumber][0] = _previousSamples[channelNumber][3]; _previousSamples[channelNumber][1] = samples[sampleCount - 3];
_previousSamples[channelNumber][1] = samples[sampleCount - 3]; _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][2] = samples[sampleCount - 2]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; break;
break; default:
default: _previousSamples[channelNumber][0] = samples[sampleCount - 4];
_previousSamples[channelNumber][0] = samples[sampleCount - 4]; _previousSamples[channelNumber][1] = samples[sampleCount - 3];
_previousSamples[channelNumber][1] = samples[sampleCount - 3]; _previousSamples[channelNumber][2] = samples[sampleCount - 2];
_previousSamples[channelNumber][2] = samples[sampleCount - 2]; _previousSamples[channelNumber][3] = samples[sampleCount - 1];
_previousSamples[channelNumber][3] = samples[sampleCount - 1]; }
}
_peak[channelNumber] = peak;
_peak[channelNumber] = peak;
channelNumber++;
channelNumber++; }
}
for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++)
for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) _peak[channelNumber] = 0.0;
_peak[channelNumber] = 0.0; }
}
// MUST HOLD LOCK
// MUST HOLD LOCK void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data)
void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data) {
{ size_t sampleCount = data->frames;
size_t sampleCount = data->frames;
int channelNumber = 0;
int channelNumber = 0; for (int planeNumber = 0; channelNumber < _channels; planeNumber++) {
for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { float *samples = (float*)data->data[planeNumber];
float *samples = (float*)data->data[planeNumber]; if (!samples)
if (!samples) continue;
continue;
float sum = 0.0;
float sum = 0.0; for (size_t i = 0; i < sampleCount; i++) {
for (size_t i = 0; i < sampleCount; i++) { float sample = samples[i];
float sample = samples[i]; sum += sample * sample;
sum += sample * sample; }
}
_magnitude[channelNumber] = std::sqrt(sum / sampleCount);
_magnitude[channelNumber] = std::sqrt(sum / sampleCount);
channelNumber++;
channelNumber++; }
} }
}
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted) {
{ auto c = static_cast<Meter*>(priv_data);
auto c = static_cast<Meter*>(priv_data);
std::unique_lock<std::mutex> l(c->_mutex);
std::unique_lock<std::mutex> l(c->_mutex);
c->_muted = muted;
c->_muted = muted; c->ProcessAudioChannels(data);
c->ProcessAudioChannels(data); c->ProcessPeak(data);
c->ProcessPeak(data); c->ProcessMagnitude(data);
c->ProcessMagnitude(data);
c->_lastUpdate = os_gettime_ns();
c->_lastUpdate = os_gettime_ns(); }
}
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd)
void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd) {
{ auto c = static_cast<Meter*>(priv_data);
auto c = static_cast<Meter*>(priv_data);
c->_volume = (float)calldata_float(cd, "volume");
c->_volume = (float)calldata_float(cd, "volume"); }
}
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) :
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) : _updateCallback(cb),
_updateCallback(cb), _updatePeriod(updatePeriod),
_updatePeriod(updatePeriod), _running(false)
_running(false) {
{ signal_handler_t *sh = obs_get_signal_handler();
signal_handler_t *sh = obs_get_signal_handler(); if (!sh)
if (!sh) return;
return;
auto enumProc = [](void *priv_data, obs_source_t *input) {
auto enumProc = [](void *priv_data, obs_source_t *input) { auto c = static_cast<Handler*>(priv_data);
auto c = static_cast<Handler*>(priv_data);
if (!obs_source_active(input))
if (!obs_source_active(input)) return true;
return true;
uint32_t flags = obs_source_get_output_flags(input);
uint32_t flags = obs_source_get_output_flags(input); if ((flags & OBS_SOURCE_AUDIO) == 0)
if ((flags & OBS_SOURCE_AUDIO) == 0) return true;
return true;
c->_meters.emplace_back(std::move(new Meter(input)));
c->_meters.emplace_back(std::move(new Meter(input)));
return true;
return true; };
}; obs_enum_sources(enumProc, this);
obs_enum_sources(enumProc, this);
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this);
signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this); signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
_running = true;
_running = true; _updateThread = std::thread(&Handler::UpdateThread, this);
_updateThread = std::thread(&Handler::UpdateThread, this);
blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created.");
blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created."); }
}
Utils::Obs::VolumeMeter::Handler::~Handler()
Utils::Obs::VolumeMeter::Handler::~Handler() {
{ signal_handler_t *sh = obs_get_signal_handler();
signal_handler_t *sh = obs_get_signal_handler(); if (!sh)
if (!sh) return;
return;
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this);
signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this); signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this);
if (_running) {
if (_running) { _running = false;
_running = false; _cond.notify_all();
_cond.notify_all(); }
}
if (_updateThread.joinable())
if (_updateThread.joinable()) _updateThread.join();
_updateThread.join();
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed.");
blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed."); }
}
void Utils::Obs::VolumeMeter::Handler::UpdateThread()
void Utils::Obs::VolumeMeter::Handler::UpdateThread() {
{ blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started.");
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started."); while (_running) {
while (_running) { {
{ std::unique_lock<std::mutex> l(_mutex);
std::unique_lock<std::mutex> l(_mutex); if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; }))
if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; })) break;
break; }
}
std::vector<json> inputs;
std::vector<json> inputs; std::unique_lock<std::mutex> l(_meterMutex);
std::unique_lock<std::mutex> l(_meterMutex); for (auto &meter : _meters) {
for (auto &meter : _meters) { if (meter->InputValid())
if (meter->InputValid()) inputs.push_back(meter->GetMeterData());
inputs.push_back(meter->GetMeterData()); }
} l.unlock();
l.unlock();
if (_updateCallback)
if (_updateCallback) _updateCallback(inputs);
_updateCallback(inputs); }
} blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped.");
blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped."); }
}
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd)
void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd) {
{ auto c = static_cast<Handler*>(priv_data);
auto c = static_cast<Handler*>(priv_data);
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source"); if (!input)
if (!input) return;
return;
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) return;
return;
uint32_t flags = obs_source_get_output_flags(input);
uint32_t flags = obs_source_get_output_flags(input); if ((flags & OBS_SOURCE_AUDIO) == 0)
if ((flags & OBS_SOURCE_AUDIO) == 0) return;
return;
std::unique_lock<std::mutex> l(c->_meterMutex);
std::unique_lock<std::mutex> l(c->_meterMutex); c->_meters.emplace_back(std::move(new Meter(input)));
c->_meters.emplace_back(std::move(new Meter(input))); }
}
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd)
void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd) {
{ auto c = static_cast<Handler*>(priv_data);
auto c = static_cast<Handler*>(priv_data);
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source");
obs_source_t *input = GetCalldataPointer<obs_source_t>(cd, "source"); if (!input)
if (!input) return;
return;
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) return;
return;
// Don't ask me why, but using std::remove_if segfaults trying this.
// Don't ask me why, but using std::remove_if segfaults trying this. std::unique_lock<std::mutex> l(c->_meterMutex);
std::unique_lock<std::mutex> l(c->_meterMutex); std::vector<MeterPtr>::iterator iter;
std::vector<MeterPtr>::iterator iter; for (iter = c->_meters.begin(); iter != c->_meters.end();) {
for (iter = c->_meters.begin(); iter != c->_meters.end();) { if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input))
if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input)) iter = c->_meters.erase(iter);
iter = c->_meters.erase(iter); else
else ++iter;
++iter; }
} }
}

View File

@ -1,99 +1,99 @@
/* /*
obs-websocket obs-websocket
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com> Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com> Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <obs.hpp> #include <obs.hpp>
#include "Obs.h" #include "Obs.h"
#include "Json.h" #include "Json.h"
namespace Utils { namespace Utils {
namespace Obs { namespace Obs {
namespace VolumeMeter { namespace VolumeMeter {
// Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c // Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c
// Keeps a running tally of the current audio levels, for a specific input // Keeps a running tally of the current audio levels, for a specific input
class Meter { class Meter {
public: public:
Meter(obs_source_t *input); Meter(obs_source_t *input);
~Meter(); ~Meter();
bool InputValid(); bool InputValid();
obs_weak_source_t *GetWeakInput() { return _input; } obs_weak_source_t *GetWeakInput() { return _input; }
json GetMeterData(); json GetMeterData();
std::atomic<enum obs_peak_meter_type> PeakMeterType; std::atomic<enum obs_peak_meter_type> PeakMeterType;
private: private:
OBSWeakSourceAutoRelease _input; OBSWeakSourceAutoRelease _input;
// All values in mul // All values in mul
std::mutex _mutex; std::mutex _mutex;
bool _muted; bool _muted;
int _channels; int _channels;
float _magnitude[MAX_AUDIO_CHANNELS]; float _magnitude[MAX_AUDIO_CHANNELS];
float _peak[MAX_AUDIO_CHANNELS]; float _peak[MAX_AUDIO_CHANNELS];
float _previousSamples[MAX_AUDIO_CHANNELS][4]; float _previousSamples[MAX_AUDIO_CHANNELS][4];
std::atomic<uint64_t> _lastUpdate; std::atomic<uint64_t> _lastUpdate;
std::atomic<float> _volume; std::atomic<float> _volume;
void ResetAudioLevels(); void ResetAudioLevels();
void ProcessAudioChannels(const struct audio_data *data); void ProcessAudioChannels(const struct audio_data *data);
void ProcessPeak(const struct audio_data *data); void ProcessPeak(const struct audio_data *data);
void ProcessMagnitude(const struct audio_data *data); void ProcessMagnitude(const struct audio_data *data);
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted); static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted);
static void InputVolumeCallback(void *priv_data, calldata_t *cd); static void InputVolumeCallback(void *priv_data, calldata_t *cd);
}; };
// Maintains an array of active inputs // Maintains an array of active inputs
class Handler { class Handler {
typedef std::function<void(std::vector<json>)> UpdateCallback; typedef std::function<void(std::vector<json>)> UpdateCallback;
typedef std::unique_ptr<Meter> MeterPtr; typedef std::unique_ptr<Meter> MeterPtr;
public: public:
Handler(UpdateCallback cb, uint64_t updatePeriod = 50); Handler(UpdateCallback cb, uint64_t updatePeriod = 50);
~Handler(); ~Handler();
private: private:
UpdateCallback _updateCallback; UpdateCallback _updateCallback;
std::mutex _meterMutex; std::mutex _meterMutex;
std::vector<MeterPtr> _meters; std::vector<MeterPtr> _meters;
uint64_t _updatePeriod; uint64_t _updatePeriod;
std::mutex _mutex; std::mutex _mutex;
std::condition_variable _cond; std::condition_variable _cond;
std::atomic<bool> _running; std::atomic<bool> _running;
std::thread _updateThread; std::thread _updateThread;
void UpdateThread(); void UpdateThread();
static void InputActivateCallback(void *priv_data, calldata_t *cd); static void InputActivateCallback(void *priv_data, calldata_t *cd);
static void InputDeactivateCallback(void *priv_data, calldata_t *cd); static void InputDeactivateCallback(void *priv_data, calldata_t *cd);
}; };
} }
} }
} }

View File

@ -56,7 +56,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
r = fmaxf(r, x4_mem[3]); \ r = fmaxf(r, x4_mem[3]); \
} while (false) } while (false)
float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount) static float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
{ {
__m128 peak = previousSamples; __m128 peak = previousSamples;
for (size_t i = 0; (i + 3) < sampleCount; i += 4) { for (size_t i = 0; (i + 3) < sampleCount; i += 4) {
@ -69,7 +69,7 @@ float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleC
return ret; return ret;
} }
float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount) static float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount)
{ {
const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f); const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);
const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f); const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);

View File

@ -111,9 +111,9 @@ void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QS
obs_queue_task(OBS_TASK_UI, [](void* param) { obs_queue_task(OBS_TASK_UI, [](void* param) {
void *systemTrayPtr = obs_frontend_get_system_tray(); void *systemTrayPtr = obs_frontend_get_system_tray();
auto systemTray = reinterpret_cast<QSystemTrayIcon*>(systemTrayPtr); auto systemTray = static_cast<QSystemTrayIcon*>(systemTrayPtr);
auto notification = reinterpret_cast<SystemTrayNotification*>(param); auto notification = static_cast<SystemTrayNotification*>(param);
systemTray->showMessage(notification->title, notification->body, notification->icon); systemTray->showMessage(notification->title, notification->body, notification->icon);
delete notification; delete notification;
}, (void*)notification, false); }, (void*)notification, false);

View File

@ -23,6 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QObject> #include <QObject>
#include <QThreadPool> #include <QThreadPool>
#include <QString> #include <QString>
#include <asio.hpp>
#include <websocketpp/config/asio_no_tls.hpp> #include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp> #include <websocketpp/server.hpp>