Compare commits

...

90 Commits

Author SHA1 Message Date
b02a32ce06 ci: Apply version suffix to macOS builds 2022-03-01 15:26:45 -08:00
ce31ed177d base: Stuff for beta1 2022-03-01 14:50:12 -08:00
71a32c981c utils: Reserve vector capacity where possible
Slight optimization for iteration
2022-03-01 14:48:10 -08:00
4440327141 docs(ci): Update generated docs - bbf9c28 [skip ci] 2022-03-01 22:37:04 +00:00
bbf9c283c0 eventhandler: Add SceneTransitionVideoEnded 2022-03-01 14:36:35 -08:00
9ac7c5890e eventhandler: Add SceneTransitionEnded 2022-03-01 14:36:35 -08:00
a7698a732f eventhandler: Add SceneTransitionStarted + cleanup
This commit cleans up a bunch of code in the event handler,
making it much easier to understand (IMO). I feel much better
about how we handle connecting and disconnecting callbacks. Before,
we were actually allowing a bunch of callbacks to stay connected and
get cleaned up. Now, we actually properly disconnect them.
2022-03-01 14:36:35 -08:00
e15b2edb4f Merge pull request #917 from you-win/readme/add-godot-obs-websocket-gd
README: Add Godot obs-websocket-gd to library list
2022-02-23 16:25:47 -08:00
7fade98407 README: Add Godot obs-websocket-gd to library list 2022-02-23 19:20:07 -05:00
e0057b05db docs(ci): Update generated docs - aa13828 [skip ci] 2022-02-16 22:26:10 +00:00
aa13828cf5 requesthandler: Add SetSourceFilterName 2022-02-16 13:37:18 -08:00
db2ffa569a Base: Fix some formatting 2022-02-16 13:30:10 -08:00
66c14dced5 requesthandler: Reorder a filter request 2022-02-16 13:23:49 -08:00
29b2b1bd5d docs(ci): Update generated docs - 361547a [skip ci] 2022-02-16 21:17:24 +00:00
361547a96d requesthandler: Filter requests & events (#888)
* Implement filter requests

* Fix CreateSourceFilter

* Implement most Filter events

* build against 27.1.3

* Update main.yml

* SourceFilterNameChanged rename

* revert main.yml changes

* rename SourceFilterCreated and revert CI changes

* cleanup

* Base: Various cleanups + fix -Werror

* Base: A few nitpicks/fixes

* requesthandler: Fix CreateSourceFilter

* utils: Fix CreateSourceFilter

Use obs_source_t* instead of OBSSourceAutoRelease to prevent double
release

* requesthandler: Remove filterIndex from CreateSourceFilter

The purpose of sceneItemEnabled in CreateSceneItem is to hide the
scene item while we still hold the scene mutex (guaranteeing the input
will never be shown). Since we don't hold a mutex when creating
filters, there's no reason to do any extra steps.

* requesthandler: Validate input/filter kinds in *DefaultSettings

Co-authored-by: tt2468 <tt2468@gmail.com>
2022-02-16 13:17:06 -08:00
b3a5c55bef CI: Only codesign when not PR 2022-02-16 12:00:01 -08:00
f76de69b34 CI: Use windows-2019 explicitly
Github actions recently migrated windows-latest to windows-2022 and
in the process broke a bunch of shit.
2022-02-16 11:55:57 -08:00
0b294734a2 docs(ci): Update generated docs - 7b52d7e [skip ci] 2022-02-15 05:02:03 +00:00
7b52d7e015 requesthandler: Move GetRecordDirectory to config
More consistency
2022-02-14 17:11:07 -08:00
9664f28483 requesthandler: Finish transition requests 2022-02-14 17:01:44 -08:00
d9070f9edb requesthandler: Add scene scene transition override requests
It's named like:
`Get Scene (Scene Transition) Override`
2022-02-14 15:57:33 -08:00
559212682a docs(ci): Update generated docs - fa8a091 [skip ci] 2022-02-13 23:20:38 +00:00
fa8a091a3e RequestHandler: Add SendStreamCaption 2022-02-13 15:20:19 -08:00
ab137ce8a4 ci: restrict push builds to the master branch 2022-02-14 00:19:19 +01:00
5a3bed7d8b ci(github): add macOS variant 2022-02-14 00:17:09 +01:00
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
341259e610 RequestHandler: Save config after profile parameter change
Fixes a bug where changed parameters were not applying across loads

Fixes #895
2022-02-13 14:46:49 -08:00
c64e360c2d Merge pull request #904 from dnaka91/fix-int-type
server: Fix int type of batch execution enum
2022-02-13 14:41:45 -08:00
7c35d6e738 docs(ci): Update generated docs - b206321 [skip ci] 2022-02-13 22:41:10 +00:00
b206321b05 Merge pull request #903 from dnaka91/fix-field-name-docs
docs: Fix wrong field name in `SceneItemLockStateChanged`
2022-02-13 14:40:49 -08:00
403c69463a server: Fix int type of batch execution enum 2022-02-02 10:36:59 +09: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
ddf752fd03 docs: Fix wrong field name in SceneItemLockStateChanged 2022-01-29 23:50:12 +09:00
e80bcad1e1 docs(ci): Update generated docs - d2ddde3 [skip ci] 2022-01-29 00:56:54 +00:00
d2ddde3229 eventhandler: Add a few transition events 2022-01-28 16:56:21 -08:00
931a1630ce README: Update link to workflow 2022-01-28 16:07:23 -08:00
5cbc1019ff docs(ci): Update generated docs - 1422723 [skip ci] 2022-01-28 23:56:23 +00:00
14227237d7 Base: [BREAKING] Update default WebSocket port to 4455
Our original strategy of relying on clients to simply detect the
protocol version and use the correct one was optimistic at best,
and it has been realized during the transition process from 4.x to 5.x
that sharing 4444 is not practical. As such, we'll be using 4455 in
the future for 5.x.

If you are a client developer, we suggest continuing to maintain
appropriate protocol version detection and support, as the WebSocket
port is at the end of the day simply a suggestion.
2022-01-28 15:38:08 -08:00
3e2984fd7a eventhandler: Add SceneItemSelected event
So I didn't think anyone actually used this, but I was wrong. So I'm
adding it again.
2022-01-28 15:33:28 -08:00
96a2fd8c25 docs(ci): Update generated docs - 38d7859 [skip ci] 2022-01-27 05:34:40 +00:00
38d78596ce requesthandler: Add replay buffer requests 2022-01-26 21:19:10 -08:00
13c7b83c34 requesthandler: Fix compiler warnings with latest OBS master
OBS has deprecated the `_addref` functions, so the new norm is to use
`_get_ref`.
2022-01-26 17:40:45 -08: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
58 changed files with 4998 additions and 949 deletions

View File

@ -69,8 +69,9 @@ 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-beta1
- 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,4 +1,4 @@
name: "CI Multiplatform Build" name: 'CI Multiplatform Build'
on: on:
push: push:
@ -18,15 +18,15 @@ on:
jobs: jobs:
windows: windows:
name: 'Windows 32/64-bit' name: 'Windows 32/64-bit'
runs-on: [windows-latest] runs-on: [windows-2019]
if: contains(github.event.head_commit.message, '[skip ci]') != true if: contains(github.event.head_commit.message, '[skip ci]') != true
env: env:
QT_CACHE_VERSION: '2' # Change whenever updating OBS dependencies URL, in order to force a cache reset QT_CACHE_VERSION: '2' # Change whenever updating OBS dependencies URL, in order to force a cache reset
QT_VERSION: '5.15.2' QT_VERSION: '5.15.2'
WINDOWS_DEPS_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset WINDOWS_DEPS_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset
WINDOWS_DEPS_VERSION: '2019' WINDOWS_DEPS_VERSION: '2019'
CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_GENERATOR: 'Visual Studio 16 2019'
CMAKE_SYSTEM_VERSION: "10.0" CMAKE_SYSTEM_VERSION: '10.0'
steps: steps:
- name: 'Add msbuild to PATH' - name: 'Add msbuild to PATH'
uses: microsoft/setup-msbuild@v1.0.2 uses: microsoft/setup-msbuild@v1.0.2
@ -35,20 +35,20 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-websocket path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive' submodules: 'recursive'
- name: 'Checkout OBS-Studio' - name: 'Checkout OBS Studio'
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio path: ${{ github.workspace }}/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: 'Get OBS-Studio Git Info' - name: 'Get OBS Studio Git Info'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
git fetch --prune --unshallow git fetch --prune --unshallow
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' - name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
@ -78,8 +78,6 @@ jobs:
with: with:
path: Qt_${{ env.QT_VERSION }}.7z path: Qt_${{ env.QT_VERSION }}.7z
key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}' key: 'qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}'
restore-keys: |
qtdep-${{ env.QT_CACHE_VERSION }} | ${{ runner.os }}
- name: 'Download Prerequisite: Qt' - name: 'Download Prerequisite: Qt'
if: steps.qtcache.outputs.cache-hit != 'true' if: steps.qtcache.outputs.cache-hit != 'true'
run: | run: |
@ -87,20 +85,18 @@ jobs:
- name: 'Extract Prerequisite: Qt' - name: 'Extract Prerequisite: Qt'
run: | run: |
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT" 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
- name: 'Restore Cached OBS-Studio Dependencies' - name: 'Restore Cached OBS Studio Dependencies'
id: obscache id: obscache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ${{ github.workspace }}\cmbuild\deps\** path: ${{ github.workspace }}\cmbuild\deps\**
key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}' key: 'obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
restore-keys: | - name: 'Install Prerequisite: Pre-built OBS Studio dependencies'
obsdep-${{ env.WINDOWS_DEPS_CACHE_VERSION }} | ${{ runner.os }}
- name: 'Install Prerequisite: Pre-built OBS-Studio dependencies'
if: steps.obscache.outputs.cache-hit != 'true' if: steps.obscache.outputs.cache-hit != 'true'
run: | run: |
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps" 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
- name: 'Restore OBS-Studio 32-bit Build v${{ env.OBS_GIT_TAG }} from Cache' - name: 'Restore OBS Studio 32-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
id: build-cache-obs-32 id: build-cache-obs-32
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
@ -108,22 +104,20 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-studio/build32 path: ${{ github.workspace }}/obs-studio/build32
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: | - name: 'Configure OBS Studio 32-bit'
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS-Studio 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true' if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\build32} if(!(Test-Path -Path ".\build32")){New-Item -ItemType directory -Path .\build32}
cd .\build32 cd .\build32
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF .. cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
- name: 'Build OBS-Studio 32-bit' - name: 'Build OBS Studio 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true' if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
- name: 'Restore OBS-Studio 64-bit Build v${{ env.OBS_GIT_TAG }} from Cache' - name: 'Restore OBS Studio 64-bit Build v${{ env.OBS_GIT_TAG }} from Cache'
id: build-cache-obs-64 id: build-cache-obs-64
uses: actions/cache@v1 uses: actions/cache@v1
env: env:
@ -131,16 +125,14 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-studio/build64 path: ${{ github.workspace }}/obs-studio/build64
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: | - name: 'Configure OBS Studio 64-bit'
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS-Studio 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true' if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\build64} if(!(Test-Path -Path ".\build64")){New-Item -ItemType directory -Path .\build64}
cd .\build64 cd .\build64
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF .. cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2019_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES -DBUILD_BROWSER=OFF ..
- name: 'Build OBS-Studio 64-bit' - name: 'Build OBS Studio 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true' if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
@ -188,7 +180,7 @@ jobs:
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer' name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Windows-Installer'
path: ${{ github.workspace }}/obs-websocket/package/*.exe path: ${{ github.workspace }}/obs-websocket/package/*.exe
ubuntu64: ubuntu64:
name: "Linux/Ubuntu 64-bit" name: 'Linux/Ubuntu 64-bit'
runs-on: [ubuntu-latest] runs-on: [ubuntu-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true if: contains(github.event.head_commit.message, '[skip ci]') != true
steps: steps:
@ -197,20 +189,20 @@ jobs:
with: with:
path: ${{ github.workspace }}/obs-websocket path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive' submodules: 'recursive'
- name: 'Checkout OBS-Studio' - name: 'Checkout OBS Studio'
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio path: ${{ github.workspace }}/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: 'Get OBS-Studio Git Info' - name: 'Get OBS Studio Git Info'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
git fetch --prune --unshallow git fetch --prune --unshallow
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' - name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
run: | run: |
@ -284,21 +276,21 @@ jobs:
libx11-xcb-dev \ libx11-xcb-dev \
libxcb1-dev \ libxcb1-dev \
libxss-dev \ libxss-dev \
- name: 'Configure OBS-Studio' - name: 'Configure OBS Studio'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
shell: bash shell: bash
run: | run: |
mkdir ./build mkdir ./build
cd ./build cd ./build
cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build OBS-Studio' - name: 'Build OBS Studio'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
shell: bash shell: bash
run: | run: |
set -e set -e
cd ./build cd ./build
make -j4 libobs obs-frontend-api make -j4 libobs obs-frontend-api
- name: 'Install OBS-Studio' - name: 'Install OBS Studio'
working-directory: ${{ github.workspace }}/obs-studio working-directory: ${{ github.workspace }}/obs-studio
shell: bash shell: bash
run: | run: |
@ -316,7 +308,7 @@ jobs:
if [ "${{ env.GIT_TAG }}" ] ; then \ if [ "${{ env.GIT_TAG }}" ] ; then \
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DCMAKE_BUILD_TYPE=Release .. ; \ cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DCMAKE_BUILD_TYPE=Release .. ; \
else \ else \
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" .. ; \ cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=TRUE -DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}" -DPLUGIN_TESTS=TRUE .. ; \
fi fi
- name: 'Build obs-websocket' - name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket working-directory: ${{ github.workspace }}/obs-websocket
@ -355,3 +347,214 @@ jobs:
with: with:
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64' name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-Ubuntu64'
path: '${{ github.workspace }}/obs-websocket/package/*.deb' path: '${{ github.workspace }}/obs-websocket/package/*.deb'
macOS:
name: 'macOS 64-bit'
runs-on: [macos-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true
env:
MACOS_DEPS_VERSION: '2022-01-01'
MACOS_DEPS_CACHE_VERSION: '2' # Change whenever updating dependencies version, in order to force a cache reset
steps:
- name: 'Checkout obs-websocket'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS Studio'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Install Prerequisite: Binary Signing Certificate'
if: github.event_name != 'pull_request'
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.MACOS_SIGNING_CERT }}
p12-password: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
create-keychain: true
keychain-password: ${{ secrets.MACOS_TEMP_CI_KEYCHAIN_PASSWORD }}
- name: 'Install Prerequisite: Installer Signing Certificate'
if: github.event_name != 'pull_request'
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.MACOS_INSTALLER_CERT }}
p12-password: ${{ secrets.MACOS_INSTALLER_CERT_PASSWORD }}
create-keychain: false
keychain-password: ${{ secrets.MACOS_TEMP_CI_KEYCHAIN_PASSWORD }}
- name: 'Get OBS Studio Git Info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo "OBS_GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "OBS_GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: 'Checkout last OBS Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
GIT_HASH=$(git rev-parse --short HEAD)
echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV
GIT_TAG=$(git describe --exact-match --tags --abbrev=0) || GIT_TAG=""
echo "GIT_TAG=$GIT_TAG" >> $GITHUB_ENV
if [ "$GIT_TAG" ] ; then \
VERSION="$GIT_TAG" \
VERSION_SUFFIX=$(echo "$GIT_TAG" | cut -c6-20) ; \
else \
VERSION="$GIT_HASH-git" \
VERSION_SUFFIX="-$GIT_HASH-git" ; \
fi
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
echo "CMAKE_VERSION_SUFFIX=$VERSION_SUFFIX" >> $GITHUB_ENV
- name: 'Install Packages'
shell: bash
run: |
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
sudo hdiutil attach ./Packages.dmg
sudo installer -pkg /Volumes/Packages\ 1.2.10/Install\ Packages.pkg -target /
- name: 'Restore Cached Qt & OBS Studio dependencies'
id: deps-cache
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/obsdeps/**
key: 'deps-cache-${{ env.MACOS_DEPS_CACHE_VERSION }} | ${{ runner.os }}'
- name: 'Install Prerequisite: Qt + OBS Studio dependencies'
if: steps.deps-cache.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir -p obsdeps
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-qt-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz
tar -xf macos-deps-qt-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz -C "./obsdeps"
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz
tar -xf macos-deps-${{ env.MACOS_DEPS_VERSION }}-universal.tar.xz -C "./obsdeps"
- run: xattr -r -d com.apple.quarantine ./obsdeps
shell: bash
- name: 'Configue OBS Studio'
if: steps.cache-obs-build.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
mkdir -p ./build
cd ./build
cmake .. \
-DQTDIR=${{ github.workspace }}/obsdeps \
-DDepsPath=${{ github.workspace }}/obsdeps \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
-DDISABLE_PLUGINS=true \
-DENABLE_SCRIPTING=0 \
-DCMAKE_PREFIX_PATH=${{ github.workspace }}/obsdeps/lib/cmake
- name: 'Build OBS Studio'
if: steps.cache-obs-build.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio/build
shell: bash
run: |
set -e
make -j4 libobs obs-frontend-api
- name: 'Configure obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
mkdir -p build
cd build
cmake .. \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
-DQTDIR=${{ github.workspace }}/obsdeps \
-DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs \
-DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs \
-DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=/usr \
-DOBS_WEBSOCKET_VERSION_SUFFIX="${{ env.CMAKE_VERSION_SUFFIX }}"
- name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket/build
shell: bash
run: |
set -e
make -j4
- name: 'Relink Qt'
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket/build
run: |
install_name_tool \
-change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets \
@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \
-change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui \
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \
-change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore \
@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \
-change /tmp/obsdeps/lib/QtNetwork.framework/Versions/5/QtNetwork \
@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork \
-change /tmp/obsdeps/lib/QtSvg.framework/Versions/5/QtSvg \
@executable_path/../Frameworks/QtSvg.framework/Versions/5/QtSvg \
./obs-websocket.so
- name: 'Sign plugin binary'
if: github.event_name != 'pull_request'
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket/build
run: |
codesign --sign "${{ secrets.MACOS_SIGNING_IDENTITY }}" ./obs-websocket.so
- name: 'Set PR Artifact Filename'
shell: bash
run: |
echo "MACOS_FILENAME=obs-websocket-${{ env.PACKAGE_VERSION }}-macOS.pkg" >> $GITHUB_ENV
echo "MACOS_FILENAME_UNSIGNED=obs-websocket-${{ env.PACKAGE_VERSION }}-macOS-Unsigned.pkg" >> $GITHUB_ENV
- name: 'Package ${{ env.MACOS_FILENAME_UNSIGNED }}'
if: success()
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
packagesbuild ./CI/macos/obs-websocket.pkgproj
mv ./release/obs-websocket.pkg ./release/${{ env.MACOS_FILENAME_UNSIGNED }}
- name: 'Sign plugin package'
if: ${{ env.GIT_TAG != '' }}
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket
run: |
productsign \
--sign "${{ secrets.MACOS_INSTALLER_IDENTITY }}" \
./release/${{ env.MACOS_FILENAME_UNSIGNED }} \
./release/${{ env.MACOS_FILENAME }}
rm ./release/${{ env.MACOS_FILENAME_UNSIGNED }}
- name: 'Notarize package'
if: ${{ env.GIT_TAG != '' }}
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket
run: |
zip -r ./release/${{ env.MACOS_FILENAME }}.zip ./release/${{ env.MACOS_FILENAME }}
UPLOAD_RESULT=$(xcrun altool --notarize-app \
--primary-bundle-id "com.obsproject.obs-websocket" \
--username "${{ secrets.MACOS_NOTARIZATION_USERNAME }}" \
--password "${{ secrets.MACOS_NOTARIZATION_PASSWORD }}" \
--asc-provider "${{ secrets.ASC_PROVIDER_SHORTNAME }}" \
--file "./release/${{ env.MACOS_FILENAME }}.zip")
rm ./release/${{ env.MACOS_FILENAME }}.zip
REQUEST_UUID=$(echo $UPLOAD_RESULT | awk -F ' = ' '/RequestUUID/ {print $2}')
# Pieces of code borrowed from rednoah/notarized-app
while sleep 30 && date; do
CHECK_RESULT=$(xcrun altool \
--notarization-info "$REQUEST_UUID" \
--username "${{ secrets.MACOS_NOTARIZATION_USERNAME }}" \
--password "${{ secrets.MACOS_NOTARIZATION_PASSWORD }}" \
--asc-provider "${{ secrets.ASC_PROVIDER_SHORTNAME }}")
if ! grep -q "Status: in progress" <<< "$CHECK_RESULT"; then
xcrun stapler staple ./release/${{ env.MACOS_FILENAME }}
break
fi
done
- name: 'Publish Packages'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: 'obs-websocket-${{ env.PACKAGE_VERSION }}-macOS'
path: '${{ github.workspace }}/obs-websocket/release/*.pkg'

1
.gitignore vendored
View File

@ -11,3 +11,4 @@
/docs/node_modules/ /docs/node_modules/
/src/plugin-macros.generated.h /src/plugin-macros.generated.h
/installer/installer-windows.generated.iss /installer/installer-windows.generated.iss
/cmake-build-debug/

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

View File

@ -6,7 +6,7 @@
WebSocket API for OBS Studio. WebSocket API for OBS Studio.
[![CI Multiplatform Build](https://github.com/obsproject/obs-websocket/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/obs-websocket/obs-websocket/actions/workflows/main.yml) [![CI Multiplatform Build](https://github.com/obsproject/obs-websocket/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/obsproject/obs-websocket/actions/workflows/main.yml)
[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A) [![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A)
[![Financial Contributors on Open Collective](https://opencollective.com/obs-websocket-dev/all/badge.svg?label=financial+contributors)](https://opencollective.com/obs-websocket-dev) [![Financial Contributors on Open Collective](https://opencollective.com/obs-websocket-dev/all/badge.svg?label=financial+contributors)](https://opencollective.com/obs-websocket-dev)
@ -27,18 +27,19 @@ It is **highly recommended** to protect obs-websocket with a password against un
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...) - Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
### Client software ### Client software
- (No known clients supporting 5.0.0 at the moment. Send a message in Discord if you have one!) - (No known clients supporting 5.0.0 at the moment. Ping us in the Discord if you have one!)
### Client libraries (for developers) ### Client libraries (for developers)
Here's a list of available language APIs for obs-websocket: Here's a list of available language APIs for obs-websocket:
- Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit - Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit
- Rust: [obws](https://github.com/dnaka91/obws/tree/v5-api) by dnaka91 - Rust: [obws](https://github.com/dnaka91/obws/tree/v5-api) by dnaka91
- Godot 3.4.x: [obs-websocket-gd](https://github.com/you-win/obs-websocket-gd) by you-win
The server is a typical Websockets server running by default on port 4444 (the port number can be changed in the Settings dialog under `Tools`). The 5.x server is a typical WebSocket server running by default on port 4455 (the port number can be changed in the Settings dialog under `Tools`).
The protocol we use is documented in [PROTOCOL.md](docs/generated/protocol.md). The protocol we use is documented in [PROTOCOL.md](docs/generated/protocol.md).
We'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A) We'd like to know what you're building with obs-websocket! If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A)
## Contributors ## Contributors

2
deps/asio vendored

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@ -41,7 +41,7 @@ Config::Config() :
PasswordOverridden(false), PasswordOverridden(false),
FirstLoad(true), FirstLoad(true),
ServerEnabled(true), ServerEnabled(true),
ServerPort(4444), ServerPort(4455),
DebugEnabled(false), DebugEnabled(false),
AlertsEnabled(false), AlertsEnabled(false),
AuthRequired(true), AuthRequired(true),

View File

@ -115,6 +115,7 @@ void EventHandler::BroadcastEvent(uint64_t requiredIntent, std::string eventType
_broadcastCallback(requiredIntent, eventType, eventData, rpcVersion); _broadcastCallback(requiredIntent, eventType, eventData, rpcVersion);
} }
// Connect source signals for Inputs, Scenes, and Transitions. Filters are automatically connected.
void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inputs and scenes void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inputs and scenes
{ {
if (!source || obs_source_removed(source)) if (!source || obs_source_removed(source))
@ -128,17 +129,17 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
obs_source_type sourceType = obs_source_get_type(source); obs_source_type sourceType = obs_source_get_type(source);
// Inputs // Inputs
signal_handler_connect(sh, "activate", HandleInputActiveStateChanged, this);
signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_connect(sh, "show", HandleInputShowStateChanged, this);
signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, 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, "activate", HandleInputActiveStateChanged, this);
signal_handler_connect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_connect(sh, "show", HandleInputShowStateChanged, this);
signal_handler_connect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_connect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_connect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_connect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
signal_handler_connect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_connect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
signal_handler_connect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this); signal_handler_connect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
signal_handler_connect(sh, "media_ended", HandleMediaInputPlaybackEnded, this); signal_handler_connect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this); signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
@ -156,10 +157,37 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
signal_handler_connect(sh, "reorder", HandleSceneItemListReindexed, this); signal_handler_connect(sh, "reorder", HandleSceneItemListReindexed, this);
signal_handler_connect(sh, "item_visible", HandleSceneItemEnableStateChanged, this); signal_handler_connect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
signal_handler_connect(sh, "item_locked", HandleSceneItemLockStateChanged, this); signal_handler_connect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
signal_handler_connect(sh, "item_select", HandleSceneItemSelected, this);
signal_handler_connect(sh, "item_transform", HandleSceneItemTransformChanged, this); signal_handler_connect(sh, "item_transform", HandleSceneItemTransformChanged, this);
} }
// Scenes and Inputs
if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_connect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
signal_handler_connect(sh, "filter_add", FilterAddMultiHandler, this);
signal_handler_connect(sh, "filter_remove", FilterRemoveMultiHandler, this);
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(filter);
};
obs_source_enum_filters(source, enumFilters, this);
}
// Transitions
if (sourceType == OBS_SOURCE_TYPE_TRANSITION) {
signal_handler_connect(sh, "transition_start", HandleSceneTransitionStarted, this);
signal_handler_connect(sh, "transition_stop", HandleSceneTransitionEnded, this);
signal_handler_connect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this);
}
// Filters
if (sourceType == OBS_SOURCE_TYPE_FILTER) {
signal_handler_connect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
signal_handler_connect(sh, "rename", HandleSourceFilterNameChanged, this);
}
} }
// Disconnect source signals for Inputs, Scenes, and Transitions. Filters are automatically disconnected.
void EventHandler::DisconnectSourceSignals(obs_source_t *source) void EventHandler::DisconnectSourceSignals(obs_source_t *source)
{ {
if (!source) if (!source)
@ -167,70 +195,120 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
signal_handler_t* sh = obs_source_get_signal_handler(source); signal_handler_t* sh = obs_source_get_signal_handler(source);
obs_source_type sourceType = obs_source_get_type(source);
// Inputs // Inputs
signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this); if (sourceType == OBS_SOURCE_TYPE_INPUT) {
signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this); signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this); signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this); signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this); signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this); signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this); signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this); signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
//signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this); signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this); signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this); signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this); signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this); signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, this); signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this); signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this);
signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this); signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, this);
signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this); signal_handler_disconnect(sh, "media_stopped", SourceMediaStopMultiHandler, this);
signal_handler_disconnect(sh, "media_next", SourceMediaNextMultiHandler, this);
signal_handler_disconnect(sh, "media_previous", SourceMediaPreviousMultiHandler, this);
}
// Scenes // Scenes
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this); if (sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this); signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this); signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this);
signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this); signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this);
signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this); signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this); signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
signal_handler_disconnect(sh, "item_select", HandleSceneItemSelected, this);
signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this);
}
// Inputs and Scenes
if (sourceType == OBS_SOURCE_TYPE_INPUT || sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_disconnect(sh, "reorder_filters", HandleSourceFilterListReindexed, this);
signal_handler_disconnect(sh, "filter_add", FilterAddMultiHandler, this);
signal_handler_disconnect(sh, "filter_remove", FilterRemoveMultiHandler, this);
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param){
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(filter);
};
obs_source_enum_filters(source, enumFilters, this);
}
// Transitions
if (sourceType == OBS_SOURCE_TYPE_TRANSITION) {
signal_handler_disconnect(sh, "transition_start", HandleSceneTransitionStarted, this);
signal_handler_disconnect(sh, "transition_stop", HandleSceneTransitionEnded, this);
signal_handler_disconnect(sh, "transition_video_stop", HandleSceneTransitionVideoEnded, this);
}
// Filters
if (sourceType == OBS_SOURCE_TYPE_FILTER) {
signal_handler_disconnect(sh, "enable", HandleSourceFilterEnableStateChanged, this);
signal_handler_disconnect(sh, "rename", HandleSourceFilterNameChanged, this);
}
} }
void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data) void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_data)
{ {
auto eventHandler = reinterpret_cast<EventHandler*>(private_data); auto eventHandler = static_cast<EventHandler*>(private_data);
if (!eventHandler->_obsLoaded.load()) { if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING)
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { return;
switch (event) {
// General
case OBS_FRONTEND_EVENT_FINISHED_LOADING:
blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events..."); blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging). // Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
eventHandler->_obsLoaded.store(true); eventHandler->_obsLoaded.store(true);
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()` // In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()`
// Enumerate inputs and connect each one // Enumerate inputs and connect each one
obs_enum_sources([](void* param, obs_source_t* source) { {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto enumInputs = [](void *param, obs_source_t *source) {
eventHandler->ConnectSourceSignals(source); auto eventHandler = static_cast<EventHandler*>(param);
return true; eventHandler->ConnectSourceSignals(source);
}, private_data); return true;
};
obs_enum_sources(enumInputs, private_data);
}
// Enumerate scenes and connect each one // Enumerate scenes and connect each one
obs_enum_scenes([](void* param, obs_source_t* source) { {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto enumScenes = [](void *param, obs_source_t *source) {
eventHandler->ConnectSourceSignals(source); auto eventHandler = static_cast<EventHandler*>(param);
return true; eventHandler->ConnectSourceSignals(source);
}, private_data); return true;
};
obs_enum_scenes(enumScenes, private_data);
}
// Enumerate all scene transitions and connect each one
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
blog_debug("[EventHandler::OnFrontendEvent] Finished."); blog_debug("[EventHandler::OnFrontendEvent] Finished.");
if (eventHandler->_obsLoadedCallback) if (eventHandler->_obsLoadedCallback)
eventHandler->_obsLoadedCallback(); eventHandler->_obsLoadedCallback();
} else {
return;
}
}
switch (event) { break;
// General
case OBS_FRONTEND_EVENT_EXIT: case OBS_FRONTEND_EVENT_EXIT:
eventHandler->HandleExitStarted(); eventHandler->HandleExitStarted();
@ -240,18 +318,35 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()` // In the case that plugins become hotloadable, this will have to go back into `EventHandler::~EventHandler()`
// Enumerate inputs and disconnect each one // Enumerate inputs and disconnect each one
obs_enum_sources([](void* param, obs_source_t* source) { {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto enumInputs = [](void *param, obs_source_t *source) {
eventHandler->DisconnectSourceSignals(source); auto eventHandler = static_cast<EventHandler*>(param);
return true; eventHandler->DisconnectSourceSignals(source);
}, private_data); return true;
};
obs_enum_sources(enumInputs, private_data);
}
// Enumerate scenes and disconnect each one // Enumerate scenes and disconnect each one
obs_enum_scenes([](void* param, obs_source_t* source) { {
auto eventHandler = reinterpret_cast<EventHandler*>(param); auto enumScenes = [](void *param, obs_source_t *source) {
eventHandler->DisconnectSourceSignals(source); auto eventHandler = static_cast<EventHandler*>(param);
return true; eventHandler->DisconnectSourceSignals(source);
}, private_data); return true;
};
obs_enum_scenes(enumScenes, private_data);
}
// Enumerate all scene transitions and disconnect each one
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->DisconnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
blog_debug("[EventHandler::OnFrontendEvent] Finished."); blog_debug("[EventHandler::OnFrontendEvent] Finished.");
@ -264,18 +359,36 @@ 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(); {
// break; obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->DisconnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
eventHandler->HandleCurrentSceneCollectionChanging();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
eventHandler->HandleCurrentSceneCollectionChanged(); eventHandler->HandleCurrentSceneCollectionChanged();
break; break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
eventHandler->HandleSceneCollectionListChanged(); eventHandler->HandleSceneCollectionListChanged();
break; break;
//case OBS_FRONTEND_EVENT_PROFILE_CHANGING: case OBS_FRONTEND_EVENT_PROFILE_CHANGING:
// eventHandler->HandleCurrentProfileChanging(); eventHandler->HandleCurrentProfileChanging();
// break; break;
case OBS_FRONTEND_EVENT_PROFILE_CHANGED: case OBS_FRONTEND_EVENT_PROFILE_CHANGED:
eventHandler->HandleCurrentProfileChanged(); eventHandler->HandleCurrentProfileChanged();
break; break;
@ -296,10 +409,21 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
// Transitions // Transitions
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED: case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
eventHandler->HandleCurrentSceneTransitionChanged();
break; break;
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED:
{
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t* transition = transitions.sources.array[i];
eventHandler->ConnectSourceSignals(transition);
}
obs_frontend_source_list_free(&transitions);
}
break; break;
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED: case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
eventHandler->HandleCurrentSceneTransitionDurationChanged();
break; break;
// Outputs // Outputs
@ -363,7 +487,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 +514,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 +542,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 +565,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;
@ -459,8 +583,6 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
case OBS_SOURCE_TYPE_INPUT: case OBS_SOURCE_TYPE_INPUT:
eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName); eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName);
break; break;
case OBS_SOURCE_TYPE_FILTER:
break;
case OBS_SOURCE_TYPE_TRANSITION: case OBS_SOURCE_TYPE_TRANSITION:
break; break;
case OBS_SOURCE_TYPE_SCENE: case OBS_SOURCE_TYPE_SCENE:

View File

@ -107,10 +107,27 @@ class EventHandler
static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback static void HandleInputShowStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback static void HandleInputMuteStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback static void HandleInputVolumeChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioBalanceChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback static void HandleInputAudioSyncOffsetChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback static void HandleInputAudioTracksChanged(void *param, calldata_t *data); // Direct callback
static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback static void HandleInputAudioMonitorTypeChanged(void *param, calldata_t *data); // Direct callback
// Transitions
void HandleCurrentSceneTransitionChanged();
void HandleCurrentSceneTransitionDurationChanged();
static void HandleSceneTransitionStarted(void *param, calldata_t *data); // Direct callback
static void HandleSceneTransitionEnded(void *param, calldata_t *data); // Direct callback
static void HandleSceneTransitionVideoEnded(void *param, calldata_t *data); // Direct callback
// Filters
static void FilterAddMultiHandler(void *param, calldata_t *data); // Direct callback
static void FilterRemoveMultiHandler(void *param, calldata_t *data); // Direct callback
static void HandleSourceFilterListReindexed(void *param, calldata_t *data); // Direct callback
void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter);
void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter);
static void HandleSourceFilterNameChanged(void *param, calldata_t *data); // Direct callback
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
// Outputs // Outputs
void HandleStreamStateChanged(ObsOutputState state); void HandleStreamStateChanged(ObsOutputState state);
void HandleRecordStateChanged(ObsOutputState state); void HandleRecordStateChanged(ObsOutputState state);
@ -124,6 +141,7 @@ class EventHandler
static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback static void HandleSceneItemListReindexed(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback static void HandleSceneItemEnableStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback static void HandleSceneItemLockStateChanged(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemSelected(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
// Media Inputs // Media Inputs

View File

@ -18,3 +18,184 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include "EventHandler.h" #include "EventHandler.h"
void EventHandler::FilterAddMultiHandler(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
if (!(source && filter))
return;
eventHandler->ConnectSourceSignals(filter);
eventHandler->HandleSourceFilterCreated(source, filter);
}
void EventHandler::FilterRemoveMultiHandler(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "filter");
if (!(source && filter))
return;
eventHandler->DisconnectSourceSignals(filter);
eventHandler->HandleSourceFilterRemoved(source, filter);
}
/**
* A source's filter list has been reindexed.
*
* @dataField sourceName | String | Name of the source
* @dataField filters | Array<Object> | Array of filter objects
*
* @eventType SourceFilterListReindexed
* @eventSubscription Filters
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category filters
*/
void EventHandler::HandleSourceFilterListReindexed(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source)
return;
json eventData;
eventData["sourceName"] = obs_source_get_name(source);
eventData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterListReindexed", eventData);
}
/**
* A filter has been added to a source.
*
* @dataField sourceName | String | Name of the source the filter was added to
* @dataField filterName | String | Name of the filter
* @dataField filterKind | String | The kind of the filter
* @dataField filterIndex | Number | Index position of the filter
* @dataField filterSettings | Object | The settings configured to the filter when it was created
* @dataField defaultFilterSettings | Object | The default settings for the filter
*
* @eventType SourceFilterCreated
* @eventSubscription Filters
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category filters
*/
void EventHandler::HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter)
{
std::string filterKind = obs_source_get_id(filter);
OBSDataAutoRelease filterSettings = obs_source_get_settings(filter);
OBSDataAutoRelease defaultFilterSettings = obs_get_source_defaults(filterKind.c_str());
json eventData;
eventData["sourceName"] = obs_source_get_name(source);
eventData["filterName"] = obs_source_get_name(filter);
eventData["filterKind"] = filterKind;
eventData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
eventData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
eventData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultFilterSettings, true);
BroadcastEvent(EventSubscription::Filters, "SourceFilterCreated", eventData);
}
/**
* A filter has been removed from a source.
*
* @dataField sourceName | String | Name of the source the filter was on
* @dataField filterName | String | Name of the filter
*
* @eventType SourceFilterRemoved
* @eventSubscription Filters
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category filters
*/
void EventHandler::HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter)
{
json eventData;
eventData["sourceName"] = obs_source_get_name(source);
eventData["filterName"] = obs_source_get_name(filter);
BroadcastEvent(EventSubscription::Filters, "SourceFilterRemoved", eventData);
}
/**
* The name of a source filter has changed.
*
* @dataField sourceName | String | The source the filter is on
* @dataField oldFilterName | String | Old name of the filter
* @dataField filterName | String | New name of the filter
*
* @eventType SourceFilterNameChanged
* @eventSubscription Filters
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category filters
*/
void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
if (!filter)
return;
json eventData;
eventData["sourceName"] = obs_source_get_name(obs_filter_get_parent(filter));
eventData["oldFilterName"] = calldata_string(data, "prev_name");
eventData["filterName"] = calldata_string(data, "new_name");
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterNameChanged", eventData);
}
/**
* A source filter's enable state has changed.
*
* @dataField sourceName | String | Name of the source the filter is on
* @dataField filterName | String | Name of the filter
* @dataField filterEnabled | Boolean | Whether the filter is enabled
*
* @eventType SourceFilterEnableStateChanged
* @eventSubscription Filters
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category filters
*/
void EventHandler::HandleSourceFilterEnableStateChanged(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *filter = GetCalldataPointer<obs_source_t>(data, "source");
if (!filter)
return;
// Not OBSSourceAutoRelease as get_parent doesn't increment refcount
obs_source_t *source = obs_filter_get_parent(filter);
if (!source)
return;
bool filterEnabled = calldata_bool(data, "enabled");
json eventData;
eventData["sourceName"] = obs_source_get_name(source);
eventData["filterName"] = obs_source_get_name(filter);
eventData["filterEnabled"] = filterEnabled;
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterEnableStateChanged", eventData);
}

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)
@ -158,9 +158,9 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
/** /**
* A scene item's lock state has changed. * A scene item's lock state has changed.
* *
* @dataField sceneName | String | Name of the scene the item is in * @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item * @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemEnabled | Boolean | Whether the scene item is locked * @dataField sceneItemLocked | Boolean | Whether the scene item is locked
* *
* @eventType SceneItemLockStateChanged * @eventType SceneItemLockStateChanged
* @eventSubscription SceneItems * @eventSubscription SceneItems
@ -172,7 +172,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
*/ */
void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data) void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data)
{ {
auto eventHandler = 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)
@ -191,6 +191,38 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData); eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData);
} }
/**
* A scene item has been selected in the Ui.
*
* @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
*
* @eventType SceneItemSelected
* @eventSubscription SceneItems
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category scene items
*/
void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_scene_t *scene = GetCalldataPointer<obs_scene_t>(data, "scene");
if (!scene)
return;
obs_sceneitem_t *sceneItem = GetCalldataPointer<obs_sceneitem_t>(data, "item");
if (!sceneItem)
return;
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemSelected", eventData);
}
/** /**
* The transform/crop of a scene item has changed. * The transform/crop of a scene item has changed.
* *
@ -208,7 +240,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

@ -18,3 +18,130 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include "EventHandler.h" #include "EventHandler.h"
/**
* The current scene transition has changed.
*
* @dataField transitionName | String | Name of the new transition
*
* @eventType CurrentSceneTransitionChanged
* @eventSubscription Transitions
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category transitions
*/
void EventHandler::HandleCurrentSceneTransitionChanged()
{
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
json eventData;
eventData["transitionName"] = obs_source_get_name(transition);
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionChanged", eventData);
}
/**
* The current scene transition duration has changed.
*
* @dataField transitionDuration | Number | Transition duration in milliseconds
*
* @eventType CurrentSceneTransitionDurationChanged
* @eventSubscription Transitions
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category transitions
*/
void EventHandler::HandleCurrentSceneTransitionDurationChanged()
{
json eventData;
eventData["transitionDuration"] = obs_frontend_get_transition_duration();
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionDurationChanged", eventData);
}
/**
* A scene transition has started.
*
* @dataField transitionName | String | Scene transition name
*
* @eventType SceneTransitionStarted
* @eventSubscription Transitions
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category transitions
*/
void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source)
return;
json eventData;
eventData["transitionName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionStarted", eventData);
}
/**
* A scene transition has completed fully.
*
* Note: Does not appear to trigger when the transition is interrupted by the user.
*
* @dataField transitionName | String | Scene transition name
*
* @eventType SceneTransitionEnded
* @eventSubscription Transitions
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category transitions
*/
void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source)
return;
json eventData;
eventData["transitionName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionEnded", eventData);
}
/**
* A scene transition's video has completed fully.
*
* Useful for stinger transitions to tell when the video *actually* ends.
* `SceneTransitionEnded` only signifies the cut point, not the completion of transition playback.
*
* Note: Appears to be called by every transition, regardless of relevance.
*
* @dataField transitionName | String | Scene transition name
*
* @eventType SceneTransitionVideoEnded
* @eventSubscription Transitions
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api events
* @category transitions
*/
void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler*>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source)
return;
json eventData;
eventData["transitionName"] = obs_source_get_name(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionVideoEnded", eventData);
}

View File

@ -151,7 +151,7 @@
<number>65534</number> <number>65534</number>
</property> </property>
<property name="value"> <property name="value">
<number>4444</number> <number>4455</number>
</property> </property>
</widget> </widget>
</item> </item>

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},
@ -47,14 +51,18 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"SetVideoSettings", &RequestHandler::SetVideoSettings}, {"SetVideoSettings", &RequestHandler::SetVideoSettings},
{"GetStreamServiceSettings", &RequestHandler::GetStreamServiceSettings}, {"GetStreamServiceSettings", &RequestHandler::GetStreamServiceSettings},
{"SetStreamServiceSettings", &RequestHandler::SetStreamServiceSettings}, {"SetStreamServiceSettings", &RequestHandler::SetStreamServiceSettings},
{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
// Sources // Sources
{"GetSourceActive", &RequestHandler::GetSourceActive}, {"GetSourceActive", &RequestHandler::GetSourceActive},
{"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot}, {"GetSourceScreenshot", &RequestHandler::GetSourceScreenshot},
{"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot}, {"SaveSourceScreenshot", &RequestHandler::SaveSourceScreenshot},
{"GetSourcePrivateSettings", &RequestHandler::GetSourcePrivateSettings},
{"SetSourcePrivateSettings", &RequestHandler::SetSourcePrivateSettings},
// Scenes // Scenes
{"GetSceneList", &RequestHandler::GetSceneList}, {"GetSceneList", &RequestHandler::GetSceneList},
{"GetGroupList", &RequestHandler::GetGroupList},
{"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene}, {"GetCurrentProgramScene", &RequestHandler::GetCurrentProgramScene},
{"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene}, {"SetCurrentProgramScene", &RequestHandler::SetCurrentProgramScene},
{"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene}, {"GetCurrentPreviewScene", &RequestHandler::GetCurrentPreviewScene},
@ -62,12 +70,15 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"CreateScene", &RequestHandler::CreateScene}, {"CreateScene", &RequestHandler::CreateScene},
{"RemoveScene", &RequestHandler::RemoveScene}, {"RemoveScene", &RequestHandler::RemoveScene},
{"SetSceneName", &RequestHandler::SetSceneName}, {"SetSceneName", &RequestHandler::SetSceneName},
{"GetSceneSceneTransitionOverride", &RequestHandler::GetSceneSceneTransitionOverride},
{"SetSceneSceneTransitionOverride", &RequestHandler::SetSceneSceneTransitionOverride},
// Inputs // Inputs
{"GetInputList", &RequestHandler::GetInputList}, {"GetInputList", &RequestHandler::GetInputList},
{"GetInputKindList", &RequestHandler::GetInputKindList}, {"GetInputKindList", &RequestHandler::GetInputKindList},
{"GetSpecialInputs", &RequestHandler::GetSpecialInputs},
{"CreateInput", &RequestHandler::CreateInput}, {"CreateInput", &RequestHandler::CreateInput},
//{"RemoveInput", &RequestHandler::RemoveInput}, // Disabled for now. Pending obs-studio#5276 {"RemoveInput", &RequestHandler::RemoveInput},
{"SetInputName", &RequestHandler::SetInputName}, {"SetInputName", &RequestHandler::SetInputName},
{"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings}, {"GetInputDefaultSettings", &RequestHandler::GetInputDefaultSettings},
{"GetInputSettings", &RequestHandler::GetInputSettings}, {"GetInputSettings", &RequestHandler::GetInputSettings},
@ -77,10 +88,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},
@ -91,7 +106,20 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition}, {"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
{"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration}, {"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings}, {"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
{"GetCurrentSceneTransitionCursor", &RequestHandler::GetCurrentSceneTransitionCursor},
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition}, {"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
{"SetTBarPosition", &RequestHandler::SetTBarPosition},
// Filters
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
{"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings},
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
{"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter},
{"SetSourceFilterName", &RequestHandler::SetSourceFilterName},
{"GetSourceFilter", &RequestHandler::GetSourceFilter},
{"SetSourceFilterIndex", &RequestHandler::SetSourceFilterIndex},
{"SetSourceFilterSettings", &RequestHandler::SetSourceFilterSettings},
{"SetSourceFilterEnabled", &RequestHandler::SetSourceFilterEnabled},
// Scene Items // Scene Items
{"GetSceneItemList", &RequestHandler::GetSceneItemList}, {"GetSceneItemList", &RequestHandler::GetSceneItemList},
@ -108,12 +136,27 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked}, {"SetSceneItemLocked", &RequestHandler::SetSceneItemLocked},
{"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex}, {"GetSceneItemIndex", &RequestHandler::GetSceneItemIndex},
{"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex}, {"SetSceneItemIndex", &RequestHandler::SetSceneItemIndex},
{"GetSceneItemBlendMode", &RequestHandler::GetSceneItemBlendMode},
{"SetSceneItemBlendMode", &RequestHandler::SetSceneItemBlendMode},
// Outputs
{"GetVirtualCamStatus", &RequestHandler::GetVirtualCamStatus},
{"ToggleVirtualCam", &RequestHandler::ToggleVirtualCam},
{"StartVirtualCam", &RequestHandler::StartVirtualCam},
{"StopVirtualCam", &RequestHandler::StopVirtualCam},
{"GetReplayBufferStatus", &RequestHandler::GetReplayBufferStatus},
{"ToggleReplayBuffer", &RequestHandler::ToggleReplayBuffer},
{"StartReplayBuffer", &RequestHandler::StartReplayBuffer},
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
{"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
// Stream // Stream
{"GetStreamStatus", &RequestHandler::GetStreamStatus}, {"GetStreamStatus", &RequestHandler::GetStreamStatus},
{"ToggleStream", &RequestHandler::ToggleStream}, {"ToggleStream", &RequestHandler::ToggleStream},
{"StartStream", &RequestHandler::StartStream}, {"StartStream", &RequestHandler::StartStream},
{"StopStream", &RequestHandler::StopStream}, {"StopStream", &RequestHandler::StopStream},
{"SendStreamCaption", &RequestHandler::SendStreamCaption},
// Record // Record
{"GetRecordStatus", &RequestHandler::GetRecordStatus}, {"GetRecordStatus", &RequestHandler::GetRecordStatus},
@ -123,7 +166,6 @@ const std::map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause}, {"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
{"PauseRecord", &RequestHandler::PauseRecord}, {"PauseRecord", &RequestHandler::PauseRecord},
{"ResumeRecord", &RequestHandler::ResumeRecord}, {"ResumeRecord", &RequestHandler::ResumeRecord},
//{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
// Media Inputs // Media Inputs
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus}, {"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},
@ -134,6 +176,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 +188,10 @@ RequestHandler::RequestHandler(SessionPtr session) :
RequestResult RequestHandler::ProcessRequest(const Request& request) RequestResult RequestHandler::ProcessRequest(const Request& request)
{ {
#ifdef PLUGIN_TESTS
ScopeProfiler prof{"obs_websocket_request_processing"};
#endif
if (!request.RequestData.is_object() && !request.RequestData.is_null()) if (!request.RequestData.is_object() && !request.RequestData.is_null())
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object."); return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");

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>
@ -69,14 +69,18 @@ class RequestHandler {
RequestResult SetVideoSettings(const Request&); RequestResult SetVideoSettings(const Request&);
RequestResult GetStreamServiceSettings(const Request&); RequestResult GetStreamServiceSettings(const Request&);
RequestResult SetStreamServiceSettings(const Request&); RequestResult SetStreamServiceSettings(const Request&);
RequestResult GetRecordDirectory(const Request&);
// Sources // Sources
RequestResult GetSourceActive(const Request&); RequestResult GetSourceActive(const Request&);
RequestResult GetSourceScreenshot(const Request&); RequestResult GetSourceScreenshot(const Request&);
RequestResult SaveSourceScreenshot(const Request&); RequestResult SaveSourceScreenshot(const Request&);
RequestResult GetSourcePrivateSettings(const Request&);
RequestResult SetSourcePrivateSettings(const Request&);
// Scenes // Scenes
RequestResult GetSceneList(const Request&); RequestResult GetSceneList(const Request&);
RequestResult GetGroupList(const Request&);
RequestResult GetCurrentProgramScene(const Request&); RequestResult GetCurrentProgramScene(const Request&);
RequestResult SetCurrentProgramScene(const Request&); RequestResult SetCurrentProgramScene(const Request&);
RequestResult GetCurrentPreviewScene(const Request&); RequestResult GetCurrentPreviewScene(const Request&);
@ -84,10 +88,13 @@ class RequestHandler {
RequestResult CreateScene(const Request&); RequestResult CreateScene(const Request&);
RequestResult RemoveScene(const Request&); RequestResult RemoveScene(const Request&);
RequestResult SetSceneName(const Request&); RequestResult SetSceneName(const Request&);
RequestResult GetSceneSceneTransitionOverride(const Request&);
RequestResult SetSceneSceneTransitionOverride(const Request&);
// Inputs // Inputs
RequestResult GetInputList(const Request&); RequestResult GetInputList(const Request&);
RequestResult GetInputKindList(const Request&); RequestResult GetInputKindList(const Request&);
RequestResult GetSpecialInputs(const Request&);
RequestResult CreateInput(const Request&); RequestResult CreateInput(const Request&);
RequestResult RemoveInput(const Request&); RequestResult RemoveInput(const Request&);
RequestResult SetInputName(const Request&); RequestResult SetInputName(const Request&);
@ -99,10 +106,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&);
@ -113,7 +124,20 @@ class RequestHandler {
RequestResult SetCurrentSceneTransition(const Request&); RequestResult SetCurrentSceneTransition(const Request&);
RequestResult SetCurrentSceneTransitionDuration(const Request&); RequestResult SetCurrentSceneTransitionDuration(const Request&);
RequestResult SetCurrentSceneTransitionSettings(const Request&); RequestResult SetCurrentSceneTransitionSettings(const Request&);
RequestResult GetCurrentSceneTransitionCursor(const Request&);
RequestResult TriggerStudioModeTransition(const Request&); RequestResult TriggerStudioModeTransition(const Request&);
RequestResult SetTBarPosition(const Request&);
// Filters
RequestResult GetSourceFilterList(const Request&);
RequestResult GetSourceFilterDefaultSettings(const Request&);
RequestResult CreateSourceFilter(const Request&);
RequestResult RemoveSourceFilter(const Request&);
RequestResult SetSourceFilterName(const Request&);
RequestResult GetSourceFilter(const Request&);
RequestResult SetSourceFilterIndex(const Request&);
RequestResult SetSourceFilterSettings(const Request&);
RequestResult SetSourceFilterEnabled(const Request&);
// Scene Items // Scene Items
RequestResult GetSceneItemList(const Request&); RequestResult GetSceneItemList(const Request&);
@ -130,12 +154,27 @@ class RequestHandler {
RequestResult SetSceneItemLocked(const Request&); RequestResult SetSceneItemLocked(const Request&);
RequestResult GetSceneItemIndex(const Request&); RequestResult GetSceneItemIndex(const Request&);
RequestResult SetSceneItemIndex(const Request&); RequestResult SetSceneItemIndex(const Request&);
RequestResult GetSceneItemBlendMode(const Request&);
RequestResult SetSceneItemBlendMode(const Request&);
// Outputs
RequestResult GetVirtualCamStatus(const Request&);
RequestResult ToggleVirtualCam(const Request&);
RequestResult StartVirtualCam(const Request&);
RequestResult StopVirtualCam(const Request&);
RequestResult GetReplayBufferStatus(const Request&);
RequestResult ToggleReplayBuffer(const Request&);
RequestResult StartReplayBuffer(const Request&);
RequestResult StopReplayBuffer(const Request&);
RequestResult SaveReplayBuffer(const Request&);
RequestResult GetLastReplayBufferReplay(const Request&);
// Stream // Stream
RequestResult GetStreamStatus(const Request&); RequestResult GetStreamStatus(const Request&);
RequestResult ToggleStream(const Request&); RequestResult ToggleStream(const Request&);
RequestResult StartStream(const Request&); RequestResult StartStream(const Request&);
RequestResult StopStream(const Request&); RequestResult StopStream(const Request&);
RequestResult SendStreamCaption(const Request&);
// Record // Record
RequestResult GetRecordStatus(const Request&); RequestResult GetRecordStatus(const Request&);
@ -145,7 +184,6 @@ class RequestHandler {
RequestResult ToggleRecordPause(const Request&); RequestResult ToggleRecordPause(const Request&);
RequestResult PauseRecord(const Request&); RequestResult PauseRecord(const Request&);
RequestResult ResumeRecord(const Request&); RequestResult ResumeRecord(const Request&);
RequestResult GetRecordDirectory(const Request&);
// Media Inputs // Media Inputs
RequestResult GetMediaInputStatus(const Request&); RequestResult GetMediaInputStatus(const Request&);
@ -156,7 +194,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();
@ -408,6 +408,8 @@ RequestResult RequestHandler::SetProfileParameter(const Request& request)
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string."); return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string.");
} }
config_save(profile);
return RequestResult::Success(); return RequestResult::Success();
} }
@ -592,3 +594,23 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the current directory that the record output is set to.
*
* @responseField recordDirectory | String | Output directory
*
* @requestType GetRecordDirectory
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category rconfig
*/
RequestResult RequestHandler::GetRecordDirectory(const Request&)
{
json responseData;
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
return RequestResult::Success(responseData);
}

View File

@ -18,3 +18,317 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include "RequestHandler.h" #include "RequestHandler.h"
/**
* Gets an array of all of a source's filters.
*
* @requestField sourceName | String | Name of the source
*
* @responseField filters | Array<Object> | Array of filters
*
* @requestType GetSourceFilterList
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::GetSourceFilterList(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if(!source)
return RequestResult::Error(statusCode, comment);
json responseData;
responseData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
return RequestResult::Success(responseData);
}
/**
* Gets the default settings for a filter kind.
*
* @requestField filterKind | String | Filter kind to get the default settings for
*
* @responseField defaultFilterSettings | Object | Object of default settings for the filter kind
*
* @requestType GetSourceFilterDefaultSettings
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
if (!request.ValidateString("filterKind", statusCode, comment))
return RequestResult::Error(statusCode, comment);
std::string filterKind = request.RequestData["filterKind"];
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidFilterKind);
OBSDataAutoRelease defaultSettings = obs_get_source_defaults(filterKind.c_str());
if (!defaultSettings)
return RequestResult::Error(RequestStatus::InvalidFilterKind);
json responseData;
responseData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true);
return RequestResult::Success(responseData);
}
/**
* Creates a new filter, adding it to the specified source.
*
* @requestField sourceName | String | Name of the source to add the filter to
* @requestField filterName | String | Name of the new filter to be created
* @requestField filterKind | String | The kind of filter to be created
* @requestField ?filterSettings | Object | Settings object to initialize the filter with | Default settings used
*
* @requestType CreateSourceFilter
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::CreateSourceFilter(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!(source && request.ValidateString("filterName", statusCode, comment) && request.ValidateString("filterKind", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
std::string filterName = request.RequestData["filterName"];
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(source, filterName.c_str());
if (existingFilter)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that name.");
std::string filterKind = request.RequestData["filterKind"];
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidFilterKind, "Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
OBSDataAutoRelease filterSettings = nullptr;
if (request.Contains("filterSettings")) {
if (!request.ValidateOptionalObject("filterSettings", statusCode, comment, true))
return RequestResult::Error(statusCode, comment);
filterSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
}
OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings);
if(!filter)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed.");
return RequestResult::Success();
}
/**
* Removes a filter from a source.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Name of the filter to remove
*
* @requestType RemoveSourceFilter
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::RemoveSourceFilter(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!pair.filter)
return RequestResult::Error(statusCode, comment);
obs_source_filter_remove(pair.source, pair.filter);
return RequestResult::Success();
}
/**
* Sets the name of a source filter (rename).
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Current name of the filter
* @requestField newFilterName | String | New name for the filter
*
* @requestType SetSourceFilterName
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::SetSourceFilterName(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!pair.filter || !request.ValidateString("newFilterName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
std::string newFilterName = request.RequestData["newFilterName"];
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(pair.source, newFilterName.c_str());
if (existingFilter)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that new name.");
obs_source_set_name(pair.filter, newFilterName.c_str());
return RequestResult::Success();
}
/**
* Gets the info for a specific source filter.
*
* @requestField sourceName | String | Name of the source
* @requestField filterName | String | Name of the filter
*
* @responseField filterEnabled | Boolean | Whether the filter is enabled
* @responseField filterIndex | Number | Index of the filter in the list, beginning at 0
* @responseField filterKind | String | The kind of filter
* @responseField filterSettings | Object | Settings object associated with the filter
*
* @requestType GetSourceFilter
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::GetSourceFilter(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!pair.filter)
return RequestResult::Error(statusCode, comment);
json responseData;
responseData["filterEnabled"] = obs_source_enabled(pair.filter);
responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
responseData["filterKind"] = obs_source_get_id(pair.filter);
OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
responseData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
return RequestResult::Success(responseData);
}
/**
* Sets the index position of a filter on a source.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Name of the filter
* @requestField filterIndex | Number | New index position of the filter | >= 0
*
* @requestType SetSourceFilterIndex
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::SetSourceFilterIndex(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode, comment, 0, 8192)))
return RequestResult::Error(statusCode, comment);
int filterIndex = request.RequestData["filterIndex"];
Utils::Obs::ActionHelper::SetSourceFilterIndex(pair.source, pair.filter, filterIndex);
return RequestResult::Success();
}
/**
* Sets the settings of a source filter.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Name of the filter to set the settings of
* @requestField filterSettings | Object | Object of settings to apply
* @requestField ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | true
*
* @requestType SetSourceFilterSettings
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::SetSourceFilterSettings(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!(pair.filter && request.ValidateObject("filterSettings", statusCode, comment, true)))
return RequestResult::Error(statusCode, comment);
// Almost identical to SetInputSettings
bool overlay = true;
if (request.Contains("overlay")) {
if (!request.ValidateOptionalBoolean("overlay", statusCode, comment))
return RequestResult::Error(statusCode, comment);
overlay = request.RequestData["overlay"];
}
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
if (!newSettings)
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
if (overlay)
obs_source_update(pair.filter, newSettings);
else
obs_source_reset_settings(pair.filter, newSettings);
obs_source_update_properties(pair.filter);
return RequestResult::Success();
}
/**
* Sets the enable state of a source filter.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | Number | Name of the filter
* @requestField filterEnabled | Boolean | New enable state of the filter
*
* @requestType SetSourceFilterEnabled
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::SetSourceFilterEnabled(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!(pair.filter && request.ValidateBoolean("filterEnabled", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
bool filterEnabled = request.RequestData["filterEnabled"];
obs_source_set_enabled(pair.filter, filterEnabled);
return RequestResult::Success();
}

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.
* *
@ -232,6 +269,9 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request& request)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
std::string inputKind = request.RequestData["inputKind"]; std::string inputKind = request.RequestData["inputKind"];
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidInputKind);
OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str()); OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str());
if (!defaultSettings) if (!defaultSettings)
@ -346,6 +386,9 @@ RequestResult RequestHandler::GetInputMute(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData; json responseData;
responseData["inputMuted"] = obs_source_muted(input); responseData["inputMuted"] = obs_source_muted(input);
return RequestResult::Success(responseData); return RequestResult::Success(responseData);
@ -372,6 +415,9 @@ RequestResult RequestHandler::SetInputMute(const Request& request)
if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment))) if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
obs_source_set_muted(input, request.RequestData["inputMuted"]); obs_source_set_muted(input, request.RequestData["inputMuted"]);
return RequestResult::Success(); return RequestResult::Success();
@ -399,6 +445,9 @@ RequestResult RequestHandler::ToggleInputMute(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
bool inputMuted = !obs_source_muted(input); bool inputMuted = !obs_source_muted(input);
obs_source_set_muted(input, inputMuted); obs_source_set_muted(input, inputMuted);
@ -430,6 +479,9 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
float inputVolumeMul = obs_source_get_volume(input); float inputVolumeMul = obs_source_get_volume(input);
float inputVolumeDb = obs_mul_to_db(inputVolumeMul); float inputVolumeDb = obs_mul_to_db(inputVolumeMul);
if (inputVolumeDb == -INFINITY) if (inputVolumeDb == -INFINITY)
@ -446,7 +498,7 @@ RequestResult RequestHandler::GetInputVolume(const Request& request)
* *
* @requestField inputName | String | Name of the input to set the volume of * @requestField inputName | String | Name of the input to set the volume of
* @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified * @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified
* @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= -26 | `inputVolumeMul` should be specified * @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= 26 | `inputVolumeMul` should be specified
* *
* @requestType SetInputVolume * @requestType SetInputVolume
* @complexity 3 * @complexity 3
@ -463,6 +515,9 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
bool hasMul = request.Contains("inputVolumeMul"); bool hasMul = request.Contains("inputVolumeMul");
if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20)) if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
@ -488,6 +543,67 @@ RequestResult RequestHandler::SetInputVolume(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the audio balance of an input.
*
* @requestField inputName | String | Name of the input to get the audio balance of
*
* @responseField inputAudioBalance | Number | Audio balance value from 0.0-1.0
*
* @requestType GetInputAudioBalance
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::GetInputAudioBalance(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData;
responseData["inputAudioBalance"] = obs_source_get_balance_value(input);
return RequestResult::Success(responseData);
}
/**
* Sets the audio balance of an input.
*
* @requestField inputName | String | Name of the input to set the audio balance of
* @requestField inputAudioBalance | Number | New audio balance value | >= 0.0, <= 1.0
*
* @requestType SetInputAudioBalance
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category inputs
*/
RequestResult RequestHandler::SetInputAudioBalance(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0)))
return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
float inputAudioBalance = request.RequestData["inputAudioBalance"];
obs_source_set_balance_value(input, inputAudioBalance);
return RequestResult::Success();
}
/** /**
* Gets the audio sync offset of an input. * Gets the audio sync offset of an input.
* *
@ -512,6 +628,9 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request& request)
if (!input) if (!input)
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
json responseData; json responseData;
// Offset is stored in nanoseconds in OBS. // Offset is stored in nanoseconds in OBS.
responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000; responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000;
@ -540,6 +659,9 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request& request)
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000))) if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
return RequestResult::Error(statusCode, comment); return RequestResult::Error(statusCode, comment);
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO))
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
int64_t syncOffset = request.RequestData["inputAudioSyncOffset"]; int64_t syncOffset = request.RequestData["inputAudioSyncOffset"];
obs_source_set_sync_offset(input, syncOffset * 1000000); obs_source_set_sync_offset(input, syncOffset * 1000000);
@ -573,6 +695,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 +725,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 +747,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,277 @@
/*
obs-websocket
Copyright (C) 2016-2021 Stephane Lepin <stephane.lepin@gmail.com>
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include "RequestHandler.h"
static bool VirtualCamAvailable()
{
OBSDataAutoRelease privateData = obs_get_private_data();
if (!privateData)
return false;
return obs_data_get_bool(privateData, "vcamEnabled");
}
static bool ReplayBufferAvailable()
{
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
return output != nullptr;
}
/**
* Gets the status of the virtualcam output.
*
* @responseField outputActive | Boolean | Whether the output is active
*
* @requestType GetVirtualCamStatus
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category outputs
* @api requests
*/
RequestResult RequestHandler::GetVirtualCamStatus(const Request&)
{
if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
json responseData;
responseData["outputActive"] = obs_frontend_virtualcam_active();
return RequestResult::Success(responseData);
}
/**
* Toggles the state of the virtualcam output.
*
* @responseField outputActive | Boolean | Whether the output is active
*
* @requestType ToggleVirtualCam
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category outputs
* @api requests
*/
RequestResult RequestHandler::ToggleVirtualCam(const Request&)
{
if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
bool outputActive = obs_frontend_virtualcam_active();
if (outputActive)
obs_frontend_stop_virtualcam();
else
obs_frontend_start_virtualcam();
json responseData;
responseData["outputActive"] = !outputActive;
return RequestResult::Success(responseData);
}
/**
* Starts the virtualcam output.
*
* @requestType StartVirtualCam
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StartVirtualCam(const Request&)
{
if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
if (obs_frontend_virtualcam_active())
return RequestResult::Error(RequestStatus::OutputRunning);
obs_frontend_start_virtualcam();
return RequestResult::Success();
}
/**
* Stops the virtualcam output.
*
* @requestType StopVirtualCam
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StopVirtualCam(const Request&)
{
if (!VirtualCamAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "VirtualCam is not available.");
if (!obs_frontend_virtualcam_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
obs_frontend_stop_virtualcam();
return RequestResult::Success();
}
/**
* Gets the status of the replay buffer output.
*
* @responseField outputActive | Boolean | Whether the output is active
*
* @requestType GetReplayBufferStatus
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category outputs
* @api requests
*/
RequestResult RequestHandler::GetReplayBufferStatus(const Request&)
{
if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
json responseData;
responseData["outputActive"] = obs_frontend_replay_buffer_active();
return RequestResult::Success(responseData);
}
/**
* Toggles the state of the replay buffer output.
*
* @responseField outputActive | Boolean | Whether the output is active
*
* @requestType ToggleReplayBuffer
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @category outputs
* @api requests
*/
RequestResult RequestHandler::ToggleReplayBuffer(const Request&)
{
if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
bool outputActive = obs_frontend_replay_buffer_active();
if (outputActive)
obs_frontend_replay_buffer_stop();
else
obs_frontend_replay_buffer_start();
json responseData;
responseData["outputActive"] = !outputActive;
return RequestResult::Success(responseData);
}
/**
* Starts the replay buffer output.
*
* @requestType StartReplayBuffer
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StartReplayBuffer(const Request&)
{
if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
if (obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputRunning);
obs_frontend_replay_buffer_start();
return RequestResult::Success();
}
/**
* Stops the replay buffer output.
*
* @requestType StopReplayBuffer
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StopReplayBuffer(const Request&)
{
if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
if (!obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
obs_frontend_replay_buffer_stop();
return RequestResult::Success();
}
/**
* Saves the contents of the replay buffer output.
*
* @requestType SaveReplayBuffer
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::SaveReplayBuffer(const Request&)
{
if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
if (!obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
obs_frontend_replay_buffer_save();
return RequestResult::Success();
}
/**
* Gets the filename of the last replay buffer save file.
*
* @responseField savedReplayPath | String | File path
*
* @requestType GetLastReplayBufferReplay
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetLastReplayBufferReplay(const Request&)
{
if (!ReplayBufferAvailable())
return RequestResult::Error(RequestStatus::InvalidResourceState, "Replay buffer is not available.");
if (!obs_frontend_replay_buffer_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
json responseData;
responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
return RequestResult::Success(responseData);
}

View File

@ -182,23 +182,3 @@ RequestResult RequestHandler::ResumeRecord(const Request&)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the current directory that the record output is set to.
*
* @responseField recordDirectory | String | Output directory
*
* @requestType GetRecordDirectory
* @complexity 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category record
*/
RequestResult RequestHandler::GetRecordDirectory(const Request&)
{
json responseData;
responseData["recordDirectory"] = Utils::Obs::StringHelper::GetCurrentRecordOutputPath();
return RequestResult::Success(responseData);
}

View File

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

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.
* *
@ -250,3 +273,105 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the scene transition overridden for a scene.
*
* @requestField sceneName | String | Name of the scene
*
* @responseField transitionName | String | Name of the overridden scene transition, else `null`
* @responseField transitionDuration | Number | Duration of the overridden scene transition, else `null`
*
* @requestType GetSceneSceneTransitionOverride
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category scenes
*/
RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
json responseData;
const char *transitionName = obs_data_get_string(privateSettings, "transition");
if (transitionName && strlen(transitionName))
responseData["transitionName"] = transitionName;
else
responseData["transitionName"] = nullptr;
if (obs_data_has_user_value(privateSettings, "transition_duration"))
responseData["transitionDuration"] = obs_data_get_int(privateSettings, "transition_duration");
else
responseData["transitionDuration"] = nullptr;
return RequestResult::Success(responseData);
}
/**
* Gets the scene transition overridden for a scene.
*
* @requestField sceneName | String | Name of the scene
* @requestField ?transitionName | String | Name of the scene transition to use as override. Specify `null` to remove | Unchanged
* @requestField ?transitionDuration | Number | Duration to use for any overridden transition. Specify `null` to remove | >= 50, <= 20000 | Unchanged
*
* @requestType SetSceneSceneTransitionOverride
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category scenes
*/
RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
OBSDataAutoRelease privateSettings = obs_source_get_private_settings(scene);
bool hasName = request.RequestData.contains("transitionName");
if (hasName && !request.RequestData["transitionName"].is_null()) {
if (!request.ValidateOptionalString("transitionName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
if (!transition)
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
}
bool hasDuration = request.RequestData.contains("transitionDuration");
if (hasDuration && !request.RequestData["transitionDuration"].is_null()) {
if (!request.ValidateOptionalNumber("transitionDuration", statusCode, comment, 50, 20000))
return RequestResult::Error(statusCode, comment);
}
if (!hasName && !hasDuration)
return RequestResult::Error(RequestStatus::MissingRequestField, "Your request data must include either `transitionName` or `transitionDuration`.");
if (hasName) {
if (request.RequestData["transitionName"].is_null()) {
obs_data_erase(privateSettings, "transition");
} else {
std::string transitionName = request.RequestData["transitionName"];
obs_data_set_string(privateSettings, "transition", transitionName.c_str());
}
}
if (hasDuration) {
if (request.RequestData["transitionDuration"].is_null()) {
obs_data_erase(privateSettings, "transition_duration");
} else {
obs_data_set_int(privateSettings, "transition_duration", request.RequestData["transitionDuration"]);
}
}
return RequestResult::Success();
}

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

@ -122,3 +122,35 @@ RequestResult RequestHandler::StopStream(const Request&)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Sends CEA-608 caption text over the stream output.
*
* @requestField captionText | String | Caption text
*
* @requestType SendStreamCaption
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @category stream
* @api requests
*/
RequestResult RequestHandler::SendStreamCaption(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
if (!request.ValidateString("captionText", statusCode, comment, true))
return RequestResult::Error(statusCode, comment);
if (!obs_frontend_streaming_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
std::string captionText = request.RequestData["captionText"];
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
// 0.0 means no delay until the next caption can be sent
obs_output_output_caption_text2(output, captionText.c_str(), 0.0);
return RequestResult::Success();
}

View File

@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <math.h>
#include "RequestHandler.h" #include "RequestHandler.h"
/** /**
@ -226,6 +228,32 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Gets the cursor position of the current scene transition.
*
* Note: `transitionCursor` will return 1.0 when the transition is inactive.
*
* @responseField transitionCursor | Number | Cursor position, between 0.0 and 1.0
*
* @requestType GetCurrentSceneTransitionCursor
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category transitions
*/
RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request&)
{
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition)
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
json responseData;
responseData["transitionCursor"] = obs_transition_get_time(transition);
return RequestResult::Success(responseData);
}
/** /**
* Triggers the current scene transition. Same functionality as the `Transition` button in studio mode. * Triggers the current scene transition. Same functionality as the `Transition` button in studio mode.
* *
@ -247,3 +275,48 @@ RequestResult RequestHandler::TriggerStudioModeTransition(const Request&)
return RequestResult::Success(); return RequestResult::Success();
} }
/**
* Sets the position of the TBar.
*
* **Very important note**: This will be deprecated and replaced in a future version of obs-websocket.
*
* @requestField position | Number | New position | >= 0.0, <= 1.0
* @requestField ?release | Boolean | Whether to release the TBar. Only set `false` if you know that you will be sending another position update | `true`
*
* @requestType SetTBarPosition
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category transitions
*/
RequestResult RequestHandler::SetTBarPosition(const Request& request)
{
if (!obs_frontend_preview_program_mode_active())
return RequestResult::Error(RequestStatus::StudioModeNotActive);
RequestStatus::RequestStatus statusCode;
std::string comment;
if (!request.ValidateNumber("position", statusCode, comment, 0.0, 1.0))
return RequestResult::Error(statusCode, comment);
bool release = true;
if (request.Contains("release")) {
if (!request.ValidateOptionalBoolean("release", statusCode, comment))
return RequestResult::Error(statusCode, comment);
}
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (!transition)
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
float position = request.RequestData["position"];
obs_frontend_set_tbar_position((int)round(position * 1024.0));
if (release)
obs_frontend_release_tbar();
return RequestResult::Success();
}

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

@ -264,18 +264,14 @@ obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::
comment = "The specified source is not a scene. (Is group)"; comment = "The specified source is not a scene. (Is group)";
return nullptr; return nullptr;
} }
OBSScene ret = obs_group_from_source(sceneSource); return obs_scene_get_ref(obs_group_from_source(sceneSource));
obs_scene_addref(ret);
return ret;
} else { } else {
if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY) { if (filter == OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY) {
statusCode = RequestStatus::InvalidResourceType; statusCode = RequestStatus::InvalidResourceType;
comment = "The specified source is not a group. (Is scene)"; comment = "The specified source is not a group. (Is scene)";
return nullptr; return nullptr;
} }
OBSScene ret = obs_scene_from_source(sceneSource); return obs_scene_get_ref(obs_scene_from_source(sceneSource));
obs_scene_addref(ret);
return ret;
} }
} }
@ -295,6 +291,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

@ -22,7 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <stdint.h> #include <stdint.h>
namespace RequestBatchExecutionType { namespace RequestBatchExecutionType {
enum RequestBatchExecutionType { enum RequestBatchExecutionType: int8_t {
/** /**
* Not a request batch. * Not a request batch.
* *
@ -77,7 +77,7 @@ namespace RequestBatchExecutionType {
Parallel = 2, Parallel = 2,
}; };
inline bool IsValid(int executionType) inline bool IsValid(int8_t executionType)
{ {
return executionType >= None && executionType <= Parallel; return executionType >= None && executionType <= Parallel;
} }

View File

@ -345,6 +345,17 @@ namespace RequestStatus {
* @api enums * @api enums
*/ */
ResourceNotConfigurable = 606, ResourceNotConfigurable = 606,
/**
* The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind.
*
* @enumIdentifier InvalidFilterKind
* @enumValue 607
* @enumType RequestStatus
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
InvalidFilterKind = 607,
/** /**
* Creating the resource failed. * Creating the resource failed.

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,12 +186,15 @@ namespace Utils {
std::vector<obs_hotkey_t *> GetHotkeyList(); std::vector<obs_hotkey_t *> GetHotkeyList();
std::vector<std::string> GetHotkeyNameList(); std::vector<std::string> GetHotkeyNameList();
std::vector<json> GetSceneList(); std::vector<json> GetSceneList();
std::vector<std::string> GetGroupList();
std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false); std::vector<json> GetSceneItemList(obs_scene_t *scene, bool basic = false);
std::vector<json> GetInputList(std::string inputKind = ""); std::vector<json> GetInputList(std::string inputKind = "");
std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false); std::vector<std::string> GetInputKindList(bool unversioned = false, bool includeDisabled = false);
std::vector<json> GetListPropertyItems(obs_property_t *property); std::vector<json> GetListPropertyItems(obs_property_t *property);
std::vector<std::string> GetTransitionKindList(); std::vector<std::string> GetTransitionKindList();
std::vector<json> GetSceneTransitionList(); std::vector<json> GetSceneTransitionList();
std::vector<json> GetSourceFilterList(obs_source_t *source);
std::vector<std::string> GetFilterKindList();
} }
namespace ObjectHelper { namespace ObjectHelper {
@ -205,6 +211,8 @@ namespace Utils {
namespace ActionHelper { namespace ActionHelper {
obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true, obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true, obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
obs_source_t *CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
} }
} }
} }

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)
@ -87,3 +87,30 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
return ret; return ret;
} }
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings)
{
obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
if (!filter)
return nullptr;
obs_source_filter_add(source, filter);
return filter;
}
void Utils::Obs::ActionHelper::SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index)
{
size_t currentIndex = Utils::Obs::NumberHelper::GetSourceFilterIndex(source, filter);
obs_order_movement direction = index > currentIndex ? OBS_ORDER_MOVE_DOWN : OBS_ORDER_MOVE_UP;
while(currentIndex != index) {
obs_source_filter_set_order(source, filter, direction);
if (direction == OBS_ORDER_MOVE_DOWN)
currentIndex++;
else
currentIndex--;
}
}

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);
@ -88,12 +88,10 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
obs_frontend_get_scenes(&sceneList); obs_frontend_get_scenes(&sceneList);
std::vector<json> ret; std::vector<json> ret;
ret.reserve(sceneList.sources.num);
for (size_t i = 0; i < sceneList.sources.num; i++) { for (size_t i = 0; i < sceneList.sources.num; i++) {
obs_source_t *scene = sceneList.sources.array[i]; obs_source_t *scene = sceneList.sources.array[i];
if (obs_source_is_group(scene))
continue;
json sceneJson; json sceneJson;
sceneJson["sceneName"] = obs_source_get_name(scene); sceneJson["sceneName"] = obs_source_get_name(scene);
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1; sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
@ -109,13 +107,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 +176,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);
@ -208,6 +226,8 @@ std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *
enum obs_combo_format itemFormat = obs_property_list_format(property); enum obs_combo_format itemFormat = obs_property_list_format(property);
size_t itemCount = obs_property_list_item_count(property); size_t itemCount = obs_property_list_item_count(property);
ret.reserve(itemCount);
for (size_t i = 0; i < itemCount; i++) { for (size_t i = 0; i < itemCount; i++) {
json itemData; json itemData;
itemData["itemName"] = obs_property_list_item_name(property, i); itemData["itemName"] = obs_property_list_item_name(property, i);
@ -245,6 +265,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
obs_frontend_get_transitions(&transitionList); obs_frontend_get_transitions(&transitionList);
std::vector<json> ret; std::vector<json> ret;
ret.reserve(transitionList.sources.num);
for (size_t i = 0; i < transitionList.sources.num; i++) { for (size_t i = 0; i < transitionList.sources.num; i++) {
obs_source_t *transition = transitionList.sources.array[i]; obs_source_t *transition = transitionList.sources.array[i];
json transitionJson; json transitionJson;
@ -259,3 +280,38 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
return ret; return ret;
} }
std::vector<std::string> Utils::Obs::ArrayHelper::GetFilterKindList()
{
std::vector<std::string> ret;
size_t idx = 0;
const char *kind;
while(obs_enum_filter_types(idx++, &kind))
ret.push_back(kind);
return ret;
}
std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *source)
{
std::vector<json> filters;
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
auto filters = reinterpret_cast<std::vector<json>*>(param);
json filterJson;
filterJson["filterEnabled"] = obs_source_enabled(filter);
filterJson["filterIndex"] = filters->size();
filterJson["filterKind"] = obs_source_get_id(filter);
filterJson["filterName"] = obs_source_get_name(filter);
OBSDataAutoRelease filterSettings = obs_source_get_settings(filter);
filterJson["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
filters->push_back(filterJson);
};
obs_source_enum_filters(source, enumFilters, &filters);
return filters;
}

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)
@ -122,12 +120,19 @@ std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath() std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
{ {
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output(); OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
if (!output)
return "";
calldata_t cd = {0}; calldata_t cd = {0};
proc_handler_t *ph = obs_output_get_proc_handler(output); proc_handler_t *ph = obs_output_get_proc_handler(output);
proc_handler_call(ph, "get_last_replay", &cd); proc_handler_call(ph, "get_last_replay", &cd);
auto ret = calldata_string(&cd, "path"); const char *savedReplayPath = calldata_string(&cd, "path");
calldata_free(&cd); calldata_free(&cd);
return ret;
if (!savedReplayPath)
return "";
return savedReplayPath;
} }
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type) std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
@ -144,6 +149,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

@ -59,8 +59,7 @@ Utils::Obs::VolumeMeter::Meter::~Meter()
bool Utils::Obs::VolumeMeter::Meter::InputValid() bool Utils::Obs::VolumeMeter::Meter::InputValid()
{ {
// return !obs_weak_source_expired(_input); return !obs_weak_source_expired(_input);
return true;
} }
json Utils::Obs::VolumeMeter::Meter::GetMeterData() json Utils::Obs::VolumeMeter::Meter::GetMeterData()

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>

View File

@ -233,7 +233,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
return; return;
} }
uint8_t requestedExecutionType = payloadData["executionType"]; int8_t requestedExecutionType = payloadData["executionType"];
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) { if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) {
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue; ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
ret.closeReason = "Your `executionType` has an invalid value."; ret.closeReason = "Your `executionType` has an invalid value.";