Compare commits

..

40 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
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
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
32 changed files with 2674 additions and 163 deletions

View File

@ -69,6 +69,7 @@ body:
label: obs-websocket Version
description: What version of obs-websocket are you using?
options:
- 5.0.0-beta1
- 5.0.0-alpha3
- 5.0.0-alpha2
- 4.9.1

View File

@ -5,7 +5,7 @@ on:
paths-ignore:
- 'docs/**'
branches:
- '*'
- master
tags:
- '[45].[0-9]+.[0-9]+*'
pull_request:
@ -18,7 +18,7 @@ on:
jobs:
windows:
name: 'Windows 32/64-bit'
runs-on: [windows-latest]
runs-on: [windows-2019]
if: contains(github.event.head_commit.message, '[skip ci]') != true
env:
QT_CACHE_VERSION: '2' # Change whenever updating OBS dependencies URL, in order to force a cache reset
@ -367,6 +367,7 @@ jobs:
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 }}
@ -374,6 +375,7 @@ jobs:
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 }}
@ -442,12 +444,12 @@ jobs:
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
-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
@ -462,13 +464,14 @@ jobs:
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
-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
@ -492,6 +495,7 @@ jobs:
@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: |

1
.gitignore vendored
View File

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

View File

@ -6,7 +6,7 @@
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)
[![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, ...)
### 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)
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
- 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).
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

View File

@ -405,6 +405,14 @@
"initialVersion": "5.0.0",
"enumValue": 606
},
{
"description": "The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind.",
"enumIdentifier": "InvalidFilterKind",
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"enumValue": 607
},
{
"description": "Creating the resource failed.",
"enumIdentifier": "ResourceCreationFailed",
@ -1129,6 +1137,183 @@
],
"responseFields": []
},
{
"description": "Gets the current directory that the record output is set to.",
"requestType": "GetRecordDirectory",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "rconfig",
"requestFields": [],
"responseFields": [
{
"valueName": "recordDirectory",
"valueType": "String",
"valueDescription": "Output directory"
}
]
},
{
"description": "Gets an array of all of a source's filters.",
"requestType": "GetSourceFilterList",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": [
{
"valueName": "filters",
"valueType": "Array<Object>",
"valueDescription": "Array of filters"
}
]
},
{
"description": "Gets the default settings for a filter kind.",
"requestType": "GetSourceFilterDefaultSettings",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "filterKind",
"valueType": "String",
"valueDescription": "Filter kind to get the default settings for",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": [
{
"valueName": "defaultFilterSettings",
"valueType": "Object",
"valueDescription": "Object of default settings for the filter kind"
}
]
},
{
"description": "Creates a new filter, adding it to the specified source.",
"requestType": "CreateSourceFilter",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source to add the filter to",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the new filter to be created",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterKind",
"valueType": "String",
"valueDescription": "The kind of filter to be created",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterSettings",
"valueType": "Object",
"valueDescription": "Settings object to initialize the filter with",
"valueRestrictions": null,
"valueOptional": true,
"valueOptionalBehavior": "Default settings used"
}
],
"responseFields": []
},
{
"description": "Removes a filter from a source.",
"requestType": "RemoveSourceFilter",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter is on",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the filter to remove",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": []
},
{
"description": "Sets the name of a source filter (rename).",
"requestType": "SetSourceFilterName",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter is on",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Current name of the filter",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "newFilterName",
"valueType": "String",
"valueDescription": "New name for the filter",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": []
},
{
"description": "Gets the info for a specific source filter.",
"requestType": "GetSourceFilter",
@ -1178,6 +1363,86 @@
}
]
},
{
"description": "Sets the index position of a filter on a source.",
"requestType": "SetSourceFilterIndex",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter is on",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the filter",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterIndex",
"valueType": "Number",
"valueDescription": "New index position of the filter",
"valueRestrictions": ">= 0",
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": []
},
{
"description": "Sets the settings of a source filter.",
"requestType": "SetSourceFilterSettings",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"requestFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter is on",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the filter to set the settings of",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "filterSettings",
"valueType": "Object",
"valueDescription": "Object of settings to apply",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "overlay",
"valueType": "Boolean",
"valueDescription": "True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings.",
"valueRestrictions": null,
"valueOptional": true,
"valueOptionalBehavior": "true"
}
],
"responseFields": []
},
{
"description": "Gets data about the current plugin and RPC version.",
"requestType": "GetVersion",
@ -2363,6 +2628,90 @@
"requestFields": [],
"responseFields": []
},
{
"description": "Gets the status of the replay buffer output.",
"requestType": "GetReplayBufferStatus",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "outputs",
"requestFields": [],
"responseFields": [
{
"valueName": "outputActive",
"valueType": "Boolean",
"valueDescription": "Whether the output is active"
}
]
},
{
"description": "Toggles the state of the replay buffer output.",
"requestType": "ToggleReplayBuffer",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "outputs",
"requestFields": [],
"responseFields": [
{
"valueName": "outputActive",
"valueType": "Boolean",
"valueDescription": "Whether the output is active"
}
]
},
{
"description": "Starts the replay buffer output.",
"requestType": "StartReplayBuffer",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "outputs",
"requestFields": [],
"responseFields": []
},
{
"description": "Stops the replay buffer output.",
"requestType": "StopReplayBuffer",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "outputs",
"requestFields": [],
"responseFields": []
},
{
"description": "Saves the contents of the replay buffer output.",
"requestType": "SaveReplayBuffer",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "outputs",
"requestFields": [],
"responseFields": []
},
{
"description": "Gets the filename of the last replay buffer save file.",
"requestType": "GetLastReplayBufferReplay",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "outputs",
"requestFields": [],
"responseFields": [
{
"valueName": "savedReplayPath",
"valueType": "String",
"valueDescription": "File path"
}
]
},
{
"description": "Gets the status of the record output.",
"requestType": "GetRecordStatus",
@ -2466,23 +2815,6 @@
"requestFields": [],
"responseFields": []
},
{
"description": "Gets the current directory that the record output is set to.",
"requestType": "GetRecordDirectory",
"complexity": 1,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "record",
"requestFields": [],
"responseFields": [
{
"valueName": "recordDirectory",
"valueType": "String",
"valueDescription": "Output directory"
}
]
},
{
"description": "Gets a list of all scene items in a scene.\n\nScenes only",
"requestType": "GetSceneItemList",
@ -3217,6 +3549,73 @@
],
"responseFields": []
},
{
"description": "Gets the scene transition overridden for a scene.",
"requestType": "GetSceneSceneTransitionOverride",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "scenes",
"requestFields": [
{
"valueName": "sceneName",
"valueType": "String",
"valueDescription": "Name of the scene",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": [
{
"valueName": "transitionName",
"valueType": "String",
"valueDescription": "Name of the overridden scene transition, else `null`"
},
{
"valueName": "transitionDuration",
"valueType": "Number",
"valueDescription": "Duration of the overridden scene transition, else `null`"
}
]
},
{
"description": "Gets the scene transition overridden for a scene.",
"requestType": "SetSceneSceneTransitionOverride",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "scenes",
"requestFields": [
{
"valueName": "sceneName",
"valueType": "String",
"valueDescription": "Name of the scene",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "transitionName",
"valueType": "String",
"valueDescription": "Name of the scene transition to use as override. Specify `null` to remove",
"valueRestrictions": null,
"valueOptional": true,
"valueOptionalBehavior": "Unchanged"
},
{
"valueName": "transitionDuration",
"valueType": "Number",
"valueDescription": "Duration to use for any overridden transition. Specify `null` to remove",
"valueRestrictions": ">= 50, <= 20000",
"valueOptional": true,
"valueOptionalBehavior": "Unchanged"
}
],
"responseFields": []
},
{
"description": "Gets the active and show state of a source.\n\n**Compatible with inputs and scenes.**",
"requestType": "GetSourceActive",
@ -3458,6 +3857,26 @@
"requestFields": [],
"responseFields": []
},
{
"description": "Sends CEA-608 caption text over the stream output.",
"requestType": "SendStreamCaption",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "stream",
"requestFields": [
{
"valueName": "captionText",
"valueType": "String",
"valueDescription": "Caption text",
"valueRestrictions": null,
"valueOptional": false,
"valueOptionalBehavior": null
}
],
"responseFields": []
},
{
"description": "Gets an array of all available transition kinds.\n\nSimilar to `GetInputKindList`",
"requestType": "GetTransitionKindList",
@ -3612,6 +4031,23 @@
],
"responseFields": []
},
{
"description": "Gets the cursor position of the current scene transition.\n\nNote: `transitionCursor` will return 1.0 when the transition is inactive.",
"requestType": "GetCurrentSceneTransitionCursor",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"requestFields": [],
"responseFields": [
{
"valueName": "transitionCursor",
"valueType": "Number",
"valueDescription": "Cursor position, between 0.0 and 1.0"
}
]
},
{
"description": "Triggers the current scene transition. Same functionality as the `Transition` button in studio mode.",
"requestType": "TriggerStudioModeTransition",
@ -3623,6 +4059,34 @@
"requestFields": [],
"responseFields": []
},
{
"description": "Sets the position of the TBar.\n\n**Very important note**: This will be deprecated and replaced in a future version of obs-websocket.",
"requestType": "SetTBarPosition",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"requestFields": [
{
"valueName": "position",
"valueType": "Number",
"valueDescription": "New position",
"valueRestrictions": ">= 0.0, <= 1.0",
"valueOptional": false,
"valueOptionalBehavior": null
},
{
"valueName": "release",
"valueType": "Boolean",
"valueDescription": "Whether to release the TBar. Only set `false` if you know that you will be sending another position update",
"valueRestrictions": null,
"valueOptional": true,
"valueOptionalBehavior": "`true`"
}
],
"responseFields": []
},
{
"description": "Gets whether studio is enabled.",
"requestType": "GetStudioModeEnabled",
@ -3824,6 +4288,146 @@
}
]
},
{
"description": "A source's filter list has been reindexed.",
"eventType": "SourceFilterListReindexed",
"eventSubscription": "Filters",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"dataFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source"
},
{
"valueName": "filters",
"valueType": "Array<Object>",
"valueDescription": "Array of filter objects"
}
]
},
{
"description": "A filter has been added to a source.",
"eventType": "SourceFilterCreated",
"eventSubscription": "Filters",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"dataFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter was added to"
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the filter"
},
{
"valueName": "filterKind",
"valueType": "String",
"valueDescription": "The kind of the filter"
},
{
"valueName": "filterIndex",
"valueType": "Number",
"valueDescription": "Index position of the filter"
},
{
"valueName": "filterSettings",
"valueType": "Object",
"valueDescription": "The settings configured to the filter when it was created"
},
{
"valueName": "defaultFilterSettings",
"valueType": "Object",
"valueDescription": "The default settings for the filter"
}
]
},
{
"description": "A filter has been removed from a source.",
"eventType": "SourceFilterRemoved",
"eventSubscription": "Filters",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"dataFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter was on"
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the filter"
}
]
},
{
"description": "The name of a source filter has changed.",
"eventType": "SourceFilterNameChanged",
"eventSubscription": "Filters",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"dataFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "The source the filter is on"
},
{
"valueName": "oldFilterName",
"valueType": "String",
"valueDescription": "Old name of the filter"
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "New name of the filter"
}
]
},
{
"description": "A source filter's enable state has changed.",
"eventType": "SourceFilterEnableStateChanged",
"eventSubscription": "Filters",
"complexity": 3,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "filters",
"dataFields": [
{
"valueName": "sourceName",
"valueType": "String",
"valueDescription": "Name of the source the filter is on"
},
{
"valueName": "filterName",
"valueType": "String",
"valueDescription": "Name of the filter"
},
{
"valueName": "filterEnabled",
"valueType": "Boolean",
"valueDescription": "Whether the filter is enabled"
}
]
},
{
"description": "OBS has begun the shutdown process.",
"eventType": "ExitStarted",
@ -4399,12 +5003,34 @@
"valueDescription": "Numeric ID of the scene item"
},
{
"valueName": "sceneItemEnabled",
"valueName": "sceneItemLocked",
"valueType": "Boolean",
"valueDescription": "Whether the scene item is locked"
}
]
},
{
"description": "A scene item has been selected in the Ui.",
"eventType": "SceneItemSelected",
"eventSubscription": "SceneItems",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "scene items",
"dataFields": [
{
"valueName": "sceneName",
"valueType": "String",
"valueDescription": "Name of the scene the item is in"
},
{
"valueName": "sceneItemId",
"valueType": "Number",
"valueDescription": "Numeric ID of the scene item"
}
]
},
{
"description": "The transform/crop of a scene item has changed.",
"eventType": "SceneItemTransformChanged",
@ -4549,6 +5175,91 @@
}
]
},
{
"description": "The current scene transition has changed.",
"eventType": "CurrentSceneTransitionChanged",
"eventSubscription": "Transitions",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"dataFields": [
{
"valueName": "transitionName",
"valueType": "String",
"valueDescription": "Name of the new transition"
}
]
},
{
"description": "The current scene transition duration has changed.",
"eventType": "CurrentSceneTransitionDurationChanged",
"eventSubscription": "Transitions",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"dataFields": [
{
"valueName": "transitionDuration",
"valueType": "Number",
"valueDescription": "Transition duration in milliseconds"
}
]
},
{
"description": "A scene transition has started.",
"eventType": "SceneTransitionStarted",
"eventSubscription": "Transitions",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"dataFields": [
{
"valueName": "transitionName",
"valueType": "String",
"valueDescription": "Scene transition name"
}
]
},
{
"description": "A scene transition has completed fully.\n\nNote: Does not appear to trigger when the transition is interrupted by the user.",
"eventType": "SceneTransitionEnded",
"eventSubscription": "Transitions",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"dataFields": [
{
"valueName": "transitionName",
"valueType": "String",
"valueDescription": "Scene transition name"
}
]
},
{
"description": "A scene transition's video has completed fully.\n\nUseful for stinger transitions to tell when the video *actually* ends.\n`SceneTransitionEnded` only signifies the cut point, not the completion of transition playback.\n\nNote: Appears to be called by every transition, regardless of relevance.",
"eventType": "SceneTransitionVideoEnded",
"eventSubscription": "Transitions",
"complexity": 2,
"rpcVersion": "1",
"deprecated": false,
"initialVersion": "5.0.0",
"category": "transitions",
"dataFields": [
{
"valueName": "transitionName",
"valueType": "String",
"valueDescription": "Scene transition name"
}
]
},
{
"description": "Studio mode has been enabled or disabled.",
"eventType": "StudioModeStateChanged",

View File

@ -443,6 +443,7 @@ These are enumeration declarations, which are referenced throughout obs-websocke
- [RequestStatus::InvalidResourceState](#requeststatusinvalidresourcestate)
- [RequestStatus::InvalidInputKind](#requeststatusinvalidinputkind)
- [RequestStatus::ResourceNotConfigurable](#requeststatusresourcenotconfigurable)
- [RequestStatus::InvalidFilterKind](#requeststatusinvalidfilterkind)
- [RequestStatus::ResourceCreationFailed](#requeststatusresourcecreationfailed)
- [RequestStatus::ResourceActionFailed](#requeststatusresourceactionfailed)
- [RequestStatus::RequestProcessingFailed](#requeststatusrequestprocessingfailed)
@ -1025,6 +1026,16 @@ This is particularly relevant to transitions, where they do not always have chan
---
### RequestStatus::InvalidFilterKind
The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) had the wrong kind.
- Identifier Value: `607`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### RequestStatus::ResourceCreationFailed
Creating the resource failed.
@ -1268,12 +1279,25 @@ Subscription value to receive the `SceneItemTransformChanged` high-volume event.
- [InputAudioTracksChanged](#inputaudiotrackschanged)
- [InputAudioMonitorTypeChanged](#inputaudiomonitortypechanged)
- [InputVolumeMeters](#inputvolumemeters)
- [Transitions](#transitions)
- [CurrentSceneTransitionChanged](#currentscenetransitionchanged)
- [CurrentSceneTransitionDurationChanged](#currentscenetransitiondurationchanged)
- [SceneTransitionStarted](#scenetransitionstarted)
- [SceneTransitionEnded](#scenetransitionended)
- [SceneTransitionVideoEnded](#scenetransitionvideoended)
- [Filters](#filters)
- [SourceFilterListReindexed](#sourcefilterlistreindexed)
- [SourceFilterCreated](#sourcefiltercreated)
- [SourceFilterRemoved](#sourcefilterremoved)
- [SourceFilterNameChanged](#sourcefilternamechanged)
- [SourceFilterEnableStateChanged](#sourcefilterenablestatechanged)
- [Scene Items](#scene-items)
- [SceneItemCreated](#sceneitemcreated)
- [SceneItemRemoved](#sceneitemremoved)
- [SceneItemListReindexed](#sceneitemlistreindexed)
- [SceneItemEnableStateChanged](#sceneitemenablestatechanged)
- [SceneItemLockStateChanged](#sceneitemlockstatechanged)
- [SceneItemSelected](#sceneitemselected)
- [SceneItemTransformChanged](#sceneitemtransformchanged)
- [Outputs](#outputs)
- [StreamStateChanged](#streamstatechanged)
@ -1758,6 +1782,192 @@ A high-volume event providing volume levels of all active inputs every 50 millis
| Name | Type | Description |
| ---- | :---: | ----------- |
| inputs | Array&lt;Object&gt; | Array of active inputs with their associated volume levels |
## Transitions
### CurrentSceneTransitionChanged
The current scene transition has changed.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionName | String | Name of the new transition |
---
### CurrentSceneTransitionDurationChanged
The current scene transition duration has changed.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionDuration | Number | Transition duration in milliseconds |
---
### SceneTransitionStarted
A scene transition has started.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionName | String | Scene transition name |
---
### SceneTransitionEnded
A scene transition has completed fully.
Note: Does not appear to trigger when the transition is interrupted by the user.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionName | String | Scene transition name |
---
### SceneTransitionVideoEnded
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.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionName | String | Scene transition name |
## Filters
### SourceFilterListReindexed
A source's filter list has been reindexed.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| sourceName | String | Name of the source |
| filters | Array&lt;Object&gt; | Array of filter objects |
---
### SourceFilterCreated
A filter has been added to a source.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| sourceName | String | Name of the source the filter was added to |
| filterName | String | Name of the filter |
| filterKind | String | The kind of the filter |
| filterIndex | Number | Index position of the filter |
| filterSettings | Object | The settings configured to the filter when it was created |
| defaultFilterSettings | Object | The default settings for the filter |
---
### SourceFilterRemoved
A filter has been removed from a source.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| sourceName | String | Name of the source the filter was on |
| filterName | String | Name of the filter |
---
### SourceFilterNameChanged
The name of a source filter has changed.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| sourceName | String | The source the filter is on |
| oldFilterName | String | Old name of the filter |
| filterName | String | New name of the filter |
---
### SourceFilterEnableStateChanged
A source filter's enable state has changed.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| sourceName | String | Name of the source the filter is on |
| filterName | String | Name of the filter |
| filterEnabled | Boolean | Whether the filter is enabled |
## Scene Items
### SceneItemCreated
@ -1853,7 +2063,25 @@ A scene item's lock state has changed.
| ---- | :---: | ----------- |
| sceneName | String | Name of the scene the item is in |
| sceneItemId | Number | Numeric ID of the scene item |
| sceneItemEnabled | Boolean | Whether the scene item is locked |
| sceneItemLocked | Boolean | Whether the scene item is locked |
---
### SceneItemSelected
A scene item has been selected in the Ui.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Data Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| sceneName | String | Name of the scene the item is in |
| sceneItemId | Number | Numeric ID of the scene item |
---
@ -2072,6 +2300,8 @@ Studio mode has been enabled or disabled.
- [CreateScene](#createscene)
- [RemoveScene](#removescene)
- [SetSceneName](#setscenename)
- [GetSceneSceneTransitionOverride](#getscenescenetransitionoverride)
- [SetSceneSceneTransitionOverride](#setscenescenetransitionoverride)
- [Inputs](#inputs-1)
- [GetInputList](#getinputlist)
- [GetInputKindList](#getinputkindlist)
@ -2097,16 +2327,25 @@ Studio mode has been enabled or disabled.
- [SetInputAudioTracks](#setinputaudiotracks)
- [GetInputPropertiesListPropertyItems](#getinputpropertieslistpropertyitems)
- [PressInputPropertiesButton](#pressinputpropertiesbutton)
- [Transitions](#transitions)
- [Transitions](#transitions-1)
- [GetTransitionKindList](#gettransitionkindlist)
- [GetSceneTransitionList](#getscenetransitionlist)
- [GetCurrentSceneTransition](#getcurrentscenetransition)
- [SetCurrentSceneTransition](#setcurrentscenetransition)
- [SetCurrentSceneTransitionDuration](#setcurrentscenetransitionduration)
- [SetCurrentSceneTransitionSettings](#setcurrentscenetransitionsettings)
- [GetCurrentSceneTransitionCursor](#getcurrentscenetransitioncursor)
- [TriggerStudioModeTransition](#triggerstudiomodetransition)
- [Filters](#filters)
- [SetTBarPosition](#settbarposition)
- [Filters](#filters-1)
- [GetSourceFilterList](#getsourcefilterlist)
- [GetSourceFilterDefaultSettings](#getsourcefilterdefaultsettings)
- [CreateSourceFilter](#createsourcefilter)
- [RemoveSourceFilter](#removesourcefilter)
- [SetSourceFilterName](#setsourcefiltername)
- [GetSourceFilter](#getsourcefilter)
- [SetSourceFilterIndex](#setsourcefilterindex)
- [SetSourceFilterSettings](#setsourcefiltersettings)
- [Scene Items](#scene-items-1)
- [GetSceneItemList](#getsceneitemlist)
- [GetGroupItemList](#getgroupitemlist)
@ -2129,11 +2368,18 @@ Studio mode has been enabled or disabled.
- [ToggleVirtualCam](#togglevirtualcam)
- [StartVirtualCam](#startvirtualcam)
- [StopVirtualCam](#stopvirtualcam)
- [GetReplayBufferStatus](#getreplaybufferstatus)
- [ToggleReplayBuffer](#togglereplaybuffer)
- [StartReplayBuffer](#startreplaybuffer)
- [StopReplayBuffer](#stopreplaybuffer)
- [SaveReplayBuffer](#savereplaybuffer)
- [GetLastReplayBufferReplay](#getlastreplaybufferreplay)
- [Stream](#stream)
- [GetStreamStatus](#getstreamstatus)
- [ToggleStream](#togglestream)
- [StartStream](#startstream)
- [StopStream](#stopstream)
- [SendStreamCaption](#sendstreamcaption)
- [Record](#record)
- [GetRecordStatus](#getrecordstatus)
- [ToggleRecord](#togglerecord)
@ -2142,7 +2388,6 @@ Studio mode has been enabled or disabled.
- [ToggleRecordPause](#togglerecordpause)
- [PauseRecord](#pauserecord)
- [ResumeRecord](#resumerecord)
- [GetRecordDirectory](#getrecorddirectory)
- [Media Inputs](#media-inputs-1)
- [GetMediaInputStatus](#getmediainputstatus)
- [SetMediaInputCursor](#setmediainputcursor)
@ -2885,6 +3130,50 @@ Sets the name of a scene (rename).
| sceneName | String | Name of the scene to be renamed | None | N/A |
| newSceneName | String | New name for the scene | None | N/A |
---
### GetSceneSceneTransitionOverride
Gets the scene transition overridden for a scene.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sceneName | String | Name of the scene | None | N/A |
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionName | String | Name of the overridden scene transition, else `null` |
| transitionDuration | Number | Duration of the overridden scene transition, else `null` |
---
### SetSceneSceneTransitionOverride
Gets the scene transition overridden for a scene.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sceneName | String | Name of the scene | None | N/A |
| ?transitionName | String | Name of the scene transition to use as override. Specify `null` to remove | None | Unchanged |
| ?transitionDuration | Number | Duration to use for any overridden transition. Specify `null` to remove | >= 50, <= 20000 | Unchanged |
## Inputs
@ -3540,6 +3829,25 @@ Sets the settings of the current scene transition.
---
### GetCurrentSceneTransitionCursor
Gets the cursor position of the current scene transition.
Note: `transitionCursor` will return 1.0 when the transition is inactive.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| transitionCursor | Number | Cursor position, between 0.0 and 1.0 |
---
### TriggerStudioModeTransition
Triggers the current scene transition. Same functionality as the `Transition` button in studio mode.
@ -3548,9 +3856,134 @@ Triggers the current scene transition. Same functionality as the `Transition` bu
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### SetTBarPosition
Sets the position of the TBar.
**Very important note**: This will be deprecated and replaced in a future version of obs-websocket.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| position | Number | New position | >= 0.0, <= 1.0 | N/A |
| ?release | Boolean | Whether to release the TBar. Only set `false` if you know that you will be sending another position update | None | `true` |
## Filters
### GetSourceFilterList
Gets an array of all of a source's filters.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sourceName | String | Name of the source | None | N/A |
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| filters | Array&lt;Object&gt; | Array of filters |
---
### GetSourceFilterDefaultSettings
Gets the default settings for a filter kind.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| filterKind | String | Filter kind to get the default settings for | None | N/A |
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| defaultFilterSettings | Object | Object of default settings for the filter kind |
---
### CreateSourceFilter
Creates a new filter, adding it to the specified source.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sourceName | String | Name of the source to add the filter to | None | N/A |
| filterName | String | Name of the new filter to be created | None | N/A |
| filterKind | String | The kind of filter to be created | None | N/A |
| ?filterSettings | Object | Settings object to initialize the filter with | None | Default settings used |
---
### RemoveSourceFilter
Removes a filter from a source.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sourceName | String | Name of the source the filter is on | None | N/A |
| filterName | String | Name of the filter to remove | None | N/A |
---
### SetSourceFilterName
Sets the name of a source filter (rename).
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sourceName | String | Name of the source the filter is on | None | N/A |
| filterName | String | Current name of the filter | None | N/A |
| newFilterName | String | New name for the filter | None | N/A |
---
### GetSourceFilter
Gets the info for a specific source filter.
@ -3577,6 +4010,45 @@ Gets the info for a specific source filter.
| filterKind | String | The kind of filter |
| filterSettings | Object | Settings object associated with the filter |
---
### SetSourceFilterIndex
Sets the index position of a filter on a source.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sourceName | String | Name of the source the filter is on | None | N/A |
| filterName | String | Name of the filter | None | N/A |
| filterIndex | Number | New index position of the filter | >= 0 | N/A |
---
### SetSourceFilterSettings
Sets the settings of a source filter.
- Complexity Rating: `3/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| sourceName | String | Name of the source the filter is on | None | N/A |
| filterName | String | Name of the filter to set the settings of | None | N/A |
| filterSettings | Object | Object of settings to apply | None | N/A |
| ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | None | true |
## Scene Items
@ -4040,6 +4512,87 @@ Stops the virtualcam output.
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### GetReplayBufferStatus
Gets the status of the replay buffer output.
- Complexity Rating: `1/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| outputActive | Boolean | Whether the output is active |
---
### ToggleReplayBuffer
Toggles the state of the replay buffer output.
- Complexity Rating: `1/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| outputActive | Boolean | Whether the output is active |
---
### StartReplayBuffer
Starts the replay buffer output.
- Complexity Rating: `1/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### StopReplayBuffer
Stops the replay buffer output.
- Complexity Rating: `1/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### SaveReplayBuffer
Saves the contents of the replay buffer output.
- Complexity Rating: `1/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### GetLastReplayBufferReplay
Gets the filename of the last replay buffer save file.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| savedReplayPath | String | File path |
## Stream
@ -4101,6 +4654,23 @@ Stops the stream output.
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### SendStreamCaption
Sends CEA-608 caption text over the stream output.
- Complexity Rating: `2/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Request Fields:**
| Name | Type | Description | Value Restrictions | ?Default Behavior |
| ---- | :---: | ----------- | :----------------: | ----------------- |
| captionText | String | Caption text | None | N/A |
## Record
@ -4183,23 +4753,6 @@ Resumes the record output.
- Latest Supported RPC Version: `1`
- Added in v5.0.0
---
### GetRecordDirectory
Gets the current directory that the record output is set to.
- Complexity Rating: `1/5`
- Latest Supported RPC Version: `1`
- Added in v5.0.0
**Response Fields:**
| Name | Type | Description |
| ---- | :---: | ----------- |
| recordDirectory | String | Output directory |
## Media Inputs

View File

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

View File

@ -115,6 +115,7 @@ void EventHandler::BroadcastEvent(uint64_t requiredIntent, std::string eventType
_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
{
if (!source || obs_source_removed(source))
@ -128,18 +129,17 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
obs_source_type sourceType = obs_source_get_type(source);
// 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_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);
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_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_connect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
@ -157,10 +157,37 @@ void EventHandler::ConnectSourceSignals(obs_source_t *source) // Applies to inpu
signal_handler_connect(sh, "reorder", HandleSceneItemListReindexed, this);
signal_handler_connect(sh, "item_visible", HandleSceneItemEnableStateChanged, 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);
}
// 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)
{
if (!source)
@ -168,71 +195,120 @@ void EventHandler::DisconnectSourceSignals(obs_source_t *source)
signal_handler_t* sh = obs_source_get_signal_handler(source);
obs_source_type sourceType = obs_source_get_type(source);
// Inputs
signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this);
signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, 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);
if (sourceType == OBS_SOURCE_TYPE_INPUT) {
signal_handler_disconnect(sh, "activate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "deactivate", HandleInputActiveStateChanged, this);
signal_handler_disconnect(sh, "show", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "hide", HandleInputShowStateChanged, this);
signal_handler_disconnect(sh, "mute", HandleInputMuteStateChanged, this);
signal_handler_disconnect(sh, "volume", HandleInputVolumeChanged, this);
signal_handler_disconnect(sh, "audio_balance", HandleInputAudioBalanceChanged, this);
signal_handler_disconnect(sh, "audio_sync", HandleInputAudioSyncOffsetChanged, this);
signal_handler_disconnect(sh, "audio_mixers", HandleInputAudioTracksChanged, this);
signal_handler_disconnect(sh, "audio_monitoring", HandleInputAudioMonitorTypeChanged, this);
signal_handler_disconnect(sh, "media_started", HandleMediaInputPlaybackStarted, this);
signal_handler_disconnect(sh, "media_ended", HandleMediaInputPlaybackEnded, this);
signal_handler_disconnect(sh, "media_pause", SourceMediaPauseMultiHandler, this);
signal_handler_disconnect(sh, "media_play", SourceMediaPlayMultiHandler, this);
signal_handler_disconnect(sh, "media_restart", SourceMediaRestartMultiHandler, 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
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this);
signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this);
signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, this);
signal_handler_disconnect(sh, "item_locked", HandleSceneItemLockStateChanged, this);
signal_handler_disconnect(sh, "item_transform", HandleSceneItemTransformChanged, this);
if (sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_disconnect(sh, "item_add", HandleSceneItemCreated, this);
signal_handler_disconnect(sh, "item_remove", HandleSceneItemRemoved, this);
signal_handler_disconnect(sh, "reorder", HandleSceneItemListReindexed, this);
signal_handler_disconnect(sh, "item_visible", HandleSceneItemEnableStateChanged, 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)
{
auto eventHandler = static_cast<EventHandler*>(private_data);
if (!eventHandler->_obsLoaded.load()) {
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
if (!eventHandler->_obsLoaded.load() && event != OBS_FRONTEND_EVENT_FINISHED_LOADING)
return;
switch (event) {
// General
case OBS_FRONTEND_EVENT_FINISHED_LOADING:
blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
eventHandler->_obsLoaded.store(true);
// In the case that plugins become hotloadable, this will have to go back into `EventHandler::EventHandler()`
// Enumerate inputs and connect each one
obs_enum_sources([](void* param, obs_source_t* source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(source);
return true;
}, private_data);
{
auto enumInputs = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(source);
return true;
};
obs_enum_sources(enumInputs, private_data);
}
// Enumerate scenes and connect each one
obs_enum_scenes([](void* param, obs_source_t* source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(source);
return true;
}, private_data);
{
auto enumScenes = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->ConnectSourceSignals(source);
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.");
if (eventHandler->_obsLoadedCallback)
eventHandler->_obsLoadedCallback();
} else {
return;
}
}
switch (event) {
// General
break;
case OBS_FRONTEND_EVENT_EXIT:
eventHandler->HandleExitStarted();
@ -242,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()`
// Enumerate inputs and disconnect each one
obs_enum_sources([](void* param, obs_source_t* source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source);
return true;
}, private_data);
{
auto enumInputs = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source);
return true;
};
obs_enum_sources(enumInputs, private_data);
}
// Enumerate scenes and disconnect each one
obs_enum_scenes([](void* param, obs_source_t* source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source);
return true;
}, private_data);
{
auto enumScenes = [](void *param, obs_source_t *source) {
auto eventHandler = static_cast<EventHandler*>(param);
eventHandler->DisconnectSourceSignals(source);
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.");
@ -267,9 +360,27 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
// Config
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
{
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:
{
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();
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED:
@ -298,10 +409,21 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
// Transitions
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
eventHandler->HandleCurrentSceneTransitionChanged();
break;
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;
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
eventHandler->HandleCurrentSceneTransitionDurationChanged();
break;
// Outputs
@ -461,8 +583,6 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
case OBS_SOURCE_TYPE_INPUT:
eventHandler->HandleInputNameChanged(source, oldSourceName, sourceName);
break;
case OBS_SOURCE_TYPE_FILTER:
break;
case OBS_SOURCE_TYPE_TRANSITION:
break;
case OBS_SOURCE_TYPE_SCENE:

View File

@ -112,6 +112,22 @@ class EventHandler
static void HandleInputAudioTracksChanged(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
void HandleStreamStateChanged(ObsOutputState state);
void HandleRecordStateChanged(ObsOutputState state);
@ -125,6 +141,7 @@ class EventHandler
static void HandleSceneItemListReindexed(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 HandleSceneItemSelected(void *param, calldata_t *data); // Direct callback
static void HandleSceneItemTransformChanged(void *param, calldata_t *data); // Direct callback
// Media Inputs

View File

@ -18,3 +18,184 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/
#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

@ -158,9 +158,9 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
/**
* A scene item's lock state has changed.
*
* @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemEnabled | Boolean | Whether the scene item is locked
* @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemLocked | Boolean | Whether the scene item is locked
*
* @eventType SceneItemLockStateChanged
* @eventSubscription SceneItems
@ -191,6 +191,38 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
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.
*

View File

@ -18,3 +18,130 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*/
#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>
</property>
<property name="value">
<number>4444</number>
<number>4455</number>
</property>
</widget>
</item>

View File

@ -51,6 +51,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"SetVideoSettings", &RequestHandler::SetVideoSettings},
{"GetStreamServiceSettings", &RequestHandler::GetStreamServiceSettings},
{"SetStreamServiceSettings", &RequestHandler::SetStreamServiceSettings},
{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
// Sources
{"GetSourceActive", &RequestHandler::GetSourceActive},
@ -69,6 +70,8 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"CreateScene", &RequestHandler::CreateScene},
{"RemoveScene", &RequestHandler::RemoveScene},
{"SetSceneName", &RequestHandler::SetSceneName},
{"GetSceneSceneTransitionOverride", &RequestHandler::GetSceneSceneTransitionOverride},
{"SetSceneSceneTransitionOverride", &RequestHandler::SetSceneSceneTransitionOverride},
// Inputs
{"GetInputList", &RequestHandler::GetInputList},
@ -103,10 +106,20 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"SetCurrentSceneTransition", &RequestHandler::SetCurrentSceneTransition},
{"SetCurrentSceneTransitionDuration", &RequestHandler::SetCurrentSceneTransitionDuration},
{"SetCurrentSceneTransitionSettings", &RequestHandler::SetCurrentSceneTransitionSettings},
{"GetCurrentSceneTransitionCursor", &RequestHandler::GetCurrentSceneTransitionCursor},
{"TriggerStudioModeTransition", &RequestHandler::TriggerStudioModeTransition},
{"SetTBarPosition", &RequestHandler::SetTBarPosition},
// Filters
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
{"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings},
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
{"RemoveSourceFilter", &RequestHandler::RemoveSourceFilter},
{"SetSourceFilterName", &RequestHandler::SetSourceFilterName},
{"GetSourceFilter", &RequestHandler::GetSourceFilter},
{"SetSourceFilterIndex", &RequestHandler::SetSourceFilterIndex},
{"SetSourceFilterSettings", &RequestHandler::SetSourceFilterSettings},
{"SetSourceFilterEnabled", &RequestHandler::SetSourceFilterEnabled},
// Scene Items
{"GetSceneItemList", &RequestHandler::GetSceneItemList},
@ -131,12 +144,19 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"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
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
{"ToggleStream", &RequestHandler::ToggleStream},
{"StartStream", &RequestHandler::StartStream},
{"StopStream", &RequestHandler::StopStream},
{"SendStreamCaption", &RequestHandler::SendStreamCaption},
// Record
{"GetRecordStatus", &RequestHandler::GetRecordStatus},
@ -146,7 +166,6 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
{"PauseRecord", &RequestHandler::PauseRecord},
{"ResumeRecord", &RequestHandler::ResumeRecord},
{"GetRecordDirectory", &RequestHandler::GetRecordDirectory},
// Media Inputs
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},

View File

@ -69,6 +69,7 @@ class RequestHandler {
RequestResult SetVideoSettings(const Request&);
RequestResult GetStreamServiceSettings(const Request&);
RequestResult SetStreamServiceSettings(const Request&);
RequestResult GetRecordDirectory(const Request&);
// Sources
RequestResult GetSourceActive(const Request&);
@ -87,6 +88,8 @@ class RequestHandler {
RequestResult CreateScene(const Request&);
RequestResult RemoveScene(const Request&);
RequestResult SetSceneName(const Request&);
RequestResult GetSceneSceneTransitionOverride(const Request&);
RequestResult SetSceneSceneTransitionOverride(const Request&);
// Inputs
RequestResult GetInputList(const Request&);
@ -121,10 +124,20 @@ class RequestHandler {
RequestResult SetCurrentSceneTransition(const Request&);
RequestResult SetCurrentSceneTransitionDuration(const Request&);
RequestResult SetCurrentSceneTransitionSettings(const Request&);
RequestResult GetCurrentSceneTransitionCursor(const Request&);
RequestResult TriggerStudioModeTransition(const Request&);
RequestResult SetTBarPosition(const Request&);
// Filters
RequestResult GetSourceFilterList(const Request&);
RequestResult GetSourceFilterDefaultSettings(const Request&);
RequestResult CreateSourceFilter(const Request&);
RequestResult RemoveSourceFilter(const Request&);
RequestResult SetSourceFilterName(const Request&);
RequestResult GetSourceFilter(const Request&);
RequestResult SetSourceFilterIndex(const Request&);
RequestResult SetSourceFilterSettings(const Request&);
RequestResult SetSourceFilterEnabled(const Request&);
// Scene Items
RequestResult GetSceneItemList(const Request&);
@ -149,12 +162,19 @@ class RequestHandler {
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
RequestResult GetStreamStatus(const Request&);
RequestResult ToggleStream(const Request&);
RequestResult StartStream(const Request&);
RequestResult StopStream(const Request&);
RequestResult SendStreamCaption(const Request&);
// Record
RequestResult GetRecordStatus(const Request&);
@ -164,7 +184,6 @@ class RequestHandler {
RequestResult ToggleRecordPause(const Request&);
RequestResult PauseRecord(const Request&);
RequestResult ResumeRecord(const Request&);
RequestResult GetRecordDirectory(const Request&);
// Media Inputs
RequestResult GetMediaInputStatus(const Request&);

View File

@ -408,6 +408,8 @@ RequestResult RequestHandler::SetProfileParameter(const Request& request)
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The field `parameterValue` must be a string.");
}
config_save(profile);
return RequestResult::Success();
}
@ -592,3 +594,23 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
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

@ -19,6 +19,178 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestHandler.h"
/**
* Gets an array of all of a source's filters.
*
* @requestField sourceName | String | Name of the source
*
* @responseField filters | Array<Object> | Array of filters
*
* @requestType GetSourceFilterList
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::GetSourceFilterList(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if(!source)
return RequestResult::Error(statusCode, comment);
json responseData;
responseData["filters"] = Utils::Obs::ArrayHelper::GetSourceFilterList(source);
return RequestResult::Success(responseData);
}
/**
* Gets the default settings for a filter kind.
*
* @requestField filterKind | String | Filter kind to get the default settings for
*
* @responseField defaultFilterSettings | Object | Object of default settings for the filter kind
*
* @requestType GetSourceFilterDefaultSettings
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
if (!request.ValidateString("filterKind", statusCode, comment))
return RequestResult::Error(statusCode, comment);
std::string filterKind = request.RequestData["filterKind"];
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidFilterKind);
OBSDataAutoRelease defaultSettings = obs_get_source_defaults(filterKind.c_str());
if (!defaultSettings)
return RequestResult::Error(RequestStatus::InvalidFilterKind);
json responseData;
responseData["defaultFilterSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true);
return RequestResult::Success(responseData);
}
/**
* Creates a new filter, adding it to the specified source.
*
* @requestField sourceName | String | Name of the source to add the filter to
* @requestField filterName | String | Name of the new filter to be created
* @requestField filterKind | String | The kind of filter to be created
* @requestField ?filterSettings | Object | Settings object to initialize the filter with | Default settings used
*
* @requestType CreateSourceFilter
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::CreateSourceFilter(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
if (!(source && request.ValidateString("filterName", statusCode, comment) && request.ValidateString("filterKind", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
std::string filterName = request.RequestData["filterName"];
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(source, filterName.c_str());
if (existingFilter)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that name.");
std::string filterKind = request.RequestData["filterKind"];
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
return RequestResult::Error(RequestStatus::InvalidFilterKind, "Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
OBSDataAutoRelease filterSettings = nullptr;
if (request.Contains("filterSettings")) {
if (!request.ValidateOptionalObject("filterSettings", statusCode, comment, true))
return RequestResult::Error(statusCode, comment);
filterSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
}
OBSSourceAutoRelease filter = Utils::Obs::ActionHelper::CreateSourceFilter(source, filterName, filterKind, filterSettings);
if(!filter)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the filter failed.");
return RequestResult::Success();
}
/**
* Removes a filter from a source.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Name of the filter to remove
*
* @requestType RemoveSourceFilter
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::RemoveSourceFilter(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!pair.filter)
return RequestResult::Error(statusCode, comment);
obs_source_filter_remove(pair.source, pair.filter);
return RequestResult::Success();
}
/**
* Sets the name of a source filter (rename).
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Current name of the filter
* @requestField newFilterName | String | New name for the filter
*
* @requestType SetSourceFilterName
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::SetSourceFilterName(const Request& request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
if (!pair.filter || !request.ValidateString("newFilterName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
std::string newFilterName = request.RequestData["newFilterName"];
OBSSourceAutoRelease existingFilter = obs_source_get_filter_by_name(pair.source, newFilterName.c_str());
if (existingFilter)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A filter already exists by that new name.");
obs_source_set_name(pair.filter, newFilterName.c_str());
return RequestResult::Success();
}
/**
* Gets the info for a specific source filter.
*
@ -55,3 +227,108 @@ RequestResult RequestHandler::GetSourceFilter(const Request& request)
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

@ -269,6 +269,9 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request& request)
return RequestResult::Error(statusCode, comment);
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());
if (!defaultSettings)

View File

@ -28,6 +28,12 @@ static bool VirtualCamAvailable()
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.
*
@ -124,3 +130,148 @@ RequestResult RequestHandler::StopVirtualCam(const Request&)
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();
}
/**
* 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)
return RequestResult::Error(statusCode, comment);
} else {
destinationScene = obs_sceneitem_get_scene(sceneItem);
obs_scene_addref(destinationScene);
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
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) {

View File

@ -273,3 +273,105 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
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

@ -122,3 +122,35 @@ RequestResult RequestHandler::StopStream(const Request&)
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/>
*/
#include <math.h>
#include "RequestHandler.h"
/**
@ -226,6 +228,32 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
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.
*
@ -247,3 +275,48 @@ RequestResult RequestHandler::TriggerStudioModeTransition(const Request&)
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

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

View File

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

View File

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

View File

@ -193,6 +193,8 @@ namespace Utils {
std::vector<json> GetListPropertyItems(obs_property_t *property);
std::vector<std::string> GetTransitionKindList();
std::vector<json> GetSceneTransitionList();
std::vector<json> GetSourceFilterList(obs_source_t *source);
std::vector<std::string> GetFilterKindList();
}
namespace ObjectHelper {
@ -209,6 +211,8 @@ namespace Utils {
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 *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

@ -87,3 +87,30 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
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

@ -88,6 +88,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
obs_frontend_get_scenes(&sceneList);
std::vector<json> ret;
ret.reserve(sceneList.sources.num);
for (size_t i = 0; i < sceneList.sources.num; i++) {
obs_source_t *scene = sceneList.sources.array[i];
@ -225,6 +226,8 @@ std::vector<json> Utils::Obs::ArrayHelper::GetListPropertyItems(obs_property_t *
enum obs_combo_format itemFormat = obs_property_list_format(property);
size_t itemCount = obs_property_list_item_count(property);
ret.reserve(itemCount);
for (size_t i = 0; i < itemCount; i++) {
json itemData;
itemData["itemName"] = obs_property_list_item_name(property, i);
@ -262,6 +265,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
obs_frontend_get_transitions(&transitionList);
std::vector<json> ret;
ret.reserve(transitionList.sources.num);
for (size_t i = 0; i < transitionList.sources.num; i++) {
obs_source_t *transition = transitionList.sources.array[i];
json transitionJson;
@ -276,3 +280,38 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
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

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

View File

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