mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Merge branch '4.x-current' into feature/t-bar
This commit is contained in:
22
.github/CONTRIBUTING.md
vendored
22
.github/CONTRIBUTING.md
vendored
@ -1,22 +0,0 @@
|
|||||||
## Contributing to obs-websocket
|
|
||||||
|
|
||||||
### Translating obs-websocket to your language
|
|
||||||
Localization happens on Crowdin: https://crowdin.com/project/obs-websocket
|
|
||||||
|
|
||||||
### Writing code for obs-websocket
|
|
||||||
#### Coding Guidelines
|
|
||||||
- Function and variable names: snake_case for C names, CamelCase for C++ names
|
|
||||||
- Tabs are 8 columns wide
|
|
||||||
- 80 columns max.
|
|
||||||
|
|
||||||
#### Commit Guidelines
|
|
||||||
- Commits follow the 50/72 standard:
|
|
||||||
- 50 characters max for the title
|
|
||||||
- One empty line after the title
|
|
||||||
- Description wrapped to 72 columns max per line.
|
|
||||||
- Commit titles:
|
|
||||||
- Use present tense
|
|
||||||
- Prefix the title with a "scope" name
|
|
||||||
- e.g: "CI: fix wrong behaviour when packaging for OS X"
|
|
||||||
- Typical scopes: CI, General, Request, Event, Server
|
|
||||||
- Look at existing commits for more examples
|
|
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
open_collective: obs-websocket
|
||||||
|
github: Palakis
|
||||||
|
custom: https://www.paypal.me/stephanelepin
|
14
.github/ISSUE_TEMPLATE.md
vendored
14
.github/ISSUE_TEMPLATE.md
vendored
@ -1,16 +1,20 @@
|
|||||||
##### Issue type
|
##### Issue type
|
||||||
Bug report? Feature request? Other?
|
<!--- Uncomment one of the two options below. -->
|
||||||
|
|
||||||
|
<!--- - Bug report -->
|
||||||
|
<!--- - Feature request -->
|
||||||
|
|
||||||
##### Description
|
##### Description
|
||||||
*Replace this with a description of the bug encountered or feature requested.*
|
<!--- Describe the bug encountered or feature requested. -->
|
||||||
|
|
||||||
##### Steps to reproduce and other useful info
|
##### Steps to reproduce and other useful info
|
||||||
*If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section.*
|
<!--- If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section. -->
|
||||||
|
|
||||||
##### Technical information
|
##### Technical information
|
||||||
- **Operating System** :
|
- **Operating System** :
|
||||||
- **OBS Studio version** :
|
- **OBS Studio version** :
|
||||||
|
- **obs-websocket version** :
|
||||||
|
|
||||||
##### Development Environment
|
##### Development Environment
|
||||||
*If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used.
|
<!--- If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used. -->
|
||||||
Remove this section if it doesn't apply to your case.*
|
<!--- Remove this section if it does not apply. -->
|
||||||
|
BIN
.github/images/obsws_logo.png
vendored
Normal file
BIN
.github/images/obsws_logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
35
.github/pull_request_template.md
vendored
Normal file
35
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<!--- Please fill out the following template, which will help other contributors review your Pull Request. -->
|
||||||
|
|
||||||
|
<!--- Make sure you’ve read the contribution guidelines here: https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md -->
|
||||||
|
|
||||||
|
### Description
|
||||||
|
<!--- Describe your changes. -->
|
||||||
|
|
||||||
|
### Motivation and Context
|
||||||
|
<!--- Why is this change required? What problem does it solve? -->
|
||||||
|
<!--- If it fixes/closes an open issue or implements feature request, -->
|
||||||
|
<!--- please link to the issue here. -->
|
||||||
|
|
||||||
|
### How Has This Been Tested?
|
||||||
|
<!--- Please describe in detail how you tested your changes, along with the OS(s) you tested with. -->
|
||||||
|
Tested OS(s):
|
||||||
|
|
||||||
|
### Types of changes
|
||||||
|
<!--- What types of changes does your PR introduce? Uncomment all that apply -->
|
||||||
|
|
||||||
|
<!--- - Bug fix (non-breaking change which fixes an issue) -->
|
||||||
|
<!--- - New request/event (non-breaking) -->
|
||||||
|
<!--- - Documentation change (a change to documentation pages) -->
|
||||||
|
<!--- - Enhancement (modification to a current event/request which adds functionality) -->
|
||||||
|
<!--- - Performance enhancement (non-breaking change which improves efficiency) -->
|
||||||
|
<!--- - Code cleanup (non-breaking change which makes code smaller or more readable) -->
|
||||||
|
|
||||||
|
### Checklist:
|
||||||
|
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||||
|
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||||
|
- [ ] I have read the [**contributing** document](https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md).
|
||||||
|
- [ ] My code is not on the master branch.
|
||||||
|
- [ ] The code has been tested.
|
||||||
|
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
|
||||||
|
- [ ] I have included updates to all appropriate documentation.
|
||||||
|
|
412
.github/workflows/pr_push.yml
vendored
Normal file
412
.github/workflows/pr_push.yml
vendored
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
name: 'CI Multiplatform Build'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
branches:
|
||||||
|
- 4.x-current
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
branches:
|
||||||
|
- 4.x-current
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows:
|
||||||
|
name: 'Windows 32+64bit'
|
||||||
|
runs-on: [windows-latest]
|
||||||
|
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
||||||
|
env:
|
||||||
|
QT_VERSION: '5.10.1'
|
||||||
|
WINDOWS_DEPS_VERSION: '2017'
|
||||||
|
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||||
|
CMAKE_SYSTEM_VERSION: "10.0"
|
||||||
|
steps:
|
||||||
|
- name: Add msbuild to PATH
|
||||||
|
uses: microsoft/setup-msbuild@v1.0.0
|
||||||
|
- name: 'Checkout'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Checkout OBS'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
path: ${{ github.workspace }}/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Get OBS-Studio git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git checkout ${{ env.OBS_GIT_TAG }}
|
||||||
|
git submodule update
|
||||||
|
- name: 'Get obs-websocket git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
|
||||||
|
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Install prerequisite: QT'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
|
||||||
|
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
|
||||||
|
- name: 'Install prerequisite: Pre-built OBS dependencies'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
|
||||||
|
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
|
||||||
|
- name: 'Restore OBS 32-bit build v${{ env.OBS_GIT_TAG }} from cache'
|
||||||
|
id: build-cache-obs-32
|
||||||
|
uses: actions/cache@v1
|
||||||
|
env:
|
||||||
|
CACHE_NAME: 'build-cache-obs-32'
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-studio/build32
|
||||||
|
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||||
|
- name: 'Configure OBS 32-bit'
|
||||||
|
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
mkdir .\build32
|
||||||
|
cd .\build32
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
|
||||||
|
- name: 'Build OBS-Studio 32-bit'
|
||||||
|
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
|
||||||
|
- name: 'Restore OBS 64-bit build v${{ env.OBS_GIT_TAG }} from cache'
|
||||||
|
id: build-cache-obs-64
|
||||||
|
uses: actions/cache@v1
|
||||||
|
env:
|
||||||
|
CACHE_NAME: 'build-cache-obs-64'
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-studio/build64
|
||||||
|
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||||
|
- name: 'Configure OBS 64-bit'
|
||||||
|
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
mkdir .\build64
|
||||||
|
cd .\build64
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
|
||||||
|
- name: 'Build OBS-Studio 64-bit'
|
||||||
|
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build64\libobs\libobs.vcxproj
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build64\UI\obs-frontend-api\obs-frontend-api.vcxproj
|
||||||
|
- name: 'Configure obs-websocket 64-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
mkdir .\build64
|
||||||
|
cd .\build64
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
|
||||||
|
- name: 'Configure obs-websocket 32-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
mkdir .\build32
|
||||||
|
cd .\build32
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
|
||||||
|
- name: 'Build obs-websocket 64-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-websocket.sln
|
||||||
|
- name: 'Build obs-websocket 32-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-websocket.sln
|
||||||
|
- name: 'Set PR artifact filename'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILENAME="obs-websocket-${{ env.GIT_HASH }}-Windows"
|
||||||
|
echo "::set-env name=FILENAME::$FILENAME"
|
||||||
|
- name: 'Package obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
mkdir package
|
||||||
|
cd package
|
||||||
|
7z a "${{ env.WIN_FILENAME }}.zip" "..\release\*"
|
||||||
|
iscc ..\installer\installer.iss /O. /F"${{ env.WIN_FILENAME }}-Installer"
|
||||||
|
- name: 'Publish ${{ env.WIN_FILENAME }}.zip'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_HASH }}-Windows'
|
||||||
|
path: ${{ github.workspace }}/obs-websocket/package/*.zip
|
||||||
|
- name: 'Publish ${{ env.WIN_FILENAME }}-Installer.exe'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_HASH }}-Windows-Installer'
|
||||||
|
path: ${{ github.workspace }}/obs-websocket/package/*.exe
|
||||||
|
ubuntu64:
|
||||||
|
name: "Linux/Ubuntu 64-bit"
|
||||||
|
runs-on: [ubuntu-latest]
|
||||||
|
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
||||||
|
steps:
|
||||||
|
- name: 'Checkout'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Checkout OBS'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
path: ${{ github.workspace }}/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Get OBS-Studio git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git checkout ${{ env.OBS_GIT_TAG }}
|
||||||
|
git submodule update
|
||||||
|
- name: 'Get obs-websocket git info'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
|
||||||
|
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Install prerequisites (Apt)'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture amd64
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
checkinstall \
|
||||||
|
cmake \
|
||||||
|
libasound2-dev \
|
||||||
|
libavcodec-dev \
|
||||||
|
libavdevice-dev \
|
||||||
|
libavfilter-dev \
|
||||||
|
libavformat-dev \
|
||||||
|
libavutil-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libfdk-aac-dev \
|
||||||
|
libfontconfig-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libjack-jackd2-dev \
|
||||||
|
libjansson-dev \
|
||||||
|
libluajit-5.1-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libqt5x11extras5-dev \
|
||||||
|
libspeexdsp-dev \
|
||||||
|
libswresample-dev \
|
||||||
|
libswscale-dev \
|
||||||
|
libudev-dev \
|
||||||
|
libv4l-dev \
|
||||||
|
libva-dev \
|
||||||
|
libvlc-dev \
|
||||||
|
libx11-dev \
|
||||||
|
libx264-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libxcb-shm0-dev \
|
||||||
|
libxcb-xinerama0-dev \
|
||||||
|
libxcomposite-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
libmbedtls-dev \
|
||||||
|
pkg-config \
|
||||||
|
python3-dev \
|
||||||
|
qtbase5-dev \
|
||||||
|
libqt5svg5-dev \
|
||||||
|
swig
|
||||||
|
- name: 'Configure OBS-Studio'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
cd ./build
|
||||||
|
cmake -DBUILD_CAPTIONS=YES -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
- name: 'Build OBS-Studio'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4 libobs obs-frontend-api
|
||||||
|
- name: 'Install OBS-Studio'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd ./build
|
||||||
|
sudo cp ./libobs/libobs.so /usr/lib
|
||||||
|
sudo cp ./UI/obs-frontend-api/libobs-frontend-api.so /usr/lib
|
||||||
|
sudo mkdir -p /usr/include/obs
|
||||||
|
sudo cp ../UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/obs-frontend-api.h
|
||||||
|
- name: 'Configure obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
cd ./build
|
||||||
|
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
- name: 'Build obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4
|
||||||
|
- name: 'Set PR artifact filename'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILENAME="obs-websocket-1-${{ env.GIT_HASH }}-1_amd64.deb"
|
||||||
|
echo "::set-env name=FILENAME::$FILENAME"
|
||||||
|
- name: 'Package ${{ env.FILENAME }}'
|
||||||
|
if: success()
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
VERSION="1-${{ env.GIT_HASH }}-git"
|
||||||
|
cd ./build
|
||||||
|
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
|
||||||
|
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion=$VERSION \
|
||||||
|
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \
|
||||||
|
--pkgsource="${{ github.event.repository.html_url }}" \
|
||||||
|
--requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \
|
||||||
|
--pakdir="../package"
|
||||||
|
sudo chmod ao+r ../package/*
|
||||||
|
cd -
|
||||||
|
- name: 'Publish ${{ env.FILENAME }}'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_HASH }}-linux'
|
||||||
|
path: '${{ github.workspace }}/obs-websocket/package/*.deb'
|
||||||
|
macos64:
|
||||||
|
name: "macOS 64-bit"
|
||||||
|
runs-on: [macos-latest]
|
||||||
|
if: contains(github.event.head_commit.message, '[skip ci]') != true
|
||||||
|
env:
|
||||||
|
MACOS_DEPS_VERSION: '2020-04-18'
|
||||||
|
QT_VERSION: '5.14.1'
|
||||||
|
steps:
|
||||||
|
- name: 'Checkout'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Checkout OBS'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
path: ${{ github.workspace }}/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Get OBS-Studio git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git checkout ${{ env.OBS_GIT_TAG }}
|
||||||
|
git submodule update
|
||||||
|
- name: 'Get obs-websocket git info'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
|
||||||
|
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Install prerequisites (Homebrew)'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew bundle --file ${{ github.workspace }}/obs-websocket/CI/macos/Brewfile
|
||||||
|
- name: 'Install prerequisite: Pre-built OBS dependencies'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
|
||||||
|
tar -xf ${{ github.workspace }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
|
||||||
|
- name: 'Configure OBS Studio'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DBUILD_CAPTIONS=YES -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake ..
|
||||||
|
- name: 'Build OBS Studio libraries'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4 libobs obs-frontend-api
|
||||||
|
- name: 'Configure obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DQTDIR=/usr/local/Cellar/qt/${{ env.QT_VERSION }} -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
- name: 'Build obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4
|
||||||
|
- name: 'Install prerequisite: Packages app'
|
||||||
|
if: success()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
|
||||||
|
sudo installer -pkg ${{ github.workspace }}/Packages.pkg -target /
|
||||||
|
- name: 'Set PR artifact filename'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILENAME_UNSIGNED="obs-websocket-${{ env.GIT_HASH }}-macOS-Unsigned.pkg"
|
||||||
|
echo "::set-env name=FILENAME_UNSIGNED::$FILENAME_UNSIGNED"
|
||||||
|
- name: 'Fix linked dynamic library paths'
|
||||||
|
if: success()
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./build/obs-websocket.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./build/obs-websocket.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./build/obs-websocket.so
|
||||||
|
echo "Dependencies for obs-websocket"
|
||||||
|
otool -L ./build/obs-websocket.so
|
||||||
|
- name: 'Package ${{ env.FILENAME }}'
|
||||||
|
if: success()
|
||||||
|
working-directory: ./obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
||||||
|
mv ./release/obs-websocket.pkg ./release/${{ env.FILENAME_UNSIGNED }}
|
||||||
|
- name: 'Publish ${{ env.FILENAME_UNSIGNED }} artifact'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_HASH }}-macOS'
|
||||||
|
path: ${{ github.workspace }}/obs-websocket/release/*.pkg
|
484
.github/workflows/tag_release.yml
vendored
Normal file
484
.github/workflows/tag_release.yml
vendored
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
name: 'CI Multiplatform Release'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
tags:
|
||||||
|
- '[45].[0-9]+.[0-9]+'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows:
|
||||||
|
name: 'Windows 32+64bit'
|
||||||
|
runs-on: [windows-latest]
|
||||||
|
env:
|
||||||
|
QT_VERSION: '5.10.1'
|
||||||
|
WINDOWS_DEPS_VERSION: '2017'
|
||||||
|
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||||
|
CMAKE_SYSTEM_VERSION: "10.0"
|
||||||
|
steps:
|
||||||
|
- name: 'Add msbuild to PATH'
|
||||||
|
uses: microsoft/setup-msbuild@v1.0.0
|
||||||
|
- name: 'Checkout'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Checkout OBS'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
path: ${{ github.workspace }}/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Get OBS-Studio git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git checkout ${{ env.OBS_GIT_TAG }}
|
||||||
|
git submodule update
|
||||||
|
- name: 'Get obs-websocket git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
|
||||||
|
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Install prerequisite: QT'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
|
||||||
|
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
|
||||||
|
- name: 'Install prerequisite: Pre-built OBS dependencies'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
|
||||||
|
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
|
||||||
|
- name: 'Restore OBS 32-bit build v${{ env.OBS_GIT_TAG }} from cache'
|
||||||
|
id: build-cache-obs-32
|
||||||
|
uses: actions/cache@v1
|
||||||
|
env:
|
||||||
|
CACHE_NAME: 'build-cache-obs-32'
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-studio/build32
|
||||||
|
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||||
|
- name: 'Configure OBS 32-bit'
|
||||||
|
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
mkdir .\build32
|
||||||
|
cd .\build32
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
|
||||||
|
- name: 'Build OBS-Studio 32-bit'
|
||||||
|
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
|
||||||
|
- name: 'Restore OBS 64-bit build v${{ env.OBS_GIT_TAG }} from cache'
|
||||||
|
id: build-cache-obs-64
|
||||||
|
uses: actions/cache@v1
|
||||||
|
env:
|
||||||
|
CACHE_NAME: 'build-cache-obs-64'
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-studio/build64
|
||||||
|
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ env.CACHE_NAME }}-
|
||||||
|
- name: 'Configure OBS 64-bit'
|
||||||
|
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
mkdir .\build64
|
||||||
|
cd .\build64
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
|
||||||
|
- name: 'Build OBS-Studio 64-bit'
|
||||||
|
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build64\libobs\libobs.vcxproj
|
||||||
|
msbuild /m /p:Configuration=RelWithDebInfo .\build64\UI\obs-frontend-api\obs-frontend-api.vcxproj
|
||||||
|
- name: 'Configure obs-websocket 64-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
mkdir .\build64
|
||||||
|
cd .\build64
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
|
||||||
|
- name: 'Configure obs-websocket 32-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
mkdir .\build32
|
||||||
|
cd .\build32
|
||||||
|
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
|
||||||
|
- name: 'Build obs-websocket 64-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-websocket.sln
|
||||||
|
- name: 'Build obs-websocket 32-bit'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-websocket.sln
|
||||||
|
- name: 'Set release filename'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILENAME="obs-websocket-${{ env.GIT_TAG }}-Windows"
|
||||||
|
echo "::set-env name=WIN_FILENAME::$FILENAME"
|
||||||
|
- name: 'Package obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
mkdir package
|
||||||
|
cd package
|
||||||
|
7z a "${{ env.WIN_FILENAME }}.zip" "..\release\*"
|
||||||
|
iscc ..\installer\installer.iss /O. /F"${{ env.WIN_FILENAME }}-Installer"
|
||||||
|
- name: 'Publish ${{ env.WIN_FILENAME }}.zip'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_TAG }}-Windows'
|
||||||
|
path: ${{ github.workspace }}/obs-websocket/package/*.zip
|
||||||
|
- name: 'Publish ${{ env.WIN_FILENAME }}-Installer.exe'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_TAG }}-Windows-Installer'
|
||||||
|
path: ${{ github.workspace }}/obs-websocket/package/*.exe
|
||||||
|
ubuntu64:
|
||||||
|
name: "Linux/Ubuntu 64-bit"
|
||||||
|
runs-on: [ubuntu-latest]
|
||||||
|
steps:
|
||||||
|
- name: 'Checkout'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Checkout OBS'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
path: ${{ github.workspace }}/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Get OBS-Studio git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git checkout ${{ env.OBS_GIT_TAG }}
|
||||||
|
git submodule update
|
||||||
|
- name: 'Get obs-websocket git info'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
|
||||||
|
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Install prerequisites (Apt)'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture amd64
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
checkinstall \
|
||||||
|
cmake \
|
||||||
|
libasound2-dev \
|
||||||
|
libavcodec-dev \
|
||||||
|
libavdevice-dev \
|
||||||
|
libavfilter-dev \
|
||||||
|
libavformat-dev \
|
||||||
|
libavutil-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libfdk-aac-dev \
|
||||||
|
libfontconfig-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libjack-jackd2-dev \
|
||||||
|
libjansson-dev \
|
||||||
|
libluajit-5.1-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libqt5x11extras5-dev \
|
||||||
|
libspeexdsp-dev \
|
||||||
|
libswresample-dev \
|
||||||
|
libswscale-dev \
|
||||||
|
libudev-dev \
|
||||||
|
libv4l-dev \
|
||||||
|
libva-dev \
|
||||||
|
libvlc-dev \
|
||||||
|
libx11-dev \
|
||||||
|
libx264-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libxcb-shm0-dev \
|
||||||
|
libxcb-xinerama0-dev \
|
||||||
|
libxcomposite-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
libmbedtls-dev \
|
||||||
|
pkg-config \
|
||||||
|
python3-dev \
|
||||||
|
qtbase5-dev \
|
||||||
|
libqt5svg5-dev \
|
||||||
|
swig
|
||||||
|
- name: 'Configure OBS-Studio'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
cd ./build
|
||||||
|
cmake -DBUILD_CAPTIONS=YES -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
- name: 'Build OBS-Studio'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4 libobs obs-frontend-api
|
||||||
|
- name: 'Install OBS-Studio'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd ./build
|
||||||
|
sudo cp ./libobs/libobs.so /usr/lib
|
||||||
|
sudo cp ./UI/obs-frontend-api/libobs-frontend-api.so /usr/lib
|
||||||
|
sudo mkdir -p /usr/include/obs
|
||||||
|
sudo cp ../UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/obs-frontend-api.h
|
||||||
|
- name: 'Configure obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
cd ./build
|
||||||
|
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
- name: 'Build obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4
|
||||||
|
- name: 'Set release filename'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILENAME="obs-websocket-${{ env.GIT_TAG }}-1_amd64.deb"
|
||||||
|
echo "::set-env name=LINUX_FILENAME::$FILENAME"
|
||||||
|
- name: 'Package ${{ env.LINUX_FILENAME }}'
|
||||||
|
if: success()
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
VERSION="${{ env.GIT_TAG }}"
|
||||||
|
cd ./build
|
||||||
|
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
|
||||||
|
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion=$VERSION \
|
||||||
|
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \
|
||||||
|
--pkgsource="${{ github.event.repository.html_url }}" \
|
||||||
|
--requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \
|
||||||
|
--pakdir="../package"
|
||||||
|
sudo chmod ao+r ../package/*
|
||||||
|
cd -
|
||||||
|
- name: 'Publish ${{ env.LINUX_FILENAME }}'
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_TAG }}-linux'
|
||||||
|
path: '${{ github.workspace }}/obs-websocket/package/*.deb'
|
||||||
|
macos64:
|
||||||
|
name: "macOS 64-bit"
|
||||||
|
runs-on: [macos-latest]
|
||||||
|
env:
|
||||||
|
MACOS_DEPS_VERSION: '2020-04-18'
|
||||||
|
QT_VERSION: '5.14.1'
|
||||||
|
steps:
|
||||||
|
- name: 'Checkout'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/obs-websocket
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Checkout OBS'
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
path: ${{ github.workspace }}/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: 'Get OBS-Studio git info'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
git checkout ${{ env.OBS_GIT_TAG }}
|
||||||
|
git submodule update
|
||||||
|
- name: 'Get obs-websocket git info'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
|
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
|
||||||
|
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||||
|
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||||
|
- name: 'Install prerequisites (Homebrew)'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew bundle --file ${{ github.workspace }}/obs-websocket/CI/macos/Brewfile
|
||||||
|
- name: 'Install prerequisite: Pre-built OBS dependencies'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
|
||||||
|
tar -xf ${{ github.workspace }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
|
||||||
|
- name: 'Configure OBS Studio'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DBUILD_CAPTIONS=YES -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake ..
|
||||||
|
- name: 'Build OBS Studio libraries'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-studio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4 libobs obs-frontend-api
|
||||||
|
- name: 'Configure obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DQTDIR=/usr/local/Cellar/qt/${{ env.QT_VERSION }} -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
- name: 'Build obs-websocket'
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4
|
||||||
|
- name: 'Install prerequisite: Packages app'
|
||||||
|
if: success()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
|
||||||
|
sudo installer -pkg ${{ github.workspace }}/Packages.pkg -target /
|
||||||
|
- name: 'Set release filename'
|
||||||
|
if: success() && startsWith(github.ref, 'refs/tags')
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILENAME_UNSIGNED="obs-websocket-${{ env.GIT_TAG }}-macOS-Unsigned.pkg"
|
||||||
|
FILENAME="obs-websocket-${{ env.GIT_TAG }}-macOS.pkg"
|
||||||
|
echo "::set-env name=MAC_FILENAME_UNSIGNED::$FILENAME_UNSIGNED"
|
||||||
|
echo "::set-env name=MAC_FILENAME::$FILENAME"
|
||||||
|
- name: 'Fix linked dynamic library paths'
|
||||||
|
if: success()
|
||||||
|
working-directory: ${{ github.workspace }}/obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./build/obs-websocket.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./build/obs-websocket.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./build/obs-websocket.so
|
||||||
|
echo "Dependencies for obs-websocket"
|
||||||
|
otool -L ./build/obs-websocket.so
|
||||||
|
- name: 'Install Apple Developer Certificate'
|
||||||
|
if: success()
|
||||||
|
uses: apple-actions/import-codesign-certs@253ddeeac23f2bdad1646faac5c8c2832e800071
|
||||||
|
with:
|
||||||
|
p12-file-base64: ${{ secrets.MACOS_CERT_CODESIGN }}
|
||||||
|
p12-password: ${{ secrets.MACOS_CERT_PASS }}
|
||||||
|
- name: 'Code signing'
|
||||||
|
if: success()
|
||||||
|
working-directory: ./obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
codesign --sign "${{ secrets.MACOS_IDENT_CODESIGN }}" ./build/obs-websocket.so
|
||||||
|
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
||||||
|
mv ./release/obs-websocket.pkg ./release/${{ env.MAC_FILENAME_UNSIGNED }}
|
||||||
|
productsign --sign "${{ secrets.MACOS_IDENT_INSTALLER }}" ./release/${{ env.MAC_FILENAME_UNSIGNED }} ./release/${{ env.MAC_FILENAME }}
|
||||||
|
rm ./release/${{ env.MAC_FILENAME_UNSIGNED }}
|
||||||
|
- name: 'Notarization'
|
||||||
|
if: success()
|
||||||
|
working-directory: ./obs-websocket
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "${{ secrets.MACOS_IDENT_USER }}" -p "${{ secrets.MACOS_IDENT_PASS }}"
|
||||||
|
xcnotary precheck ./release/${{ env.MAC_FILENAME }}
|
||||||
|
if [ "$?" -eq 0 ]; then xcnotary notarize ./release/${{ env.MAC_FILENAME }} --developer-account "${{ secrets.MACOS_IDENT_USER }}" --developer-password-keychain-item "AC_PASSWORD" --provider "${{ secrets.MACOS_IDENT_PROVIDER }}"; fi
|
||||||
|
- name: 'Publish ${{ env.MAC_FILENAME }} artifact'
|
||||||
|
if: success() && startsWith(github.ref, 'refs/tags')
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.GIT_TAG }}-macOS'
|
||||||
|
path: ${{ github.workspace }}/obs-websocket/release/*.pkg
|
||||||
|
make-release:
|
||||||
|
name: 'Create and upload release'
|
||||||
|
runs-on: [ubuntu-latest]
|
||||||
|
needs: [windows, ubuntu64, macos64]
|
||||||
|
steps:
|
||||||
|
- name: 'Get the version'
|
||||||
|
shell: bash
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
echo ::set-env name=TAG_VERSION::${GITHUB_REF/refs\/tags\//}
|
||||||
|
- name: 'Create Release'
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.TAG_VERSION }}
|
||||||
|
release_name: obs-websocket ${{ env.TAG_VERSION }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
- name: 'Download release artifacts'
|
||||||
|
uses: actions/download-artifact@v2-preview
|
||||||
|
- name: 'Upload Windows .zip artifact to release'
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-Windows/obs-websocket-${{ env.TAG_VERSION }}-Windows.zip
|
||||||
|
asset_name: obs-websocket-${{ env.TAG_VERSION }}-Windows.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- name: 'Upload Windows .exe artifact to release'
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-Windows-Installer/obs-websocket-${{ env.TAG_VERSION }}-Windows-Installer.exe
|
||||||
|
asset_name: obs-websocket-${{ env.TAG_VERSION }}-Windows-Installer.exe
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- name: 'Upload Linux artifact to release'
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-linux/obs-websocket_${{ env.TAG_VERSION }}-1_amd64.deb
|
||||||
|
asset_name: obs-websocket-${{ env.TAG_VERSION }}-1_amd64.deb
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
- name: 'Upload macOS artifact to release'
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-macOS/obs-websocket-${{ env.TAG_VERSION }}-macOS.pkg
|
||||||
|
asset_name: obs-websocket-${{ env.TAG_VERSION }}-macOS.pkg
|
||||||
|
asset_content_type: application/octet-stream
|
14
BUILDING.md
14
BUILDING.md
@ -24,17 +24,23 @@ sudo apt-get install libboost-all-dev
|
|||||||
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
||||||
cd obs-websocket
|
cd obs-websocket
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
|
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true ..
|
||||||
make -j4
|
make -j4
|
||||||
sudo make install
|
sudo make install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
On other linux OS's, use this cmake command instead:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
```
|
||||||
|
|
||||||
## OS X
|
## OS X
|
||||||
|
|
||||||
As a prerequisite, you will need Xcode for your current OSX version, the Xcode command line tools, and [Homebrew](https://brew.sh/).
|
As a prerequisite, you will need Xcode for your current OSX version, the Xcode command line tools, and [Homebrew](https://brew.sh/).
|
||||||
Homebrew's setup will guide you in getting your system set up, you should be good to go once Homebrew is successfully up and running.
|
Homebrew's setup will guide you in getting your system set up, you should be good to go once Homebrew is successfully up and running.
|
||||||
|
|
||||||
Use of the Travis macOS CI scripts is recommended. Please note that these
|
Use of the macOS CI scripts is recommended. Please note that these
|
||||||
scripts install new software and can change several settings on your system. An
|
scripts install new software and can change several settings on your system. An
|
||||||
existing obs-studio development environment is not required, as
|
existing obs-studio development environment is not required, as
|
||||||
`install-build-obs-macos.sh` will install it for you. If you already have a
|
`install-build-obs-macos.sh` will install it for you. If you already have a
|
||||||
@ -57,6 +63,4 @@ This will result in a ready-to-use `obs-websocket.pkg` installer in the `release
|
|||||||
|
|
||||||
## Automated Builds
|
## Automated Builds
|
||||||
|
|
||||||
- Windows: [](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
|
[](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
|
||||||
- Linux: [](https://travis-ci.org/Palakis/obs-websocket)
|
|
||||||
- macOS: [](https://dev.azure.com/Palakis/obs-websocket/_build)
|
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
cmake -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true ..
|
||||||
make -j4
|
make -j4
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
@echo off
|
|
||||||
SETLOCAL EnableDelayedExpansion
|
|
||||||
|
|
||||||
REM Check if obs-studio build exists.
|
|
||||||
REM If the obs-studio directory does exist, check if the last OBS tag built
|
|
||||||
REM matches the latest OBS tag.
|
|
||||||
REM If the tags match, do not build obs-studio.
|
|
||||||
REM If the tags do not match, build obs-studio.
|
|
||||||
REM If the obs-studio directory doesn't exist, build obs-studio.
|
|
||||||
echo Checking for obs-studio build...
|
|
||||||
|
|
||||||
set OBSLatestTagPrePull=0
|
|
||||||
set OBSLatestTagPostPull=0
|
|
||||||
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
|
||||||
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
|
||||||
|
|
||||||
REM Set up the build flag as undefined.
|
|
||||||
set "BuildOBS="
|
|
||||||
|
|
||||||
REM Check the last tag successfully built by CI.
|
|
||||||
if exist "%OBSPath%\obs-studio-last-tag-built.txt" (
|
|
||||||
set /p OBSLastTagBuilt=<"%OBSPath%\obs-studio-last-tag-built.txt"
|
|
||||||
) else (
|
|
||||||
set OBSLastTagBuilt=0
|
|
||||||
)
|
|
||||||
|
|
||||||
REM If obs-studio directory exists, run git pull and get the latest tag number.
|
|
||||||
if exist %OBSPath% (
|
|
||||||
echo obs-studio directory exists
|
|
||||||
echo Updating tag info
|
|
||||||
cd /D %OBSPath%
|
|
||||||
git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\latest-obs-studio-tag-pre-pull.txt"
|
|
||||||
set /p OBSLatestTagPrePull=<"%OBSPath%\latest-obs-studio-tag-pre-pull.txt"
|
|
||||||
git checkout master
|
|
||||||
git pull
|
|
||||||
git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\latest-obs-studio-tag-post-pull.txt"
|
|
||||||
set /p OBSLatestTagPostPull=<"%OBSPath%\latest-obs-studio-tag-post-pull.txt"
|
|
||||||
set /p OBSLatestTag=<"%OBSPath%\latest-obs-studio-tag-post-pull.txt"
|
|
||||||
echo %OBSLatestTagPostPull%> "%OBSPath%\latest-obs-studio-tag.txt"
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check the obs-studio tags for mismatches.
|
|
||||||
REM If a new tag was pulled, set the build flag.
|
|
||||||
if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% (
|
|
||||||
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
|
||||||
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
|
||||||
echo Tags do not match. Need to rebuild OBS.
|
|
||||||
set BuildOBS=true
|
|
||||||
)
|
|
||||||
|
|
||||||
REM If the latest git tag doesn't match the last built tag, set the build flag.
|
|
||||||
if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% (
|
|
||||||
echo Last built OBS tag: %OBSLastTagBuilt%
|
|
||||||
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
|
||||||
echo Tags do not match. Need to rebuild OBS.
|
|
||||||
set BuildOBS=true
|
|
||||||
)
|
|
||||||
|
|
||||||
REM If obs-studio directory does not exist, clone the git repo, get the latest
|
|
||||||
REM tag number, and set the build flag.
|
|
||||||
if not exist %OBSPath% (
|
|
||||||
echo obs-studio directory does not exist
|
|
||||||
git clone https://github.com/obsproject/obs-studio %OBSPath%
|
|
||||||
cd /D %OBSPath%\
|
|
||||||
git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\obs-studio-latest-tag.txt"
|
|
||||||
set /p OBSLatestTag=<"%OBSPath%\obs-studio-latest-tag.txt"
|
|
||||||
set BuildOBS=true
|
|
||||||
)
|
|
||||||
|
|
||||||
REM If the needed obs-studio libs for this build_config do not exist,
|
|
||||||
REM set the build flag.
|
|
||||||
if not exist %OBSPath%\build32\libobs\%build_config%\obs.lib (
|
|
||||||
echo obs-studio\build32\libobs\%build_config%\obs.lib does not exist
|
|
||||||
set BuildOBS=true
|
|
||||||
)
|
|
||||||
if not exist %OBSPath%\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib (
|
|
||||||
echo obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib does not exist
|
|
||||||
set BuildOBS=true
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Some debug info
|
|
||||||
echo:
|
|
||||||
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
|
||||||
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
|
||||||
echo Latest tag: %OBSLatestTag%
|
|
||||||
echo Last built OBS tag: %OBSLastTagBuilt%
|
|
||||||
|
|
||||||
if defined BuildOBS (
|
|
||||||
echo BuildOBS: true
|
|
||||||
) else (
|
|
||||||
echo BuildOBS: false
|
|
||||||
)
|
|
||||||
echo:
|
|
||||||
|
|
||||||
REM If the build flag is set, build obs-studio.
|
|
||||||
if defined BuildOBS (
|
|
||||||
echo Building obs-studio...
|
|
||||||
cd /D %OBSPath%
|
|
||||||
echo git checkout %OBSLatestTag%
|
|
||||||
git checkout %OBSLatestTag%
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo Removing previous build dirs...
|
|
||||||
if exist build32 rmdir /s /q "%OBSPath%\build32"
|
|
||||||
if exist build64 rmdir /s /q "%OBSPath%\build64"
|
|
||||||
|
|
||||||
echo Making new build dirs...
|
|
||||||
mkdir build32
|
|
||||||
mkdir build64
|
|
||||||
|
|
||||||
echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
|
|
||||||
cd build32
|
|
||||||
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
|
||||||
echo:
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
|
|
||||||
cd ..\build64
|
|
||||||
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
|
||||||
echo:
|
|
||||||
echo:
|
|
||||||
|
|
||||||
REM echo Building obs-studio %OBSLatestTag% 32-bit ^(Build Config: %build_config%^)...
|
|
||||||
REM call msbuild /m /p:Configuration=%build_config% %OBSPath%\build32\obs-studio.sln
|
|
||||||
|
|
||||||
REM echo Building obs-studio %OBSLatestTag% 64-bit ^(Build Config: %build_config%^)...
|
|
||||||
REM call msbuild /m /p:Configuration=%build_config% %OBSPath%\build64\obs-studio.sln
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
git describe --tags --abbrev=0 > "%OBSPath%\obs-studio-last-tag-built.txt"
|
|
||||||
set /p OBSLastTagBuilt=<"%OBSPath%\obs-studio-last-tag-built.txt"
|
|
||||||
) else (
|
|
||||||
echo Last OBS tag built is: %OBSLastTagBuilt%
|
|
||||||
echo No need to rebuild OBS.
|
|
||||||
)
|
|
||||||
|
|
||||||
dir "%OBSPath%\libobs"
|
|
@ -34,6 +34,7 @@ git checkout $OBSLatestTag
|
|||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
echo "[obs-websocket] Building obs-studio.."
|
echo "[obs-websocket] Building obs-studio.."
|
||||||
cmake .. \
|
cmake .. \
|
||||||
|
-DBUILD_CAPTIONS=true \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
|
||||||
-DDISABLE_PLUGINS=true \
|
-DDISABLE_PLUGINS=true \
|
||||||
-DENABLE_SCRIPTING=0 \
|
-DENABLE_SCRIPTING=0 \
|
||||||
|
@ -35,13 +35,8 @@ done
|
|||||||
|
|
||||||
# qtwebsockets deps
|
# qtwebsockets deps
|
||||||
echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.."
|
echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.."
|
||||||
# =!= NOTICE =!=
|
|
||||||
# When building QT5 from sources on macOS 10.13+, use local qt5 formula:
|
|
||||||
# brew install ./CI/macos/qt.rb
|
|
||||||
# Pouring from the bottle is much quicker though, so use bottle for now.
|
|
||||||
# =!= NOTICE =!=
|
|
||||||
|
|
||||||
brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb
|
brew install ./CI/macos/qt.rb
|
||||||
|
|
||||||
# Pin this version of QT5 to avoid `brew upgrade`
|
# Pin this version of QT5 to avoid `brew upgrade`
|
||||||
# upgrading it to incompatible version
|
# upgrading it to incompatible version
|
||||||
|
10
CI/macos/Brewfile
Normal file
10
CI/macos/Brewfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
tap "akeru-inc/tap"
|
||||||
|
brew "jack"
|
||||||
|
brew "speexdsp"
|
||||||
|
brew "cmake"
|
||||||
|
brew "freetype"
|
||||||
|
brew "fdk-aac"
|
||||||
|
brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb"
|
||||||
|
brew "swig", link: false
|
||||||
|
brew "https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb"
|
||||||
|
brew "akeru-inc/tap/xcnotary"
|
@ -3,10 +3,10 @@
|
|||||||
class Qt < Formula
|
class Qt < Formula
|
||||||
desc "Cross-platform application and UI framework"
|
desc "Cross-platform application and UI framework"
|
||||||
homepage "https://www.qt.io/"
|
homepage "https://www.qt.io/"
|
||||||
url "https://download.qt.io/archive/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
|
url "https://download.qt.io/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
|
||||||
mirror "https://mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
|
mirror "https://www.mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
|
||||||
sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a"
|
sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a"
|
||||||
head "https://code.qt.io/qt/qt5.git", :branch => "5.10.1", :shallow => false
|
head "https://code.qt.io/qt/qt5.git", :branch => "5.10", :shallow => false
|
||||||
|
|
||||||
bottle do
|
bottle do
|
||||||
sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra
|
sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra
|
||||||
@ -18,17 +18,16 @@ class Qt < Formula
|
|||||||
|
|
||||||
option "with-docs", "Build documentation"
|
option "with-docs", "Build documentation"
|
||||||
option "with-examples", "Build examples"
|
option "with-examples", "Build examples"
|
||||||
|
option "without-proprietary-codecs", "Don't build with proprietary codecs (e.g. mp3)"
|
||||||
deprecated_option "with-mysql" => "with-mysql-client"
|
|
||||||
|
|
||||||
# OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference
|
# OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference
|
||||||
# configuration and thus untested in practice. Builds on OS X 10.7 have been
|
# configuration and thus untested in practice. Builds on OS X 10.7 have been
|
||||||
# reported to fail: <https://github.com/Homebrew/homebrew/issues/45284>.
|
# reported to fail: <https://github.com/Homebrew/homebrew/issues/45284>.
|
||||||
depends_on :macos => :mountain_lion
|
# depends_on :macos => :mountain_lion
|
||||||
|
|
||||||
depends_on "pkg-config" => :build
|
depends_on "pkg-config" => :build
|
||||||
depends_on :xcode => :build
|
depends_on :xcode => :build
|
||||||
depends_on "mysql-client" => :optional
|
depends_on "mysql" => :optional
|
||||||
depends_on "postgresql" => :optional
|
depends_on "postgresql" => :optional
|
||||||
|
|
||||||
# Restore `.pc` files for framework-based build of Qt 5 on OS X. This
|
# Restore `.pc` files for framework-based build of Qt 5 on OS X. This
|
||||||
@ -44,23 +43,6 @@ class Qt < Formula
|
|||||||
sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b"
|
sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fix compile error on macOS 10.13 around QFixed:
|
|
||||||
# https://github.com/Homebrew/homebrew-core/issues/27095
|
|
||||||
# https://bugreports.qt.io/browse/QTBUG-67545
|
|
||||||
patch do
|
|
||||||
url "https://raw.githubusercontent.com/z00m1n/formula-patches/0de0e229/qt/QTBUG-67545.patch"
|
|
||||||
sha256 "4a115097c7582c7dce4207f5500d13feb8c990eb8a05a43f41953985976ebe6c"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fix compile error on macOS 10.13 caused by qtlocation dependency
|
|
||||||
# mapbox-gl-native using Boost 1.62.0 does not build with C++ 17:
|
|
||||||
# https://github.com/Homebrew/homebrew-core/issues/27095
|
|
||||||
# https://bugreports.qt.io/browse/QTBUG-67810
|
|
||||||
patch do
|
|
||||||
url "https://raw.githubusercontent.com/z00m1n/formula-patches/a1a1f0dd/qt/QTBUG-67810.patch"
|
|
||||||
sha256 "8ee0bf71df1043f08ebae3aa35036be29c4d9ebff8a27e3b0411a6bd635e9382"
|
|
||||||
end
|
|
||||||
|
|
||||||
def install
|
def install
|
||||||
args = %W[
|
args = %W[
|
||||||
-verbose
|
-verbose
|
||||||
@ -76,12 +58,11 @@ class Qt < Formula
|
|||||||
-no-rpath
|
-no-rpath
|
||||||
-pkg-config
|
-pkg-config
|
||||||
-dbus-runtime
|
-dbus-runtime
|
||||||
-no-assimp
|
|
||||||
]
|
]
|
||||||
|
|
||||||
args << "-nomake" << "examples" if build.without? "examples"
|
args << "-nomake" << "examples" if build.without? "examples"
|
||||||
|
|
||||||
if build.with? "mysql-client"
|
if build.with? "mysql"
|
||||||
args << "-plugin-sql-mysql"
|
args << "-plugin-sql-mysql"
|
||||||
(buildpath/"brew_shim/mysql_config").write <<~EOS
|
(buildpath/"brew_shim/mysql_config").write <<~EOS
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
@ -96,6 +77,7 @@ class Qt < Formula
|
|||||||
end
|
end
|
||||||
|
|
||||||
args << "-plugin-sql-psql" if build.with? "postgresql"
|
args << "-plugin-sql-psql" if build.with? "postgresql"
|
||||||
|
args << "-proprietary-codecs" if build.with? "proprietary-codecs"
|
||||||
|
|
||||||
system "./configure", *args
|
system "./configure", *args
|
||||||
system "make"
|
system "make"
|
||||||
|
37
CI/prepare-obs-windows.cmd
Normal file
37
CI/prepare-obs-windows.cmd
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
@echo off
|
||||||
|
SETLOCAL EnableDelayedExpansion
|
||||||
|
|
||||||
|
REM If obs-studio directory does not exist, clone the git repo
|
||||||
|
if not exist %OBSPath% (
|
||||||
|
echo obs-studio directory does not exist
|
||||||
|
git clone https://github.com/obsproject/obs-studio %OBSPath%
|
||||||
|
cd /D %OBSPath%\
|
||||||
|
git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\obs-studio-latest-tag.txt"
|
||||||
|
set /p OBSLatestTag=<"%OBSPath%\obs-studio-latest-tag.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Prepare OBS Studio builds
|
||||||
|
|
||||||
|
echo Running CMake...
|
||||||
|
cd /D %OBSPath%
|
||||||
|
echo git checkout %OBSLatestTag%
|
||||||
|
git checkout %OBSLatestTag%
|
||||||
|
echo:
|
||||||
|
|
||||||
|
if not exist build32 mkdir build32
|
||||||
|
if not exist build64 mkdir build64
|
||||||
|
|
||||||
|
echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
|
||||||
|
cd build32
|
||||||
|
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
|
echo:
|
||||||
|
echo:
|
||||||
|
|
||||||
|
echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
|
||||||
|
cd ..\build64
|
||||||
|
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
|
echo:
|
||||||
|
echo:
|
||||||
|
|
||||||
|
dir "%OBSPath%\libobs"
|
@ -11,6 +11,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
add_definitions(-DASIO_STANDALONE)
|
add_definitions(-DASIO_STANDALONE)
|
||||||
|
|
||||||
|
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
|
||||||
|
set(CMAKE_CXX_FLAGS "-mfpu=neon")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32 OR APPLE)
|
if (WIN32 OR APPLE)
|
||||||
include(external/FindLibObs.cmake)
|
include(external/FindLibObs.cmake)
|
||||||
endif()
|
endif()
|
||||||
@ -35,6 +39,7 @@ set(obs-websocket_SOURCES
|
|||||||
src/WSRequestHandler_StudioMode.cpp
|
src/WSRequestHandler_StudioMode.cpp
|
||||||
src/WSRequestHandler_Transitions.cpp
|
src/WSRequestHandler_Transitions.cpp
|
||||||
src/WSRequestHandler_Outputs.cpp
|
src/WSRequestHandler_Outputs.cpp
|
||||||
|
src/WSRequestHandler_MediaControl.cpp
|
||||||
src/WSEvents.cpp
|
src/WSEvents.cpp
|
||||||
src/Config.cpp
|
src/Config.cpp
|
||||||
src/Utils.cpp
|
src/Utils.cpp
|
||||||
@ -187,6 +192,10 @@ if(UNIX AND NOT APPLE)
|
|||||||
|
|
||||||
file(GLOB locale_files data/locale/*.ini)
|
file(GLOB locale_files data/locale/*.ini)
|
||||||
|
|
||||||
|
if(${USE_UBUNTU_FIX})
|
||||||
|
install(TARGETS obs-websocket
|
||||||
|
LIBRARY DESTINATION "/usr/lib/obs-plugins")
|
||||||
|
endif()
|
||||||
install(TARGETS obs-websocket
|
install(TARGETS obs-websocket
|
||||||
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins")
|
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins")
|
||||||
|
|
||||||
|
82
CONTRIBUTING.md
Normal file
82
CONTRIBUTING.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Contributing to obs-websocket
|
||||||
|
|
||||||
|
## Translating obs-websocket to your language
|
||||||
|
|
||||||
|
Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket)
|
||||||
|
|
||||||
|
## Branches
|
||||||
|
|
||||||
|
**Development happens on `4.x-current`**
|
||||||
|
|
||||||
|
## Writing code for obs-websocket
|
||||||
|
|
||||||
|
### Code Formatting Guidelines
|
||||||
|
|
||||||
|
* Function and variable names: snake_case for C names, camelCase for C++ method names
|
||||||
|
|
||||||
|
* Request and Event names should use MixedCaps names
|
||||||
|
|
||||||
|
* Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml)
|
||||||
|
|
||||||
|
* Code is indented with Tabs. Assume they are 8 columns wide
|
||||||
|
|
||||||
|
* 80 columns max code width. (Docs can be larger)
|
||||||
|
|
||||||
|
* New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples).
|
||||||
|
These are required to automatically generate the [protocol specification document](docs/generated/protocol.md).
|
||||||
|
|
||||||
|
### Code Best-Practices
|
||||||
|
|
||||||
|
* Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this:
|
||||||
|
```cpp
|
||||||
|
if (success) {
|
||||||
|
return req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
return req->SendErrorResponse("something went wrong");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
is better like this:
|
||||||
|
```cpp
|
||||||
|
if (!success) {
|
||||||
|
return req->SendErrorResponse("something went wrong");
|
||||||
|
}
|
||||||
|
return req->SendOKResponse();
|
||||||
|
```
|
||||||
|
|
||||||
|
* Some example common response/request property names are:
|
||||||
|
* `sceneName` - The name of a scene
|
||||||
|
* `sourceName` - The name of a source
|
||||||
|
* `fromScene` - From a scene - scene name
|
||||||
|
|
||||||
|
### Commit Guidelines
|
||||||
|
|
||||||
|
* Commits follow the 50/72 standard:
|
||||||
|
* 50 characters max for the commit title (excluding scope name)
|
||||||
|
* One empty line after the title
|
||||||
|
* Description wrapped to 72 columns max width per line.
|
||||||
|
|
||||||
|
* Commit titles:
|
||||||
|
* Use present tense
|
||||||
|
* Prefix the title with a "scope" name
|
||||||
|
* e.g: "CI: fix wrong behaviour when packaging for OS X"
|
||||||
|
* Typical scopes: CI, General, Requests, Events, Server
|
||||||
|
|
||||||
|
**Example commit:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Requests: Add GetTransitionPosition
|
||||||
|
|
||||||
|
Adds a new request called `GetTransitionPosition` which gets the current
|
||||||
|
transition's state from 0.0f to 1.0f. Works with both auto and manual
|
||||||
|
transitions.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
|
||||||
|
* Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`).
|
||||||
|
* Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...)
|
||||||
|
|
||||||
|
* Only open a pull request if you are ready to show off your work.
|
||||||
|
|
||||||
|
* If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers),
|
||||||
|
create it as a Draft Pull Request (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request").
|
93
README.md
93
README.md
@ -1,11 +1,14 @@
|
|||||||
obs-websocket
|
# obs-websocket
|
||||||
==============
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="/.github/images/obsws_logo.png" width=150 align="center">
|
||||||
|
</p>
|
||||||
|
|
||||||
WebSockets API for OBS Studio.
|
WebSockets API for OBS Studio.
|
||||||
|
|
||||||
Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis)
|
[](https://discord.gg/WBaSQ3A)
|
||||||
|
|
||||||
[](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
|
[](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
|
||||||
|
[](https://twitter.com/LePalakis)
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
@ -13,7 +16,10 @@ Binaries for Windows, MacOS, and Linux are available in the [Releases](https://g
|
|||||||
|
|
||||||
## Using obs-websocket
|
## Using obs-websocket
|
||||||
|
|
||||||
A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remote) (compatible with tablets and other touch interfaces) is available here : http://t2t2.github.io/obs-tablet-remote/
|
Here is a list of available web clients: (compatible with tablets and other touch interfaces)
|
||||||
|
|
||||||
|
- [Niek/obs-web](https://github.com/Niek/obs-web)
|
||||||
|
- [t2t2/obs-tablet-remote](https://github.com/t2t2/obs-tablet-remote)
|
||||||
|
|
||||||
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
|
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
|
||||||
|
|
||||||
@ -38,7 +44,13 @@ Here's a list of available language APIs for obs-websocket :
|
|||||||
- Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf
|
- Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf
|
||||||
- HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468
|
- HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468
|
||||||
|
|
||||||
I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `stephane /dot/ lepin /at/ gmail /dot/ com` !
|
I'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)
|
||||||
|
|
||||||
|
### Securing obs-websocket (via TLS/SSL)
|
||||||
|
|
||||||
|
If you are intending to use obs-websocket outside of a LAN environment, it is highly recommended to secure the connection using a tunneling service.
|
||||||
|
|
||||||
|
See the SSL [tunnelling guide](SSL-TUNNELLING.md) for easy instructions on how to encrypt your websocket connection.
|
||||||
|
|
||||||
## Compiling obs-websocket
|
## Compiling obs-websocket
|
||||||
|
|
||||||
@ -46,80 +58,23 @@ See the [build instructions](BUILDING.md).
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
### Branches
|
See [the contributing document](/CONTRIBUTING.md)
|
||||||
|
|
||||||
Development happens on `4.x-current`
|
|
||||||
|
|
||||||
### Pull Requests
|
|
||||||
|
|
||||||
Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch
|
|
||||||
based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work.
|
|
||||||
|
|
||||||
**If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request").
|
|
||||||
|
|
||||||
### Code style & formatting
|
|
||||||
|
|
||||||
Source code is indented with tabs, with spaces allowed for alignment.
|
|
||||||
|
|
||||||
Regarding protocol changes: new and updated request types / events must always come with accompanying documentation comments (see existing protocol elements for examples).
|
|
||||||
These are required to automatically generate the [protocol specification document](docs/generated/protocol.md).
|
|
||||||
|
|
||||||
Among other recommendations: favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
if (success) {
|
|
||||||
return req->SendOKResponse();
|
|
||||||
} else {
|
|
||||||
return req->SendErrorResponse("something went wrong");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
is better like this:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
if (!success) {
|
|
||||||
return req->SendErrorResponse("something went wrong");
|
|
||||||
}
|
|
||||||
return req->SendOKResponse();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
**Your help is welcome on translations**. Please join the localization project on Crowdin: https://crowdin.com/project/obs-websocket
|
**Your help is welcome on translations.**
|
||||||
|
|
||||||
|
Please join the localization project on [Crowdin](https://crowdin.com/project/obs-websocket)
|
||||||
|
|
||||||
## Special thanks
|
## Special thanks
|
||||||
|
|
||||||
In (almost) order of appearance:
|
Thank you so much to all of the contibutors [(here)](https://github.com/Palakis/obs-websocket/graphs/contributors) for your amazing help.
|
||||||
|
|
||||||
- [Brendan H.](https://github.com/haganbmj): Code contributions and gooder English in the Protocol specification
|
|
||||||
- [Mikhail Swift](https://github.com/mikhailswift): Code contributions
|
|
||||||
- [Tobias Frahmer](https://github.com/Frahmer): Initial German localization
|
|
||||||
- [Genture](https://github.com/Genteure): Initial Simplified Chinese and Traditional Chinese localizations
|
|
||||||
- [Larissa Gabilan](https://github.com/laris151): Initial Portuguese localization
|
|
||||||
- [Andy Asquelt](https://github.com/asquelt): Initial Polish localization
|
|
||||||
- [Marcel Haazen](https://github.com/nekocentral): Initial Dutch localization
|
|
||||||
- [Peter Antonvich](https://github.com/pantonvich): Code contributions
|
|
||||||
- [yinzara](https://github.com/yinzara): Code contributions
|
|
||||||
- [Chris Angelico](https://github.com/Rosuav): Code contributions
|
|
||||||
- [Guillaume "Elektordi" Genty](https://github.com/Elektordi): Code contributions
|
|
||||||
- [Marwin M](https://github.com/dragonbane0): Code contributions
|
|
||||||
- [Logan S.](https://github.com/lsdaniel): Code contributions
|
|
||||||
- [RainbowEK](https://github.com/RainbowEK): Code contributions
|
|
||||||
- [RytoEX](https://github.com/RytoEX): CI script and code contributions
|
|
||||||
- [Theodore Stoddard](https://github.com/TStod): Code contributions
|
|
||||||
- [Philip Loche](https://github.com/PicoCentauri): Code contributions
|
|
||||||
- [Patrick Heyer](https://github.com/PatTheMav): Code contributions and CI fixes
|
|
||||||
- [Alex Van Camp](https://github.com/Lange): Code contributions
|
|
||||||
- [Freddie Meyer](https://github.com/DungFu): Code contributions
|
|
||||||
- [Casey Muller](https://github.com/caseymrm): CI fixes
|
|
||||||
- [Chris Angelico](https://github.com/Rosuav): Documentation fixes
|
|
||||||
|
|
||||||
And also: special thanks to supporters of the project!
|
And also: special thanks to supporters of the project!
|
||||||
|
|
||||||
## Supporters
|
## Supporters
|
||||||
|
|
||||||
They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
|
These supporters have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
45
SSL-TUNNELLING.md
Normal file
45
SSL-TUNNELLING.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Connecting over a TLS/secure connection (or remotely)
|
||||||
|
|
||||||
|
If you want to expose the WebSocket server of obs-websocket over a secure TLS connection (or to connect remotely), the easiest approach is to use a localhost tunneling service like [ngrok](https://ngrok.com/) or [pagekite](https://pagekite.net/).
|
||||||
|
|
||||||
|
**Before doing this, secure the WebSocket server first by enabling authentication with a strong password!**
|
||||||
|
|
||||||
|
**Please bear in mind that doing this will expose your OBS instance to the open Internet and the security risks it implies. *You've been warned!***
|
||||||
|
|
||||||
|
|
||||||
|
## ngrok
|
||||||
|
|
||||||
|
[Install the ngrok CLI tool](https://ngrok.com/download) on a linux OS, then start ngrok bound to port 4444 like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok http 4444
|
||||||
|
```
|
||||||
|
|
||||||
|
The ngrok command will output something like this:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ngrok by @inconshreveable
|
||||||
|
|
||||||
|
Tunnel Status online
|
||||||
|
Version 2.0/2.0
|
||||||
|
Web Interface http://127.0.0.1:4040
|
||||||
|
Forwarding http://TUNNEL_ID.ngrok.io -> localhost:4444
|
||||||
|
Forwarding https://TUNNEL_ID.ngrok.io -> localhost:4444
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `TUNNEL_ID` is, as the name implies, the unique name of your ngrok tunnel. You'll get a new one every time you start ngrok.
|
||||||
|
|
||||||
|
Then, use `wss://TUNNEL_ID.ngrok.io` to connect to obs-websocket over TLS.
|
||||||
|
|
||||||
|
See the [ngrok documentation](https://ngrok.com/docs) for more tunneling options and settings.
|
||||||
|
|
||||||
|
|
||||||
|
## PageKite
|
||||||
|
|
||||||
|
[Install the PageKite CLI tool](http://pagekite.net/downloads), then start PageKite bound to port 4444 like this (replace NAME with one of your choosing):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python pagekite.py 4444 NAME.pagekite.me
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, use `wss://NAME.pagekite.me` to connect to obs-websocket over TLS.
|
@ -4,7 +4,7 @@ variables:
|
|||||||
trigger:
|
trigger:
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- master
|
- '4.x-current'
|
||||||
tags:
|
tags:
|
||||||
include:
|
include:
|
||||||
- '*'
|
- '*'
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
obs | "$(Agent.OS)"
|
obs | "$(Agent.OS)"
|
||||||
path: $(OBSPath)
|
path: $(OBSPath)
|
||||||
|
|
||||||
- script: ./CI/checkout-cmake-obs-windows.cmd
|
- script: ./CI/prepare-obs-windows.cmd
|
||||||
displayName: 'Checkout & CMake OBS Studio'
|
displayName: 'Checkout & CMake OBS Studio'
|
||||||
env:
|
env:
|
||||||
build_config: $(build_config)
|
build_config: $(build_config)
|
||||||
|
@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="Enable WebSockets server"
|
|||||||
OBSWebsocket.Settings.ServerPort="Server Port"
|
OBSWebsocket.Settings.ServerPort="Server Port"
|
||||||
OBSWebsocket.Settings.AuthRequired="Enable authentication"
|
OBSWebsocket.Settings.AuthRequired="Enable authentication"
|
||||||
OBSWebsocket.Settings.Password="Password"
|
OBSWebsocket.Settings.Password="Password"
|
||||||
|
OBSWebsocket.Settings.LockToIPv4="Lock server to only using IPv4"
|
||||||
OBSWebsocket.Settings.DebugEnable="Enable debug logging"
|
OBSWebsocket.Settings.DebugEnable="Enable debug logging"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts"
|
OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts"
|
||||||
OBSWebsocket.NotifyConnect.Title="New WebSocket connection"
|
OBSWebsocket.NotifyConnect.Title="New WebSocket connection"
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
|||||||
# obs-websocket 4.7.0 protocol reference
|
# obs-websocket 4.8.0 protocol reference
|
||||||
|
|
||||||
# General Introduction
|
# General Introduction
|
||||||
Messages are exchanged between the client and the server as JSON objects.
|
Messages are exchanged between the client and the server as JSON objects.
|
||||||
This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio.
|
This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. As of v5.0.0, backwards compatability with the protocol will not be kept.
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
`obs-websocket` uses SHA256 to transmit credentials.
|
`obs-websocket` uses SHA256 to transmit credentials.
|
||||||
@ -32,3 +32,5 @@ auth_response_string = secret + challenge
|
|||||||
auth_response_hash = binary_sha256(auth_response_string)
|
auth_response_hash = binary_sha256(auth_response_string)
|
||||||
auth_response = base64_encode(auth_response_hash)
|
auth_response = base64_encode(auth_response_hash)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also refer to any of the client libraries listed on the [README](README.md) for examples of how to authenticate.
|
||||||
|
@ -6,6 +6,6 @@ Requests are sent by the client and require at least the following two fields:
|
|||||||
Once a request is sent, the server will return a JSON response with at least the following fields:
|
Once a request is sent, the server will return a JSON response with at least the following fields:
|
||||||
- `message-id` _String_: The client defined identifier specified in the request.
|
- `message-id` _String_: The client defined identifier specified in the request.
|
||||||
- `status` _String_: Response status, will be one of the following: `ok`, `error`
|
- `status` _String_: Response status, will be one of the following: `ok`, `error`
|
||||||
- `error` _String_: An error message accompanying an `error` status.
|
- `error` _String (Optional)_: An error message accompanying an `error` status.
|
||||||
|
|
||||||
Additional information may be required/returned depending on the request type. See below for more information.
|
Additional information may be required/returned depending on the request type. See below for more information.
|
||||||
|
@ -23,6 +23,7 @@ DefaultGroupName={#MyAppName}
|
|||||||
OutputBaseFilename=obs-websocket-Windows-Installer
|
OutputBaseFilename=obs-websocket-Windows-Installer
|
||||||
Compression=lzma
|
Compression=lzma
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
|
DirExistsWarning=no
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
@ -25,6 +25,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define SECTION_NAME "WebsocketAPI"
|
#define SECTION_NAME "WebsocketAPI"
|
||||||
#define PARAM_ENABLE "ServerEnabled"
|
#define PARAM_ENABLE "ServerEnabled"
|
||||||
#define PARAM_PORT "ServerPort"
|
#define PARAM_PORT "ServerPort"
|
||||||
|
#define PARAM_LOCKTOIPV4 "LockToIPv4"
|
||||||
#define PARAM_DEBUG "DebugEnabled"
|
#define PARAM_DEBUG "DebugEnabled"
|
||||||
#define PARAM_ALERT "AlertsEnabled"
|
#define PARAM_ALERT "AlertsEnabled"
|
||||||
#define PARAM_AUTHREQUIRED "AuthRequired"
|
#define PARAM_AUTHREQUIRED "AuthRequired"
|
||||||
@ -41,6 +42,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
Config::Config() :
|
Config::Config() :
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4444),
|
ServerPort(4444),
|
||||||
|
LockToIPv4(false),
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(true),
|
AlertsEnabled(true),
|
||||||
AuthRequired(false),
|
AuthRequired(false),
|
||||||
@ -67,6 +69,7 @@ void Config::Load()
|
|||||||
|
|
||||||
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
|
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
|
||||||
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
|
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
|
||||||
|
LockToIPv4 = config_get_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4);
|
||||||
|
|
||||||
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
|
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
|
||||||
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
|
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
|
||||||
@ -82,6 +85,7 @@ void Config::Save()
|
|||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4);
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
||||||
@ -104,6 +108,8 @@ void Config::SetDefaults()
|
|||||||
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
config_set_default_uint(obsConfig,
|
config_set_default_uint(obsConfig,
|
||||||
SECTION_NAME, PARAM_PORT, ServerPort);
|
SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
config_set_default_bool(obsConfig,
|
||||||
|
SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4);
|
||||||
|
|
||||||
config_set_default_bool(obsConfig,
|
config_set_default_bool(obsConfig,
|
||||||
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
@ -205,16 +211,17 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param)
|
|||||||
|
|
||||||
bool previousEnabled = config->ServerEnabled;
|
bool previousEnabled = config->ServerEnabled;
|
||||||
uint64_t previousPort = config->ServerPort;
|
uint64_t previousPort = config->ServerPort;
|
||||||
|
bool previousLock = config->LockToIPv4;
|
||||||
|
|
||||||
config->SetDefaults();
|
config->SetDefaults();
|
||||||
config->Load();
|
config->Load();
|
||||||
|
|
||||||
if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort) {
|
if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort || config->LockToIPv4 != previousLock) {
|
||||||
auto server = GetServer();
|
auto server = GetServer();
|
||||||
server->stop();
|
server->stop();
|
||||||
|
|
||||||
if (config->ServerEnabled) {
|
if (config->ServerEnabled) {
|
||||||
server->start(config->ServerPort);
|
server->start(config->ServerPort, config->LockToIPv4);
|
||||||
|
|
||||||
if (previousEnabled != config->ServerEnabled) {
|
if (previousEnabled != config->ServerEnabled) {
|
||||||
Utils::SysTrayNotify(startMessage, QSystemTrayIcon::MessageIcon::Information);
|
Utils::SysTrayNotify(startMessage, QSystemTrayIcon::MessageIcon::Information);
|
||||||
@ -247,6 +254,13 @@ void Config::MigrateFromGlobalSettings()
|
|||||||
config_remove_value(source, SECTION_NAME, PARAM_PORT);
|
config_remove_value(source, SECTION_NAME, PARAM_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(config_has_user_value(source, SECTION_NAME, PARAM_LOCKTOIPV4)) {
|
||||||
|
bool value = config_get_bool(source, SECTION_NAME, PARAM_LOCKTOIPV4);
|
||||||
|
config_set_bool(destination, SECTION_NAME, PARAM_LOCKTOIPV4, value);
|
||||||
|
|
||||||
|
config_remove_value(source, SECTION_NAME, PARAM_LOCKTOIPV4);
|
||||||
|
}
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) {
|
if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) {
|
||||||
bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG);
|
bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG);
|
||||||
config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value);
|
config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value);
|
||||||
|
@ -42,6 +42,7 @@ class Config {
|
|||||||
|
|
||||||
bool ServerEnabled;
|
bool ServerEnabled;
|
||||||
uint64_t ServerPort;
|
uint64_t ServerPort;
|
||||||
|
bool LockToIPv4;
|
||||||
|
|
||||||
bool DebugEnabled;
|
bool DebugEnabled;
|
||||||
bool AlertsEnabled;
|
bool AlertsEnabled;
|
||||||
|
@ -51,6 +51,25 @@ obs_bounds_type getBoundsTypeFromName(QString name) {
|
|||||||
return boundTypeNames.key(name);
|
return boundTypeNames.key(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Utils::StringInStringList(char** strings, const char* string) {
|
||||||
|
if (!strings) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
while (strings[index] != NULL) {
|
||||||
|
char* value = strings[index];
|
||||||
|
|
||||||
|
if (strcmp(value, string) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_array_t* Utils::StringListToArray(char** strings, const char* key) {
|
obs_data_array_t* Utils::StringListToArray(char** strings, const char* key) {
|
||||||
obs_data_array_t* list = obs_data_array_create();
|
obs_data_array_t* list = obs_data_array_create();
|
||||||
|
|
||||||
@ -485,11 +504,8 @@ QString Utils::OBSVersionString() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QSystemTrayIcon* Utils::GetTrayIcon() {
|
QSystemTrayIcon* Utils::GetTrayIcon() {
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
void* systemTray = obs_frontend_get_system_tray();
|
||||||
if (!main) return nullptr;
|
return reinterpret_cast<QSystemTrayIcon*>(systemTray);
|
||||||
|
|
||||||
QList<QSystemTrayIcon*> trays = main->findChildren<QSystemTrayIcon*>();
|
|
||||||
return trays.isEmpty() ? nullptr : trays.first();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::SysTrayNotify(QString text,
|
void Utils::SysTrayNotify(QString text,
|
||||||
@ -684,8 +700,8 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} `SceneItemTransform`
|
* @typedef {Object} `SceneItemTransform`
|
||||||
* @property {int} `position.x` The x position of the scene item from the left.
|
* @property {double} `position.x` The x position of the scene item from the left.
|
||||||
* @property {int} `position.y` The y position of the scene item from the top.
|
* @property {double} `position.y` The y position of the scene item from the top.
|
||||||
* @property {int} `position.alignment` The point on the scene item that the item is manipulated from.
|
* @property {int} `position.alignment` The point on the scene item that the item is manipulated from.
|
||||||
* @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.
|
* @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.
|
||||||
* @property {double} `scale.x` The x-scale factor of the scene item.
|
* @property {double} `scale.x` The x-scale factor of the scene item.
|
||||||
|
@ -35,6 +35,7 @@ typedef void(*PauseRecordingFunction)(bool);
|
|||||||
typedef bool(*RecordingPausedFunction)();
|
typedef bool(*RecordingPausedFunction)();
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
bool StringInStringList(char** strings, const char* string);
|
||||||
obs_data_array_t* StringListToArray(char** strings, const char* key);
|
obs_data_array_t* StringListToArray(char** strings, const char* key);
|
||||||
obs_data_array_t* GetSceneItems(obs_source_t* source);
|
obs_data_array_t* GetSceneItems(obs_source_t* source);
|
||||||
obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
|
obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
|
||||||
|
273
src/WSEvents.cpp
273
src/WSEvents.cpp
@ -271,11 +271,22 @@ void WSEvents::connectSourceSignals(obs_source_t* source) {
|
|||||||
signal_handler_connect(sh, "volume", OnSourceVolumeChange, this);
|
signal_handler_connect(sh, "volume", OnSourceVolumeChange, this);
|
||||||
signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this);
|
signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this);
|
||||||
signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, this);
|
signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, this);
|
||||||
|
signal_handler_connect(sh, "audio_activate", OnSourceAudioActivated, this);
|
||||||
|
signal_handler_connect(sh, "audio_deactivate", OnSourceAudioDeactivated, this);
|
||||||
|
|
||||||
signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this);
|
signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this);
|
||||||
signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this);
|
signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this);
|
||||||
signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this);
|
signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this);
|
||||||
|
|
||||||
|
signal_handler_connect(sh, "media_play", OnMediaPlaying, this);
|
||||||
|
signal_handler_connect(sh, "media_pause", OnMediaPaused, this);
|
||||||
|
signal_handler_connect(sh, "media_restart", OnMediaRestarted, this);
|
||||||
|
signal_handler_connect(sh, "media_stopped", OnMediaStopped, this);
|
||||||
|
signal_handler_connect(sh, "media_next", OnMediaNext, this);
|
||||||
|
signal_handler_connect(sh, "media_previous", OnMediaPrevious, this);
|
||||||
|
signal_handler_connect(sh, "media_started", OnMediaStarted, this);
|
||||||
|
signal_handler_connect(sh, "media_ended", OnMediaEnded, this);
|
||||||
|
|
||||||
if (sourceType == OBS_SOURCE_TYPE_SCENE) {
|
if (sourceType == OBS_SOURCE_TYPE_SCENE) {
|
||||||
signal_handler_connect(sh, "reorder", OnSceneReordered, this);
|
signal_handler_connect(sh, "reorder", OnSceneReordered, this);
|
||||||
signal_handler_connect(sh, "item_add", OnSceneItemAdd, this);
|
signal_handler_connect(sh, "item_add", OnSceneItemAdd, this);
|
||||||
@ -303,6 +314,8 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) {
|
|||||||
signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this);
|
signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this);
|
||||||
signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this);
|
signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this);
|
||||||
signal_handler_disconnect(sh, "audio_mixers", OnSourceAudioMixersChanged, this);
|
signal_handler_disconnect(sh, "audio_mixers", OnSourceAudioMixersChanged, this);
|
||||||
|
signal_handler_disconnect(sh, "audio_activate", OnSourceAudioActivated, this);
|
||||||
|
signal_handler_disconnect(sh, "audio_deactivate", OnSourceAudioDeactivated, this);
|
||||||
|
|
||||||
signal_handler_disconnect(sh, "filter_add", OnSourceFilterAdded, this);
|
signal_handler_disconnect(sh, "filter_add", OnSourceFilterAdded, this);
|
||||||
signal_handler_disconnect(sh, "filter_remove", OnSourceFilterRemoved, this);
|
signal_handler_disconnect(sh, "filter_remove", OnSourceFilterRemoved, this);
|
||||||
@ -322,6 +335,15 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) {
|
|||||||
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
|
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
|
||||||
signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this);
|
signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this);
|
||||||
signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this);
|
signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this);
|
||||||
|
|
||||||
|
signal_handler_disconnect(sh, "media_play", OnMediaPlaying, this);
|
||||||
|
signal_handler_disconnect(sh, "media_pause", OnMediaPaused, this);
|
||||||
|
signal_handler_disconnect(sh, "media_restart", OnMediaRestarted, this);
|
||||||
|
signal_handler_disconnect(sh, "media_stopped", OnMediaStopped, this);
|
||||||
|
signal_handler_disconnect(sh, "media_next", OnMediaNext, this);
|
||||||
|
signal_handler_disconnect(sh, "media_previous", OnMediaPrevious, this);
|
||||||
|
signal_handler_disconnect(sh, "media_started", OnMediaStarted, this);
|
||||||
|
signal_handler_disconnect(sh, "media_ended", OnMediaEnded, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::connectFilterSignals(obs_source_t* filter) {
|
void WSEvents::connectFilterSignals(obs_source_t* filter) {
|
||||||
@ -348,7 +370,7 @@ void WSEvents::hookTransitionPlaybackEvents() {
|
|||||||
obs_frontend_source_list transitions = {};
|
obs_frontend_source_list transitions = {};
|
||||||
obs_frontend_get_transitions(&transitions);
|
obs_frontend_get_transitions(&transitions);
|
||||||
|
|
||||||
for (int i = 0; i < transitions.sources.num; i++) {
|
for (uint i = 0; i < transitions.sources.num; i++) {
|
||||||
obs_source_t* transition = transitions.sources.array[i];
|
obs_source_t* transition = transitions.sources.array[i];
|
||||||
signal_handler_t* sh = obs_source_get_signal_handler(transition);
|
signal_handler_t* sh = obs_source_get_signal_handler(transition);
|
||||||
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
|
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
|
||||||
@ -366,7 +388,7 @@ void WSEvents::unhookTransitionPlaybackEvents() {
|
|||||||
obs_frontend_source_list transitions = {};
|
obs_frontend_source_list transitions = {};
|
||||||
obs_frontend_get_transitions(&transitions);
|
obs_frontend_get_transitions(&transitions);
|
||||||
|
|
||||||
for (int i = 0; i < transitions.sources.num; i++) {
|
for (uint i = 0; i < transitions.sources.num; i++) {
|
||||||
obs_source_t* transition = transitions.sources.array[i];
|
obs_source_t* transition = transitions.sources.array[i];
|
||||||
signal_handler_t* sh = obs_source_get_signal_handler(transition);
|
signal_handler_t* sh = obs_source_get_signal_handler(transition);
|
||||||
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
|
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
|
||||||
@ -407,6 +429,16 @@ QString WSEvents::getRecordingTimecode() {
|
|||||||
return Utils::nsToTimestamp(getRecordingTime());
|
return Utils::nsToTimestamp(getRecordingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease getMediaSourceData(calldata_t* data) {
|
||||||
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
|
OBSSource source = calldata_get_pointer<obs_source_t>(data, "source");
|
||||||
|
|
||||||
|
obs_data_set_string(fields, "sourceName", obs_source_get_name(source));
|
||||||
|
obs_data_set_string(fields, "sourceKind", obs_source_get_id(source));
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates a scene change.
|
* Indicates a scene change.
|
||||||
*
|
*
|
||||||
@ -433,6 +465,8 @@ void WSEvents::OnSceneChange() {
|
|||||||
* The scene list has been modified.
|
* The scene list has been modified.
|
||||||
* Scenes have been added, removed, or renamed.
|
* Scenes have been added, removed, or renamed.
|
||||||
*
|
*
|
||||||
|
* Note: This event is not fired when the scenes are reordered.
|
||||||
|
*
|
||||||
* @api events
|
* @api events
|
||||||
* @name ScenesChanged
|
* @name ScenesChanged
|
||||||
* @category scenes
|
* @category scenes
|
||||||
@ -724,7 +758,7 @@ void WSEvents::OnExit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit every 2 seconds.
|
* Emitted every 2 seconds when stream is active.
|
||||||
*
|
*
|
||||||
* @return {boolean} `streaming` Current streaming state.
|
* @return {boolean} `streaming` Current streaming state.
|
||||||
* @return {boolean} `recording` Current recording state.
|
* @return {boolean} `recording` Current recording state.
|
||||||
@ -831,6 +865,7 @@ void WSEvents::StreamStatus() {
|
|||||||
* @api events
|
* @api events
|
||||||
* @name Heartbeat
|
* @name Heartbeat
|
||||||
* @category general
|
* @category general
|
||||||
|
* @since v0.3
|
||||||
*/
|
*/
|
||||||
void WSEvents::Heartbeat() {
|
void WSEvents::Heartbeat() {
|
||||||
|
|
||||||
@ -1098,6 +1133,52 @@ void WSEvents::OnSourceMuteStateChange(void* param, calldata_t* data) {
|
|||||||
self->broadcastUpdate("SourceMuteStateChanged", fields);
|
self->broadcastUpdate("SourceMuteStateChanged", fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source has removed audio.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name SourceAudioDeactivated
|
||||||
|
* @category sources
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnSourceAudioDeactivated(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSSource source = calldata_get_pointer<obs_source_t>(data, "source");
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "sourceName", obs_source_get_name(source));
|
||||||
|
self->broadcastUpdate("SourceAudioDeactivated", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source has added audio.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name SourceAudioActivated
|
||||||
|
* @category sources
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnSourceAudioActivated(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSSource source = calldata_get_pointer<obs_source_t>(data, "source");
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "sourceName", obs_source_get_name(source));
|
||||||
|
self->broadcastUpdate("SourceAudioActivated", fields);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The audio sync offset of a source has changed.
|
* The audio sync offset of a source has changed.
|
||||||
*
|
*
|
||||||
@ -1177,6 +1258,7 @@ void WSEvents::OnSourceAudioMixersChanged(void* param, calldata_t* data) {
|
|||||||
*
|
*
|
||||||
* @return {String} `previousName` Previous source name
|
* @return {String} `previousName` Previous source name
|
||||||
* @return {String} `newName` New source name
|
* @return {String} `newName` New source name
|
||||||
|
* @return {String} `sourceType` Type of source (input, scene, filter, transition)
|
||||||
*
|
*
|
||||||
* @api events
|
* @api events
|
||||||
* @name SourceRenamed
|
* @name SourceRenamed
|
||||||
@ -1201,6 +1283,8 @@ void WSEvents::OnSourceRename(void* param, calldata_t* data) {
|
|||||||
OBSDataAutoRelease fields = obs_data_create();
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
obs_data_set_string(fields, "previousName", previousName);
|
obs_data_set_string(fields, "previousName", previousName);
|
||||||
obs_data_set_string(fields, "newName", newName);
|
obs_data_set_string(fields, "newName", newName);
|
||||||
|
obs_data_set_string(fields, "sourceType",
|
||||||
|
sourceTypeToString(obs_source_get_type(source))); // TODO: Split into dedicated events for source/scene. Only doing it this way for backwards compatability until 5.0
|
||||||
self->broadcastUpdate("SourceRenamed", fields);
|
self->broadcastUpdate("SourceRenamed", fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1313,6 +1397,7 @@ void WSEvents::OnSourceFilterVisibilityChanged(void* param, calldata_t* data) {
|
|||||||
* @return {Array<Object>} `filters` Ordered Filters list
|
* @return {Array<Object>} `filters` Ordered Filters list
|
||||||
* @return {String} `filters.*.name` Filter name
|
* @return {String} `filters.*.name` Filter name
|
||||||
* @return {String} `filters.*.type` Filter type
|
* @return {String} `filters.*.type` Filter type
|
||||||
|
* @return {boolean} `filters.*.enabled` Filter visibility status
|
||||||
*
|
*
|
||||||
* @api events
|
* @api events
|
||||||
* @name SourceFiltersReordered
|
* @name SourceFiltersReordered
|
||||||
@ -1336,7 +1421,175 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scene items have been reordered.
|
* A media source has started playing.
|
||||||
|
*
|
||||||
|
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaPlaying
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaPlaying(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaPlaying", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has been paused.
|
||||||
|
*
|
||||||
|
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaPaused
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaPaused(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaPaused", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has been restarted.
|
||||||
|
*
|
||||||
|
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaRestarted
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaRestarted(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaRestarted", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has been stopped.
|
||||||
|
*
|
||||||
|
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaStopped
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaStopped(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaStopped", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has gone to the next item in the playlist.
|
||||||
|
*
|
||||||
|
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaNext
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaNext(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaNext", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has gone to the previous item in the playlist.
|
||||||
|
*
|
||||||
|
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaPrevious
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaPrevious(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaPrevious", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has been started.
|
||||||
|
*
|
||||||
|
* Note: These events are emitted by the OBS sources themselves. For example when the media file starts playing. The behavior depends on the type of media source being used.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaStarted
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaStarted(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaStarted", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A media source has ended.
|
||||||
|
*
|
||||||
|
* Note: These events are emitted by the OBS sources themselves. For example when the media file ends. The behavior depends on the type of media source being used.
|
||||||
|
*
|
||||||
|
* @return {String} `sourceName` Source name
|
||||||
|
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
|
||||||
|
*
|
||||||
|
* @api events
|
||||||
|
* @name MediaEnded
|
||||||
|
* @category media
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
void WSEvents::OnMediaEnded(void* param, calldata_t* data) {
|
||||||
|
auto self = reinterpret_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = getMediaSourceData(data);
|
||||||
|
|
||||||
|
self->broadcastUpdate("MediaEnded", fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scene items within a scene have been reordered.
|
||||||
*
|
*
|
||||||
* @return {String} `scene-name` Name of the scene where items have been reordered.
|
* @return {String} `scene-name` Name of the scene where items have been reordered.
|
||||||
* @return {Array<Object>} `scene-items` Ordered list of scene items
|
* @return {Array<Object>} `scene-items` Ordered list of scene items
|
||||||
@ -1379,7 +1632,7 @@ void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item has been added to the current scene.
|
* A scene item has been added to a scene.
|
||||||
*
|
*
|
||||||
* @return {String} `scene-name` Name of the scene.
|
* @return {String} `scene-name` Name of the scene.
|
||||||
* @return {String} `item-name` Name of the item added to the scene.
|
* @return {String} `item-name` Name of the item added to the scene.
|
||||||
@ -1412,7 +1665,7 @@ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item has been removed from the current scene.
|
* A scene item has been removed from a scene.
|
||||||
*
|
*
|
||||||
* @return {String} `scene-name` Name of the scene.
|
* @return {String} `scene-name` Name of the scene.
|
||||||
* @return {String} `item-name` Name of the item removed from the scene.
|
* @return {String} `item-name` Name of the item removed from the scene.
|
||||||
@ -1445,7 +1698,7 @@ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item's visibility has been toggled.
|
* A scene item's visibility has been toggled.
|
||||||
*
|
*
|
||||||
* @return {String} `scene-name` Name of the scene.
|
* @return {String} `scene-name` Name of the scene.
|
||||||
* @return {String} `item-name` Name of the item in the scene.
|
* @return {String} `item-name` Name of the item in the scene.
|
||||||
@ -1483,7 +1736,7 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item's locked status has been toggled.
|
* A scene item's locked status has been toggled.
|
||||||
*
|
*
|
||||||
* @return {String} `scene-name` Name of the scene.
|
* @return {String} `scene-name` Name of the scene.
|
||||||
* @return {String} `item-name` Name of the item in the scene.
|
* @return {String} `item-name` Name of the item in the scene.
|
||||||
@ -1521,7 +1774,7 @@ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item's transform has been changed.
|
* A scene item's transform has been changed.
|
||||||
*
|
*
|
||||||
* @return {String} `scene-name` Name of the scene.
|
* @return {String} `scene-name` Name of the scene.
|
||||||
* @return {String} `item-name` Name of the item in the scene.
|
* @return {String} `item-name` Name of the item in the scene.
|
||||||
@ -1672,7 +1925,7 @@ void WSEvents::OnStudioModeSwitched(bool checked) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom broadcast message was received
|
* A custom broadcast message, sent by the server, requested by one of the websocket clients.
|
||||||
*
|
*
|
||||||
* @return {String} `realm` Identifier provided by the sender
|
* @return {String} `realm` Identifier provided by the sender
|
||||||
* @return {Object} `data` User-defined data
|
* @return {Object} `data` User-defined data
|
||||||
|
@ -126,6 +126,8 @@ private:
|
|||||||
static void OnSourceMuteStateChange(void* param, calldata_t* data);
|
static void OnSourceMuteStateChange(void* param, calldata_t* data);
|
||||||
static void OnSourceAudioSyncOffsetChanged(void* param, calldata_t* data);
|
static void OnSourceAudioSyncOffsetChanged(void* param, calldata_t* data);
|
||||||
static void OnSourceAudioMixersChanged(void* param, calldata_t* data);
|
static void OnSourceAudioMixersChanged(void* param, calldata_t* data);
|
||||||
|
static void OnSourceAudioActivated(void* param, calldata_t* data);
|
||||||
|
static void OnSourceAudioDeactivated(void* param, calldata_t* data);
|
||||||
|
|
||||||
static void OnSourceRename(void* param, calldata_t* data);
|
static void OnSourceRename(void* param, calldata_t* data);
|
||||||
|
|
||||||
@ -134,6 +136,15 @@ private:
|
|||||||
static void OnSourceFilterVisibilityChanged(void* param, calldata_t* data);
|
static void OnSourceFilterVisibilityChanged(void* param, calldata_t* data);
|
||||||
static void OnSourceFilterOrderChanged(void* param, calldata_t* data);
|
static void OnSourceFilterOrderChanged(void* param, calldata_t* data);
|
||||||
|
|
||||||
|
static void OnMediaPlaying(void* param, calldata_t* data);
|
||||||
|
static void OnMediaPaused(void* param, calldata_t* data);
|
||||||
|
static void OnMediaRestarted(void* param, calldata_t* data);
|
||||||
|
static void OnMediaStopped(void* param, calldata_t* data);
|
||||||
|
static void OnMediaNext(void* param, calldata_t* data);
|
||||||
|
static void OnMediaPrevious(void* param, calldata_t* data);
|
||||||
|
static void OnMediaStarted(void* param, calldata_t* data);
|
||||||
|
static void OnMediaEnded(void* param, calldata_t* data);
|
||||||
|
|
||||||
static void OnSceneReordered(void* param, calldata_t* data);
|
static void OnSceneReordered(void* param, calldata_t* data);
|
||||||
static void OnSceneItemAdd(void* param, calldata_t* data);
|
static void OnSceneItemAdd(void* param, calldata_t* data);
|
||||||
static void OnSceneItemDelete(void* param, calldata_t* data);
|
static void OnSceneItemDelete(void* param, calldata_t* data);
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap{
|
||||||
{ "GetVersion", &WSRequestHandler::GetVersion },
|
{ "GetVersion", &WSRequestHandler::GetVersion },
|
||||||
{ "GetAuthRequired", &WSRequestHandler::GetAuthRequired },
|
{ "GetAuthRequired", &WSRequestHandler::GetAuthRequired },
|
||||||
{ "Authenticate", &WSRequestHandler::Authenticate },
|
{ "Authenticate", &WSRequestHandler::Authenticate },
|
||||||
@ -43,11 +43,19 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
|
|
||||||
{ "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage },
|
{ "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage },
|
||||||
|
|
||||||
|
{ "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName },
|
||||||
|
{ "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence },
|
||||||
|
|
||||||
{ "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
|
{ "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
|
||||||
{ "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
|
{ "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
|
||||||
{ "GetSceneList", &WSRequestHandler::GetSceneList },
|
{ "GetSceneList", &WSRequestHandler::GetSceneList },
|
||||||
|
{ "CreateScene", &WSRequestHandler::CreateScene },
|
||||||
|
{ "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride },
|
||||||
|
{ "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride },
|
||||||
|
{ "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride },
|
||||||
|
|
||||||
{ "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat
|
{ "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat
|
||||||
|
{ "GetSceneItemList", &WSRequestHandler::GetSceneItemList },
|
||||||
{ "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender },
|
{ "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender },
|
||||||
{ "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition },
|
{ "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition },
|
||||||
{ "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform },
|
{ "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform },
|
||||||
@ -56,6 +64,7 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
{ "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties },
|
{ "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties },
|
||||||
{ "ResetSceneItem", &WSRequestHandler::ResetSceneItem },
|
{ "ResetSceneItem", &WSRequestHandler::ResetSceneItem },
|
||||||
{ "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem },
|
{ "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem },
|
||||||
|
{ "AddSceneItem", &WSRequestHandler::AddSceneItem },
|
||||||
{ "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem },
|
{ "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem },
|
||||||
{ "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems },
|
{ "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems },
|
||||||
|
|
||||||
@ -66,11 +75,13 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
{ "StartStreaming", &WSRequestHandler::StartStreaming },
|
{ "StartStreaming", &WSRequestHandler::StartStreaming },
|
||||||
{ "StopStreaming", &WSRequestHandler::StopStreaming },
|
{ "StopStreaming", &WSRequestHandler::StopStreaming },
|
||||||
|
|
||||||
|
{ "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus },
|
||||||
{ "StartRecording", &WSRequestHandler::StartRecording },
|
{ "StartRecording", &WSRequestHandler::StartRecording },
|
||||||
{ "StopRecording", &WSRequestHandler::StopRecording },
|
{ "StopRecording", &WSRequestHandler::StopRecording },
|
||||||
{ "PauseRecording", &WSRequestHandler::PauseRecording },
|
{ "PauseRecording", &WSRequestHandler::PauseRecording },
|
||||||
{ "ResumeRecording", &WSRequestHandler::ResumeRecording },
|
{ "ResumeRecording", &WSRequestHandler::ResumeRecording },
|
||||||
|
|
||||||
|
{ "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus },
|
||||||
{ "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer },
|
{ "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer },
|
||||||
{ "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer },
|
{ "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer },
|
||||||
{ "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer },
|
{ "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer },
|
||||||
@ -84,6 +95,9 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
{ "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition },
|
{ "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition },
|
||||||
{ "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration },
|
{ "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration },
|
||||||
{ "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration },
|
{ "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration },
|
||||||
|
{ "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition },
|
||||||
|
{ "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings },
|
||||||
|
{ "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings },
|
||||||
{ "ReleaseTBar", &WSRequestHandler::ReleaseTBar },
|
{ "ReleaseTBar", &WSRequestHandler::ReleaseTBar },
|
||||||
{ "SetTBarPosition", &WSRequestHandler::SetTBarPosition },
|
{ "SetTBarPosition", &WSRequestHandler::SetTBarPosition },
|
||||||
|
|
||||||
@ -92,6 +106,8 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
{ "ToggleMute", &WSRequestHandler::ToggleMute },
|
{ "ToggleMute", &WSRequestHandler::ToggleMute },
|
||||||
{ "SetMute", &WSRequestHandler::SetMute },
|
{ "SetMute", &WSRequestHandler::SetMute },
|
||||||
{ "GetMute", &WSRequestHandler::GetMute },
|
{ "GetMute", &WSRequestHandler::GetMute },
|
||||||
|
{ "GetAudioActive", &WSRequestHandler::GetAudioActive },
|
||||||
|
{ "SetSourceName", &WSRequestHandler::SetSourceName },
|
||||||
{ "SetSyncOffset", &WSRequestHandler::SetSyncOffset },
|
{ "SetSyncOffset", &WSRequestHandler::SetSyncOffset },
|
||||||
{ "GetSyncOffset", &WSRequestHandler::GetSyncOffset },
|
{ "GetSyncOffset", &WSRequestHandler::GetSyncOffset },
|
||||||
{ "GetSpecialSources", &WSRequestHandler::GetSpecialSources },
|
{ "GetSpecialSources", &WSRequestHandler::GetSpecialSources },
|
||||||
@ -99,6 +115,8 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
{ "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList },
|
{ "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList },
|
||||||
{ "GetSourceSettings", &WSRequestHandler::GetSourceSettings },
|
{ "GetSourceSettings", &WSRequestHandler::GetSourceSettings },
|
||||||
{ "SetSourceSettings", &WSRequestHandler::SetSourceSettings },
|
{ "SetSourceSettings", &WSRequestHandler::SetSourceSettings },
|
||||||
|
{ "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType },
|
||||||
|
{ "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType },
|
||||||
{ "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot },
|
{ "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot },
|
||||||
|
|
||||||
{ "GetSourceFilters", &WSRequestHandler::GetSourceFilters },
|
{ "GetSourceFilters", &WSRequestHandler::GetSourceFilters },
|
||||||
@ -145,7 +163,19 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
|
|||||||
{ "ListOutputs", &WSRequestHandler::ListOutputs },
|
{ "ListOutputs", &WSRequestHandler::ListOutputs },
|
||||||
{ "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
|
{ "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
|
||||||
{ "StartOutput", &WSRequestHandler::StartOutput },
|
{ "StartOutput", &WSRequestHandler::StartOutput },
|
||||||
{ "StopOutput", &WSRequestHandler::StopOutput }
|
{ "StopOutput", &WSRequestHandler::StopOutput },
|
||||||
|
|
||||||
|
{ "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia },
|
||||||
|
{ "RestartMedia", &WSRequestHandler::RestartMedia },
|
||||||
|
{ "StopMedia", &WSRequestHandler::StopMedia },
|
||||||
|
{ "NextMedia", &WSRequestHandler::NextMedia },
|
||||||
|
{ "PreviousMedia", &WSRequestHandler::PreviousMedia },
|
||||||
|
{ "GetMediaDuration", &WSRequestHandler::GetMediaDuration },
|
||||||
|
{ "GetMediaTime", &WSRequestHandler::GetMediaTime },
|
||||||
|
{ "SetMediaTime", &WSRequestHandler::SetMediaTime },
|
||||||
|
{ "ScrubMedia", &WSRequestHandler::ScrubMedia },
|
||||||
|
{ "GetMediaState", &WSRequestHandler::GetMediaState },
|
||||||
|
{ "GetMediaSourcesList", &WSRequestHandler::GetMediaSourcesList }
|
||||||
};
|
};
|
||||||
|
|
||||||
const QSet<QString> WSRequestHandler::authNotRequired {
|
const QSet<QString> WSRequestHandler::authNotRequired {
|
||||||
|
@ -61,10 +61,18 @@ class WSRequestHandler {
|
|||||||
|
|
||||||
RpcResponse BroadcastCustomMessage(const RpcRequest&);
|
RpcResponse BroadcastCustomMessage(const RpcRequest&);
|
||||||
|
|
||||||
|
RpcResponse TriggerHotkeyByName(const RpcRequest&);
|
||||||
|
RpcResponse TriggerHotkeyBySequence(const RpcRequest&);
|
||||||
|
|
||||||
RpcResponse SetCurrentScene(const RpcRequest&);
|
RpcResponse SetCurrentScene(const RpcRequest&);
|
||||||
RpcResponse GetCurrentScene(const RpcRequest&);
|
RpcResponse GetCurrentScene(const RpcRequest&);
|
||||||
RpcResponse GetSceneList(const RpcRequest&);
|
RpcResponse GetSceneList(const RpcRequest&);
|
||||||
|
RpcResponse CreateScene(const RpcRequest&);
|
||||||
|
RpcResponse SetSceneTransitionOverride(const RpcRequest&);
|
||||||
|
RpcResponse RemoveSceneTransitionOverride(const RpcRequest&);
|
||||||
|
RpcResponse GetSceneTransitionOverride(const RpcRequest&);
|
||||||
|
|
||||||
|
RpcResponse GetSceneItemList(const RpcRequest&);
|
||||||
RpcResponse SetSceneItemRender(const RpcRequest&);
|
RpcResponse SetSceneItemRender(const RpcRequest&);
|
||||||
RpcResponse SetSceneItemPosition(const RpcRequest&);
|
RpcResponse SetSceneItemPosition(const RpcRequest&);
|
||||||
RpcResponse SetSceneItemTransform(const RpcRequest&);
|
RpcResponse SetSceneItemTransform(const RpcRequest&);
|
||||||
@ -74,6 +82,7 @@ class WSRequestHandler {
|
|||||||
RpcResponse ResetSceneItem(const RpcRequest&);
|
RpcResponse ResetSceneItem(const RpcRequest&);
|
||||||
RpcResponse DuplicateSceneItem(const RpcRequest&);
|
RpcResponse DuplicateSceneItem(const RpcRequest&);
|
||||||
RpcResponse DeleteSceneItem(const RpcRequest&);
|
RpcResponse DeleteSceneItem(const RpcRequest&);
|
||||||
|
RpcResponse AddSceneItem(const RpcRequest&);
|
||||||
RpcResponse ReorderSceneItems(const RpcRequest&);
|
RpcResponse ReorderSceneItems(const RpcRequest&);
|
||||||
|
|
||||||
RpcResponse GetStreamingStatus(const RpcRequest&);
|
RpcResponse GetStreamingStatus(const RpcRequest&);
|
||||||
@ -83,11 +92,13 @@ class WSRequestHandler {
|
|||||||
RpcResponse StartStreaming(const RpcRequest&);
|
RpcResponse StartStreaming(const RpcRequest&);
|
||||||
RpcResponse StopStreaming(const RpcRequest&);
|
RpcResponse StopStreaming(const RpcRequest&);
|
||||||
|
|
||||||
|
RpcResponse GetRecordingStatus(const RpcRequest&);
|
||||||
RpcResponse StartRecording(const RpcRequest&);
|
RpcResponse StartRecording(const RpcRequest&);
|
||||||
RpcResponse StopRecording(const RpcRequest&);
|
RpcResponse StopRecording(const RpcRequest&);
|
||||||
RpcResponse PauseRecording(const RpcRequest&);
|
RpcResponse PauseRecording(const RpcRequest&);
|
||||||
RpcResponse ResumeRecording(const RpcRequest&);
|
RpcResponse ResumeRecording(const RpcRequest&);
|
||||||
|
|
||||||
|
RpcResponse GetReplayBufferStatus(const RpcRequest&);
|
||||||
RpcResponse StartStopReplayBuffer(const RpcRequest&);
|
RpcResponse StartStopReplayBuffer(const RpcRequest&);
|
||||||
RpcResponse StartReplayBuffer(const RpcRequest&);
|
RpcResponse StartReplayBuffer(const RpcRequest&);
|
||||||
RpcResponse StopReplayBuffer(const RpcRequest&);
|
RpcResponse StopReplayBuffer(const RpcRequest&);
|
||||||
@ -101,6 +112,9 @@ class WSRequestHandler {
|
|||||||
RpcResponse SetCurrentTransition(const RpcRequest&);
|
RpcResponse SetCurrentTransition(const RpcRequest&);
|
||||||
RpcResponse SetTransitionDuration(const RpcRequest&);
|
RpcResponse SetTransitionDuration(const RpcRequest&);
|
||||||
RpcResponse GetTransitionDuration(const RpcRequest&);
|
RpcResponse GetTransitionDuration(const RpcRequest&);
|
||||||
|
RpcResponse GetTransitionPosition(const RpcRequest&);
|
||||||
|
RpcResponse GetTransitionSettings(const RpcRequest&);
|
||||||
|
RpcResponse SetTransitionSettings(const RpcRequest&);
|
||||||
RpcResponse ReleaseTBar(const RpcRequest&);
|
RpcResponse ReleaseTBar(const RpcRequest&);
|
||||||
RpcResponse SetTBarPosition(const RpcRequest&);
|
RpcResponse SetTBarPosition(const RpcRequest&);
|
||||||
|
|
||||||
@ -109,6 +123,8 @@ class WSRequestHandler {
|
|||||||
RpcResponse ToggleMute(const RpcRequest&);
|
RpcResponse ToggleMute(const RpcRequest&);
|
||||||
RpcResponse SetMute(const RpcRequest&);
|
RpcResponse SetMute(const RpcRequest&);
|
||||||
RpcResponse GetMute(const RpcRequest&);
|
RpcResponse GetMute(const RpcRequest&);
|
||||||
|
RpcResponse GetAudioActive(const RpcRequest&);
|
||||||
|
RpcResponse SetSourceName(const RpcRequest&);
|
||||||
RpcResponse SetSyncOffset(const RpcRequest&);
|
RpcResponse SetSyncOffset(const RpcRequest&);
|
||||||
RpcResponse GetSyncOffset(const RpcRequest&);
|
RpcResponse GetSyncOffset(const RpcRequest&);
|
||||||
RpcResponse GetSpecialSources(const RpcRequest&);
|
RpcResponse GetSpecialSources(const RpcRequest&);
|
||||||
@ -116,6 +132,8 @@ class WSRequestHandler {
|
|||||||
RpcResponse GetSourceTypesList(const RpcRequest&);
|
RpcResponse GetSourceTypesList(const RpcRequest&);
|
||||||
RpcResponse GetSourceSettings(const RpcRequest&);
|
RpcResponse GetSourceSettings(const RpcRequest&);
|
||||||
RpcResponse SetSourceSettings(const RpcRequest&);
|
RpcResponse SetSourceSettings(const RpcRequest&);
|
||||||
|
RpcResponse GetAudioMonitorType(const RpcRequest&);
|
||||||
|
RpcResponse SetAudioMonitorType(const RpcRequest&);
|
||||||
RpcResponse TakeSourceScreenshot(const RpcRequest&);
|
RpcResponse TakeSourceScreenshot(const RpcRequest&);
|
||||||
|
|
||||||
RpcResponse GetSourceFilters(const RpcRequest&);
|
RpcResponse GetSourceFilters(const RpcRequest&);
|
||||||
@ -163,4 +181,16 @@ class WSRequestHandler {
|
|||||||
RpcResponse GetOutputInfo(const RpcRequest&);
|
RpcResponse GetOutputInfo(const RpcRequest&);
|
||||||
RpcResponse StartOutput(const RpcRequest&);
|
RpcResponse StartOutput(const RpcRequest&);
|
||||||
RpcResponse StopOutput(const RpcRequest&);
|
RpcResponse StopOutput(const RpcRequest&);
|
||||||
|
|
||||||
|
RpcResponse PlayPauseMedia(const RpcRequest&);
|
||||||
|
RpcResponse RestartMedia(const RpcRequest&);
|
||||||
|
RpcResponse StopMedia(const RpcRequest&);
|
||||||
|
RpcResponse NextMedia(const RpcRequest&);
|
||||||
|
RpcResponse PreviousMedia(const RpcRequest&);
|
||||||
|
RpcResponse GetMediaDuration(const RpcRequest&);
|
||||||
|
RpcResponse GetMediaTime(const RpcRequest&);
|
||||||
|
RpcResponse SetMediaTime(const RpcRequest&);
|
||||||
|
RpcResponse ScrubMedia(const RpcRequest&);
|
||||||
|
RpcResponse GetMediaState(const RpcRequest&);
|
||||||
|
RpcResponse GetMediaSourcesList(const RpcRequest&);
|
||||||
};
|
};
|
||||||
|
@ -171,6 +171,7 @@ RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) {
|
|||||||
* @name SetHeartbeat
|
* @name SetHeartbeat
|
||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
|
* @deprecated Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0.
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) {
|
RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) {
|
||||||
if (!request.hasField("enable")) {
|
if (!request.hasField("enable")) {
|
||||||
@ -231,7 +232,7 @@ RpcResponse WSRequestHandler::GetFilenameFormatting(const RpcRequest& request) {
|
|||||||
/**
|
/**
|
||||||
* Get OBS stats (almost the same info as provided in OBS' stats window)
|
* Get OBS stats (almost the same info as provided in OBS' stats window)
|
||||||
*
|
*
|
||||||
* @return {OBSStats} `stats` OBS stats
|
* @return {OBSStats} `stats` [OBS stats](#obsstats)
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetStats
|
* @name GetStats
|
||||||
@ -320,9 +321,9 @@ RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) {
|
|||||||
/**
|
/**
|
||||||
* Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer.
|
* Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer.
|
||||||
*
|
*
|
||||||
* @param {String (Optional)} `type` Type of projector: Preview (default), Source, Scene, StudioProgram, or Multiview (case insensitive).
|
* @param {String (Optional)} `type` Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive).
|
||||||
* @param {int (Optional)} `monitor` Monitor to open the projector on. If -1 or omitted, opens a window.
|
* @param {int (Optional)} `monitor` Monitor to open the projector on. If -1 or omitted, opens a window.
|
||||||
* @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using Qt's geometry encoding (https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.
|
* @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.
|
||||||
* @param {String (Optional)} `name` Name of the source or scene to be displayed (ignored for other projector types).
|
* @param {String (Optional)} `name` Name of the source or scene to be displayed (ignored for other projector types).
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -344,3 +345,73 @@ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) {
|
|||||||
obs_frontend_open_projector(type, monitor, geometry, name);
|
obs_frontend_open_projector(type, monitor, geometry, name);
|
||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes hotkey routine, identified by hotkey unique name
|
||||||
|
*
|
||||||
|
* @param {String} `hotkeyName` Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save")
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name TriggerHotkeyByName
|
||||||
|
* @category general
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) {
|
||||||
|
const char* name = obs_data_get_string(request.parameters(), "hotkeyName");
|
||||||
|
|
||||||
|
obs_hotkey_t* hk = Utils::FindHotkeyByName(name);
|
||||||
|
if (!hk) {
|
||||||
|
return request.failed("hotkey not found");
|
||||||
|
}
|
||||||
|
obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hk), true);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings
|
||||||
|
*
|
||||||
|
* @param {String} `keyId` Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h)
|
||||||
|
* @param {Object (Optional)} `keyModifiers` Optional key modifiers object. False entries can be ommitted
|
||||||
|
* @param {boolean} `keyModifiers.shift` Trigger Shift Key
|
||||||
|
* @param {boolean} `keyModifiers.alt` Trigger Alt Key
|
||||||
|
* @param {boolean} `keyModifiers.control` Trigger Control (Ctrl) Key
|
||||||
|
* @param {boolean} `keyModifiers.command` Trigger Command Key (Mac)
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name TriggerHotkeyBySequence
|
||||||
|
* @category general
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("keyId")) {
|
||||||
|
return request.failed("missing request keyId parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "keyModifiers");
|
||||||
|
|
||||||
|
obs_key_combination_t combo = {0};
|
||||||
|
uint32_t modifiers = 0;
|
||||||
|
if (obs_data_get_bool(data, "shift"))
|
||||||
|
modifiers |= INTERACT_SHIFT_KEY;
|
||||||
|
if (obs_data_get_bool(data, "control"))
|
||||||
|
modifiers |= INTERACT_CONTROL_KEY;
|
||||||
|
if (obs_data_get_bool(data, "alt"))
|
||||||
|
modifiers |= INTERACT_ALT_KEY;
|
||||||
|
if (obs_data_get_bool(data, "command"))
|
||||||
|
modifiers |= INTERACT_COMMAND_KEY;
|
||||||
|
|
||||||
|
combo.modifiers = modifiers;
|
||||||
|
combo.key = obs_key_from_name(obs_data_get_string(request.parameters(), "keyId"));
|
||||||
|
|
||||||
|
if (!modifiers
|
||||||
|
&& (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) {
|
||||||
|
return request.failed("invalid key-modifier combination");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject hotkey press-release sequence
|
||||||
|
obs_hotkey_inject_event(combo, false);
|
||||||
|
obs_hotkey_inject_event(combo, true);
|
||||||
|
obs_hotkey_inject_event(combo, false);
|
||||||
|
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
396
src/WSRequestHandler_MediaControl.cpp
Normal file
396
src/WSRequestHandler_MediaControl.cpp
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
bool isMediaSource(const QString& sourceKind)
|
||||||
|
{
|
||||||
|
return (sourceKind == "vlc_source" || sourceKind == "ffmpeg_source");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getSourceMediaState(obs_source_t *source)
|
||||||
|
{
|
||||||
|
QString mediaState;
|
||||||
|
enum obs_media_state mstate = obs_source_media_get_state(source);
|
||||||
|
switch (mstate) {
|
||||||
|
case OBS_MEDIA_STATE_NONE:
|
||||||
|
mediaState = "none";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_PLAYING:
|
||||||
|
mediaState = "playing";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_OPENING:
|
||||||
|
mediaState = "opening";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_BUFFERING:
|
||||||
|
mediaState = "buffering";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_PAUSED:
|
||||||
|
mediaState = "paused";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_STOPPED:
|
||||||
|
mediaState = "stopped";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_ENDED:
|
||||||
|
mediaState = "ended";
|
||||||
|
break;
|
||||||
|
case OBS_MEDIA_STATE_ERROR:
|
||||||
|
mediaState = "error";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mediaState = "unknown";
|
||||||
|
}
|
||||||
|
return mediaState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
* @param {boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name PlayPauseMedia
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) {
|
||||||
|
if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
bool playPause = obs_data_get_bool(request.parameters(), "playPause");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_play_pause(source, playPause);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name RestartMedia
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_restart(source);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name StopMedia
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_stop(source);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name NextMedia
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_next(source);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name PreviousMedia
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_previous(source);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
* Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms.
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @return {int} `mediaDuration` The total length of media in milliseconds..
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetMediaDuration
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_int(response, "mediaDuration", obs_source_media_get_duration(source));
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @return {int} `timestamp` The time in milliseconds since the start of the media.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetMediaTime
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_int(response, "timestamp", obs_source_media_get_time(source));
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
* @param {int} `timestamp` Milliseconds to set the timestamp to.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name SetMediaTime
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName") || !request.hasField("timestamp")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
int64_t timestamp = (int64_t)obs_data_get_int(request.parameters(), "timestamp");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_set_time(source, timestamp);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
* Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested.
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
* @param {int} `timeOffset` Millisecond offset (positive or negative) to offset the current media position.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name ScrubMedia
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName") || !request.hasField("timeOffset")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
int64_t timeOffset = (int64_t)obs_data_get_int(request.parameters(), "timeOffset");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t newTime = obs_source_media_get_time(source) + timeOffset;
|
||||||
|
if (newTime < 0) {
|
||||||
|
newTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_media_set_time(source, newTime);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetMediaState
|
||||||
|
* @category media control
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_string(response, "mediaState", getSourceMediaState(source).toUtf8());
|
||||||
|
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the media state of all media sources (vlc and media source)
|
||||||
|
*
|
||||||
|
* @return {Array<Object>} `mediaSources` Array of sources
|
||||||
|
* @return {String} `mediaSources.*.sourceName` Unique source name
|
||||||
|
* @return {String} `mediaSources.*.sourceKind` Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`)
|
||||||
|
* @return {String} `mediaSources.*.mediaState` The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetMediaSourcesList
|
||||||
|
* @category sources
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request)
|
||||||
|
{
|
||||||
|
OBSDataArrayAutoRelease sourcesArray = obs_data_array_create();
|
||||||
|
|
||||||
|
auto sourceEnumProc = [](void* privateData, obs_source_t* source) -> bool {
|
||||||
|
obs_data_array_t* sourcesArray = (obs_data_array_t*)privateData;
|
||||||
|
|
||||||
|
QString sourceKind = obs_source_get_id(source);
|
||||||
|
if (isMediaSource(sourceKind)) {
|
||||||
|
OBSDataAutoRelease sourceData = obs_data_create();
|
||||||
|
obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source));
|
||||||
|
obs_data_set_string(sourceData, "sourceKind", sourceKind.toUtf8());
|
||||||
|
|
||||||
|
QString mediaState = getSourceMediaState(source);
|
||||||
|
obs_data_set_string(sourceData, "mediaState", mediaState.toUtf8());
|
||||||
|
|
||||||
|
obs_data_array_push_back(sourcesArray, sourceData);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
obs_enum_sources(sourceEnumProc, sourcesArray);
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_array(response, "mediaSources", sourcesArray);
|
||||||
|
return request.success(response);
|
||||||
|
}
|
@ -129,6 +129,8 @@ RpcResponse WSRequestHandler::GetOutputInfo(const RpcRequest& request)
|
|||||||
/**
|
/**
|
||||||
* Start an output
|
* Start an output
|
||||||
*
|
*
|
||||||
|
* Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.
|
||||||
|
*
|
||||||
* @param {String} `outputName` Output name
|
* @param {String} `outputName` Output name
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -157,6 +159,8 @@ RpcResponse WSRequestHandler::StartOutput(const RpcRequest& request)
|
|||||||
/**
|
/**
|
||||||
* Stop an output
|
* Stop an output
|
||||||
*
|
*
|
||||||
|
* Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.
|
||||||
|
*
|
||||||
* @param {String} `outputName` Output name
|
* @param {String} `outputName` Output name
|
||||||
* @param {boolean (optional)} `force` Force stop (default: false)
|
* @param {boolean (optional)} `force` Force stop (default: false)
|
||||||
*
|
*
|
||||||
|
@ -17,13 +17,22 @@ RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) {
|
|||||||
return request.failed("missing request parameters");
|
return request.failed("missing request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString profileName = obs_data_get_string(request.parameters(), "profile-name");
|
const char* profileName = obs_data_get_string(request.parameters(), "profile-name");
|
||||||
if (profileName.isEmpty()) {
|
if (!profileName) {
|
||||||
return request.failed("invalid request parameters");
|
return request.failed("invalid request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : check if profile exists
|
char** profiles = obs_frontend_get_profiles();
|
||||||
obs_frontend_set_current_profile(profileName.toUtf8());
|
bool profileExists = Utils::StringInStringList(profiles, profileName);
|
||||||
|
bfree(profiles);
|
||||||
|
if (!profileExists) {
|
||||||
|
return request.failed("profile does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
|
obs_frontend_set_current_profile(reinterpret_cast<const char*>(param));
|
||||||
|
}, (void*)profileName, true);
|
||||||
|
|
||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +58,7 @@ RpcResponse WSRequestHandler::GetCurrentProfile(const RpcRequest& request) {
|
|||||||
* Get a list of available profiles.
|
* Get a list of available profiles.
|
||||||
*
|
*
|
||||||
* @return {Array<Object>} `profiles` List of available profiles.
|
* @return {Array<Object>} `profiles` List of available profiles.
|
||||||
|
* @return {String} `profiles.*.profile-name` Filter name
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ListProfiles
|
* @name ListProfiles
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
#include "obs-websocket.h"
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <util/platform.h>
|
#include <util/platform.h>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "WSEvents.h"
|
||||||
|
|
||||||
RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> callback)
|
RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> callback)
|
||||||
{
|
{
|
||||||
@ -13,8 +15,35 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> c
|
|||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current recording status.
|
||||||
|
*
|
||||||
|
* @return {boolean} `isRecording` Current recording status.
|
||||||
|
* @return {boolean} `isRecordingPaused` Whether the recording is paused or not.
|
||||||
|
* @return {String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording).
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetRecordingStatus
|
||||||
|
* @category recording
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) {
|
||||||
|
auto events = GetEventsSystem();
|
||||||
|
|
||||||
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
|
obs_data_set_bool(data, "isRecording", obs_frontend_recording_active());
|
||||||
|
obs_data_set_bool(data, "isRecordingPaused", obs_frontend_recording_paused());
|
||||||
|
|
||||||
|
if (obs_frontend_recording_active()) {
|
||||||
|
QString recordingTimecode = events->getRecordingTimecode();
|
||||||
|
obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.success(data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle recording on or off.
|
* Toggle recording on or off (depending on the current recording state).
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStopRecording
|
* @name StartStopRecording
|
||||||
|
@ -1,9 +1,29 @@
|
|||||||
|
#include "obs-websocket.h"
|
||||||
|
#include "WSEvents.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status of the OBS replay buffer.
|
||||||
|
*
|
||||||
|
* @return {boolean} `isReplayBufferActive` Current recording status.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetReplayBufferStatus
|
||||||
|
* @category replay buffer
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) {
|
||||||
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
|
obs_data_set_bool(data, "isReplayBufferActive", obs_frontend_replay_buffer_active());
|
||||||
|
|
||||||
|
return request.success(data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the Replay Buffer on/off.
|
* Toggle the Replay Buffer on/off (depending on the current state of the replay buffer).
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStopReplayBuffer
|
* @name StartStopReplayBuffer
|
||||||
|
@ -17,13 +17,22 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques
|
|||||||
return request.failed("missing request parameters");
|
return request.failed("missing request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sceneCollection = obs_data_get_string(request.parameters(), "sc-name");
|
const char* sceneCollection = obs_data_get_string(request.parameters(), "sc-name");
|
||||||
if (sceneCollection.isEmpty()) {
|
if (!sceneCollection) {
|
||||||
return request.failed("invalid request parameters");
|
return request.failed("invalid request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : Check if specified profile exists and if changing is allowed
|
char** collections = obs_frontend_get_scene_collections();
|
||||||
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
|
bool collectionExists = Utils::StringInStringList(collections, sceneCollection);
|
||||||
|
bfree(collections);
|
||||||
|
if (!collectionExists) {
|
||||||
|
return request.failed("scene collection does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
|
obs_frontend_set_current_scene_collection(reinterpret_cast<const char*>(param));
|
||||||
|
}, (void*)sceneCollection, true);
|
||||||
|
|
||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +60,7 @@ RpcResponse WSRequestHandler::GetCurrentSceneCollection(const RpcRequest& reques
|
|||||||
* List available scene collections
|
* List available scene collections
|
||||||
*
|
*
|
||||||
* @return {Array<String>} `scene-collections` Scene collections list
|
* @return {Array<String>} `scene-collections` Scene collections list
|
||||||
|
* @return {String} `scene-collections.*.sc-name` Scene collection name
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ListSceneCollections
|
* @name ListSceneCollections
|
||||||
|
@ -2,6 +2,90 @@
|
|||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
struct AddSourceData {
|
||||||
|
obs_source_t *source;
|
||||||
|
obs_sceneitem_t *sceneItem;
|
||||||
|
bool setVisible;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddSourceHelper(void *_data, obs_scene_t *scene) {
|
||||||
|
auto *data = reinterpret_cast<AddSourceData*>(_data);
|
||||||
|
data->sceneItem = obs_scene_add(scene, data->source);
|
||||||
|
obs_sceneitem_set_visible(data->sceneItem, data->setVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of all scene items in a scene.
|
||||||
|
*
|
||||||
|
* @param {String (optional)} `sceneName` Name of the scene to get the list of scene items from. Defaults to the current scene if not specified.
|
||||||
|
*
|
||||||
|
* @return {String} `sceneName` Name of the requested (or current) scene
|
||||||
|
* @return {Array<Object>} `sceneItems` Array of scene items
|
||||||
|
* @return {int} `sceneItems.*.itemId` Unique item id of the source item
|
||||||
|
* @return {String} `sceneItems.*.sourceKind` ID if the scene item's source. For example `vlc_source` or `image_source`
|
||||||
|
* @return {String} `sceneItems.*.sourceName` Name of the scene item's source
|
||||||
|
* @return {String} `sceneItems.*.sourceType` Type of the scene item's source. Either `input`, `group`, or `scene`
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetSceneItemList
|
||||||
|
* @category scene items
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) {
|
||||||
|
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
||||||
|
|
||||||
|
OBSSourceAutoRelease sceneSource;
|
||||||
|
if (sceneName && strcmp(sceneName, "") != 0) {
|
||||||
|
sceneSource = obs_get_source_by_name(sceneName);
|
||||||
|
} else {
|
||||||
|
sceneSource = obs_frontend_get_current_scene();
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSScene scene = obs_scene_from_source(sceneSource);
|
||||||
|
if (!scene) {
|
||||||
|
return request.failed("requested scene is invalid or doesnt exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataArrayAutoRelease sceneItemArray = obs_data_array_create();
|
||||||
|
|
||||||
|
auto sceneItemEnumProc = [](obs_scene_t *, obs_sceneitem_t* item, void* privateData) -> bool {
|
||||||
|
obs_data_array_t* sceneItemArray = (obs_data_array_t*)privateData;
|
||||||
|
|
||||||
|
OBSDataAutoRelease sceneItemData = obs_data_create();
|
||||||
|
obs_data_set_int(sceneItemData, "itemId", obs_sceneitem_get_id(item));
|
||||||
|
OBSSource source = obs_sceneitem_get_source(item);
|
||||||
|
obs_data_set_string(sceneItemData, "sourceKind", obs_source_get_id(source));
|
||||||
|
obs_data_set_string(sceneItemData, "sourceName", obs_source_get_name(source));
|
||||||
|
|
||||||
|
QString typeString = "";
|
||||||
|
enum obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
switch (sourceType) {
|
||||||
|
case OBS_SOURCE_TYPE_INPUT:
|
||||||
|
typeString = "input";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBS_SOURCE_TYPE_SCENE:
|
||||||
|
typeString = "scene";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
typeString = "unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
obs_data_set_string(sceneItemData, "sourceType", typeString.toUtf8());
|
||||||
|
|
||||||
|
obs_data_array_push_back(sceneItemArray, sceneItemData);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
obs_scene_enum_items(scene, sceneItemEnumProc, sceneItemArray);
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_string(response, "sceneName", obs_source_get_name(sceneSource));
|
||||||
|
obs_data_set_array(response, "sceneItems", sceneItemArray);
|
||||||
|
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the scene specific properties of the specified source item.
|
* Gets the scene specific properties of the specified source item.
|
||||||
* Coordinates are relative to the item's parent (the scene or group it belongs to).
|
* Coordinates are relative to the item's parent (the scene or group it belongs to).
|
||||||
@ -13,8 +97,8 @@
|
|||||||
*
|
*
|
||||||
* @return {String} `name` Scene Item name.
|
* @return {String} `name` Scene Item name.
|
||||||
* @return {int} `itemId` Scene Item ID.
|
* @return {int} `itemId` Scene Item ID.
|
||||||
* @return {int} `position.x` The x position of the source from the left.
|
* @return {double} `position.x` The x position of the source from the left.
|
||||||
* @return {int} `position.y` The y position of the source from the top.
|
* @return {double} `position.y` The y position of the source from the top.
|
||||||
* @return {int} `position.alignment` The point on the source that the item is manipulated from.
|
* @return {int} `position.alignment` The point on the source that the item is manipulated from.
|
||||||
* @return {double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.
|
* @return {double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.
|
||||||
* @return {double} `scale.x` The x-scale factor of the source.
|
* @return {double} `scale.x` The x-scale factor of the source.
|
||||||
@ -79,8 +163,8 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request)
|
|||||||
* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
|
* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
|
||||||
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
|
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
|
||||||
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
|
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
|
||||||
* @param {int (optional)} `position.x` The new x position of the source.
|
* @param {double (optional)} `position.x` The new x position of the source.
|
||||||
* @param {int (optional)} `position.y` The new y position of the source.
|
* @param {double (optional)} `position.y` The new y position of the source.
|
||||||
* @param {int (optional)} `position.alignment` The new alignment of the source.
|
* @param {int (optional)} `position.alignment` The new alignment of the source.
|
||||||
* @param {double (optional)} `rotation` The new clockwise rotation of the item in degrees.
|
* @param {double (optional)} `rotation` The new clockwise rotation of the item in degrees.
|
||||||
* @param {double (optional)} `scale.x` The new x scale of the item.
|
* @param {double (optional)} `scale.x` The new x scale of the item.
|
||||||
@ -134,10 +218,10 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
|
|||||||
vec2 newPosition = oldPosition;
|
vec2 newPosition = oldPosition;
|
||||||
|
|
||||||
if (obs_data_has_user_value(reqPosition, "x")) {
|
if (obs_data_has_user_value(reqPosition, "x")) {
|
||||||
newPosition.x = obs_data_get_int(reqPosition, "x");
|
newPosition.x = obs_data_get_double(reqPosition, "x");
|
||||||
}
|
}
|
||||||
if (obs_data_has_user_value(reqPosition, "y")) {
|
if (obs_data_has_user_value(reqPosition, "y")) {
|
||||||
newPosition.y = obs_data_get_int(reqPosition, "y");
|
newPosition.y = obs_data_get_double(reqPosition, "y");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_data_has_user_value(reqPosition, "alignment")) {
|
if (obs_data_has_user_value(reqPosition, "alignment")) {
|
||||||
@ -329,7 +413,6 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) {
|
|||||||
* @name SetSceneItemRender
|
* @name SetSceneItemRender
|
||||||
* @category scene items
|
* @category scene items
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
|
RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
|
||||||
if (!request.hasField("source") ||
|
if (!request.hasField("source") ||
|
||||||
@ -544,6 +627,59 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) {
|
|||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a scene item in a scene. In other words, this is how you add a source into a scene.
|
||||||
|
*
|
||||||
|
* @param {String} `sceneName` Name of the scene to create the scene item in
|
||||||
|
* @param {String} `sourceName` Name of the source to be added
|
||||||
|
* @param {boolean} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true`
|
||||||
|
*
|
||||||
|
* @return {int} `itemId` Numerical ID of the created scene item
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name AddSceneItem
|
||||||
|
* @category scene items
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sceneName") || !request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
||||||
|
OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName);
|
||||||
|
OBSScene scene = obs_scene_from_source(sceneSource);
|
||||||
|
if (!scene) {
|
||||||
|
return request.failed("requested scene is invalid or doesnt exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("requested source does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source == sceneSource) {
|
||||||
|
return request.failed("you cannot add a scene as a sceneitem to itself");
|
||||||
|
}
|
||||||
|
|
||||||
|
AddSourceData data;
|
||||||
|
data.source = source;
|
||||||
|
data.setVisible = true;
|
||||||
|
if (request.hasField("setVisible")) {
|
||||||
|
data.setVisible = obs_data_get_bool(request.parameters(), "setVisible");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_enter_graphics();
|
||||||
|
obs_scene_atomic_update(scene, AddSourceHelper, &data);
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
OBSDataAutoRelease responseData = obs_data_create();
|
||||||
|
obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem));
|
||||||
|
|
||||||
|
return request.success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duplicates a scene item.
|
* Duplicates a scene item.
|
||||||
*
|
*
|
||||||
|
@ -79,13 +79,38 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) {
|
|||||||
return request.success(data);
|
return request.success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new scene scene.
|
||||||
|
*
|
||||||
|
* @param {String} `sceneName` Name of the scene to create.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name CreateScene
|
||||||
|
* @category scenes
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sceneName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
return request.failed("scene with this name already exists");
|
||||||
|
}
|
||||||
|
obs_scene_create(sceneName);
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the order of scene items in the requested scene.
|
* Changes the order of scene items in the requested scene.
|
||||||
*
|
*
|
||||||
* @param {String (optional)} `scene` Name of the scene to reorder (defaults to current).
|
* @param {String (optional)} `scene` Name of the scene to reorder (defaults to current).
|
||||||
* @param {Array<Scene>} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene
|
* @param {Array<Scene>} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene
|
||||||
* @param {int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis.
|
* @param {int (optional)} `items.*.id` Id of a specific scene item. Unique on a scene by scene basis.
|
||||||
* @param {String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene.
|
* @param {String (optional)} `items.*.name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ReorderSceneItems
|
* @name ReorderSceneItems
|
||||||
@ -121,7 +146,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) {
|
|||||||
struct obs_sceneitem_order_info info;
|
struct obs_sceneitem_order_info info;
|
||||||
|
|
||||||
size_t itemCount = obs_data_array_count(ctx->items);
|
size_t itemCount = obs_data_array_count(ctx->items);
|
||||||
for (int i = 0; i < itemCount; i++) {
|
for (uint i = 0; i < itemCount; i++) {
|
||||||
OBSDataAutoRelease item = obs_data_array_item(ctx->items, i);
|
OBSDataAutoRelease item = obs_data_array_item(ctx->items, i);
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
|
||||||
@ -148,3 +173,128 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) {
|
|||||||
|
|
||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a scene to use a specific transition override.
|
||||||
|
*
|
||||||
|
* @param {String} `sceneName` Name of the scene to switch to.
|
||||||
|
* @param {String} `transitionName` Name of the transition to use.
|
||||||
|
* @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name SetSceneTransitionOverride
|
||||||
|
* @category scenes
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sceneName") || !request.hasField("transitionName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("requested scene does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
if (sourceType != OBS_SOURCE_TYPE_SCENE) {
|
||||||
|
return request.failed("requested scene is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString transitionName = obs_data_get_string(request.parameters(), "transitionName");
|
||||||
|
if (!Utils::GetTransitionFromName(transitionName)) {
|
||||||
|
return request.failed("requested transition does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease sourceData = obs_source_get_private_settings(source);
|
||||||
|
obs_data_set_string(sourceData, "transition", transitionName.toUtf8().constData());
|
||||||
|
|
||||||
|
if (request.hasField("transitionDuration")) {
|
||||||
|
int transitionOverrideDuration = obs_data_get_int(request.parameters(), "transitionDuration");
|
||||||
|
obs_data_set_int(sourceData, "transition_duration", transitionOverrideDuration);
|
||||||
|
} else if(!obs_data_has_user_value(sourceData, "transition_duration")) {
|
||||||
|
obs_data_set_int(sourceData, "transition_duration",
|
||||||
|
obs_frontend_get_transition_duration()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any transition override on a scene.
|
||||||
|
*
|
||||||
|
* @param {String} `sceneName` Name of the scene to switch to.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name RemoveSceneTransitionOverride
|
||||||
|
* @category scenes
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sceneName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("requested scene does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
if (sourceType != OBS_SOURCE_TYPE_SCENE) {
|
||||||
|
return request.failed("requested scene is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease sourceData = obs_source_get_private_settings(source);
|
||||||
|
obs_data_erase(sourceData, "transition");
|
||||||
|
obs_data_erase(sourceData, "transition_duration");
|
||||||
|
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current scene transition override.
|
||||||
|
*
|
||||||
|
* @param {String} `sceneName` Name of the scene to switch to.
|
||||||
|
*
|
||||||
|
* @return {String} `transitionName` Name of the current overriding transition. Empty string if no override is set.
|
||||||
|
* @return {int} `transitionDuration` Transition duration. `-1` if no override is set.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetSceneTransitionOverride
|
||||||
|
* @category scenes
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("sceneName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("requested scene does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum obs_source_type sourceType = obs_source_get_type(source);
|
||||||
|
if (sourceType != OBS_SOURCE_TYPE_SCENE) {
|
||||||
|
return request.failed("requested scene is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease sourceData = obs_source_get_private_settings(source);
|
||||||
|
const char* transitionOverrideName = obs_data_get_string(sourceData, "transition");
|
||||||
|
|
||||||
|
bool hasDurationOverride = obs_data_has_user_value(sourceData, "transition_duration");
|
||||||
|
int transitionOverrideDuration = obs_data_get_int(sourceData, "transition_duration");
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "transitionName", transitionOverrideName);
|
||||||
|
obs_data_set_int(fields, "transitionDuration",
|
||||||
|
(hasDurationOverride ? transitionOverrideDuration : -1)
|
||||||
|
);
|
||||||
|
|
||||||
|
return request.success(fields);
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ bool isTextFreeType2Source(const QString& sourceKind)
|
|||||||
*
|
*
|
||||||
* @return {Array<Object>} `sources` Array of sources
|
* @return {Array<Object>} `sources` Array of sources
|
||||||
* @return {String} `sources.*.name` Unique source name
|
* @return {String} `sources.*.name` Unique source name
|
||||||
* @return {String} `sources.*.typeId` Non-unique source internal type (a.k.a type id)
|
* @return {String} `sources.*.typeId` Non-unique source internal type (a.k.a kind)
|
||||||
* @return {String} `sources.*.type` Source type. Value is one of the following: "input", "filter", "transition", "scene" or "unknown"
|
* @return {String} `sources.*.type` Source type. Value is one of the following: "input", "filter", "transition", "scene" or "unknown"
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -156,12 +156,13 @@ RpcResponse WSRequestHandler::GetSourceTypesList(const RpcRequest& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the volume of the specified source.
|
* Get the volume of the specified source. Default response uses mul format, NOT SLIDER PERCENTAGE.
|
||||||
*
|
*
|
||||||
* @param {String} `source` Source name.
|
* @param {String} `source` Source name.
|
||||||
|
* @param {boolean (optional)} `useDecibel` Output volume in decibels of attenuation instead of amplitude/mul.
|
||||||
*
|
*
|
||||||
* @return {String} `name` Source name.
|
* @return {String} `name` Source name.
|
||||||
* @return {double} `volume` Volume of the source. Between `0.0` and `1.0`.
|
* @return {double} `volume` Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating).
|
||||||
* @return {boolean} `muted` Indicates whether the source is muted.
|
* @return {boolean} `muted` Indicates whether the source is muted.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -185,35 +186,50 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request)
|
|||||||
return request.failed("specified source doesn't exist");
|
return request.failed("specified source doesn't exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float volume = obs_source_get_volume(source);
|
||||||
|
|
||||||
|
bool useDecibel = obs_data_get_bool(request.parameters(), "useDecibel");
|
||||||
|
if (useDecibel) {
|
||||||
|
volume = obs_mul_to_db(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volume == -INFINITY) {
|
||||||
|
volume = -100.0;
|
||||||
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "name", obs_source_get_name(source));
|
obs_data_set_string(response, "name", obs_source_get_name(source));
|
||||||
obs_data_set_double(response, "volume", obs_source_get_volume(source));
|
obs_data_set_double(response, "volume", volume);
|
||||||
obs_data_set_bool(response, "muted", obs_source_muted(source));
|
obs_data_set_bool(response, "muted", obs_source_muted(source));
|
||||||
|
|
||||||
return request.success(response);
|
return request.success(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the volume of the specified source.
|
* Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE.
|
||||||
*
|
*
|
||||||
* @param {String} `source` Source name.
|
* @param {String} `source` Source name.
|
||||||
* @param {double} `volume` Desired volume. Must be between `0.0` and `1.0`.
|
* @param {double} `volume` Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf.
|
||||||
*
|
* @param {boolean (optional)} `useDecibel` Interperet `volume` data as decibels instead of amplitude/mul.
|
||||||
* @api requests
|
*
|
||||||
* @name SetVolume
|
* @api requests
|
||||||
* @category sources
|
* @name SetVolume
|
||||||
* @since 4.0.0
|
* @category sources
|
||||||
*/
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request)
|
RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.hasField("source") || !request.hasField("volume")) {
|
if (!request.hasField("source") || !request.hasField("volume")) {
|
||||||
return request.failed("missing request parameters");
|
return request.failed("missing request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool useDecibel = obs_data_get_bool(request.parameters(), "useDecibel");
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "source");
|
QString sourceName = obs_data_get_string(request.parameters(), "source");
|
||||||
float sourceVolume = obs_data_get_double(request.parameters(), "volume");
|
float sourceVolume = obs_data_get_double(request.parameters(), "volume");
|
||||||
|
|
||||||
if (sourceName.isEmpty() || sourceVolume < 0.0 || sourceVolume > 1.0) {
|
bool isNotValidDecibel = (useDecibel && sourceVolume > 0.0);
|
||||||
|
bool isNotValidMul = (!useDecibel && (sourceVolume < 0.0 || sourceVolume > 1.0));
|
||||||
|
if (sourceName.isEmpty() || isNotValidDecibel || isNotValidMul) {
|
||||||
return request.failed("invalid request parameters");
|
return request.failed("invalid request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +238,11 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request)
|
|||||||
return request.failed("specified source doesn't exist");
|
return request.failed("specified source doesn't exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useDecibel) {
|
||||||
|
sourceVolume = obs_db_to_mul(sourceVolume);
|
||||||
|
}
|
||||||
obs_source_set_volume(source, sourceVolume);
|
obs_source_set_volume(source, sourceVolume);
|
||||||
|
|
||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,6 +345,80 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request)
|
|||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the audio's active status of a specified source.
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @return {boolean} `audioActive` Audio active status of the source.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetAudioActive
|
||||||
|
* @category sources
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetAudioActive(const RpcRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_bool(response, "audioActive", obs_source_audio_active(source));
|
||||||
|
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets (aka rename) the name of a source. Also works with scenes since scenes are technically sources in OBS.
|
||||||
|
*
|
||||||
|
* Note: If the new name already exists as a source, obs-websocket will return an error.
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
* @param {String} `newName` New source name.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name SetSourceName
|
||||||
|
* @category sources
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::SetSourceName(const RpcRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.hasField("sourceName") || !request.hasField("newName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
QString newName = obs_data_get_string(request.parameters(), "newName");
|
||||||
|
if (sourceName.isEmpty() || newName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.toUtf8());
|
||||||
|
if (!existingSource) { // OBS is supposed to automatically rename colliding source names, but it doesn't. So this gets to be the solution for now.
|
||||||
|
obs_source_set_name(source, newName.toUtf8());
|
||||||
|
|
||||||
|
return request.success();
|
||||||
|
} else {
|
||||||
|
return request.failed("a source with that name already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the audio sync offset of a specified source.
|
* Set the audio sync offset of a specified source.
|
||||||
*
|
*
|
||||||
@ -417,6 +511,7 @@ RpcResponse WSRequestHandler::GetSourceSettings(const RpcRequest& request)
|
|||||||
|
|
||||||
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
|
||||||
|
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return request.failed("specified source doesn't exist");
|
return request.failed("specified source doesn't exist");
|
||||||
}
|
}
|
||||||
@ -477,21 +572,17 @@ RpcResponse WSRequestHandler::SetSourceSettings(const RpcRequest& request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease currentSettings = obs_source_get_settings(source);
|
|
||||||
OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings");
|
OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings");
|
||||||
|
|
||||||
OBSDataAutoRelease sourceSettings = obs_data_create();
|
obs_source_update(source, newSettings);
|
||||||
obs_data_apply(sourceSettings, currentSettings);
|
|
||||||
obs_data_apply(sourceSettings, newSettings);
|
|
||||||
|
|
||||||
obs_source_update(source, sourceSettings);
|
|
||||||
obs_source_update_properties(source);
|
obs_source_update_properties(source);
|
||||||
|
|
||||||
|
OBSDataAutoRelease updatedSettings = obs_source_get_settings(source);
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "sourceName", obs_source_get_name(source));
|
obs_data_set_string(response, "sourceName", obs_source_get_name(source));
|
||||||
obs_data_set_string(response, "sourceType", obs_source_get_id(source));
|
obs_data_set_string(response, "sourceType", obs_source_get_id(source));
|
||||||
obs_data_set_obj(response, "sourceSettings", sourceSettings);
|
obs_data_set_obj(response, "sourceSettings", updatedSettings);
|
||||||
|
|
||||||
return request.success(response);
|
return request.success(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,8 +593,8 @@ RpcResponse WSRequestHandler::SetSourceSettings(const RpcRequest& request)
|
|||||||
*
|
*
|
||||||
* @return {String} `source` Source name.
|
* @return {String} `source` Source name.
|
||||||
* @return {String} `align` Text Alignment ("left", "center", "right").
|
* @return {String} `align` Text Alignment ("left", "center", "right").
|
||||||
* @return {int} `bk-color` Background color.
|
* @return {int} `bk_color` Background color.
|
||||||
* @return {int} `bk-opacity` Background opacity (0-100).
|
* @return {int} `bk_opacity` Background opacity (0-100).
|
||||||
* @return {boolean} `chatlog` Chat log.
|
* @return {boolean} `chatlog` Chat log.
|
||||||
* @return {int} `chatlog_lines` Chat log lines.
|
* @return {int} `chatlog_lines` Chat log lines.
|
||||||
* @return {int} `color` Text color.
|
* @return {int} `color` Text color.
|
||||||
@ -562,8 +653,8 @@ RpcResponse WSRequestHandler::GetTextGDIPlusProperties(const RpcRequest& request
|
|||||||
*
|
*
|
||||||
* @param {String} `source` Name of the source.
|
* @param {String} `source` Name of the source.
|
||||||
* @param {String (optional)} `align` Text Alignment ("left", "center", "right").
|
* @param {String (optional)} `align` Text Alignment ("left", "center", "right").
|
||||||
* @param {int (optional)} `bk-color` Background color.
|
* @param {int (optional)} `bk_color` Background color.
|
||||||
* @param {int (optional)} `bk-opacity` Background opacity (0-100).
|
* @param {int (optional)} `bk_opacity` Background opacity (0-100).
|
||||||
* @param {boolean (optional)} `chatlog` Chat log.
|
* @param {boolean (optional)} `chatlog` Chat log.
|
||||||
* @param {int (optional)} `chatlog_lines` Chat log lines.
|
* @param {int (optional)} `chatlog_lines` Chat log lines.
|
||||||
* @param {int (optional)} `color` Text color.
|
* @param {int (optional)} `color` Text color.
|
||||||
@ -919,7 +1010,7 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque
|
|||||||
* @name GetBrowserSourceProperties
|
* @name GetBrowserSourceProperties
|
||||||
* @category sources
|
* @category sources
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
* @deprecated Since 4.8.0. Prefer the use of GetSourceSettings.
|
* @deprecated Since 4.8.0. Prefer the use of GetSourceSettings. Will be removed in v5.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request)
|
RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request)
|
||||||
{
|
{
|
||||||
@ -961,7 +1052,7 @@ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& reque
|
|||||||
* @api requests
|
* @api requests
|
||||||
* @name SetBrowserSourceProperties
|
* @name SetBrowserSourceProperties
|
||||||
* @category sources
|
* @category sources
|
||||||
* @deprecated Since 4.8.0. Prefer the use of SetSourceSettings.
|
* @deprecated Since 4.8.0. Prefer the use of SetSourceSettings. Will be removed in v5.0.0
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request)
|
RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request)
|
||||||
@ -1426,6 +1517,99 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques
|
|||||||
return request.success();
|
return request.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the audio monitoring type of the specified source.
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
*
|
||||||
|
* @return {String} `monitorType` The monitor type in use. Options: `none`, `monitorOnly`, `monitorAndOutput`.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetAudioMonitorType
|
||||||
|
* @category sources
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetAudioMonitorType(const RpcRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
|
||||||
|
if (sourceName.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
|
||||||
|
QString monitorType;
|
||||||
|
enum obs_monitoring_type mtype = obs_source_get_monitoring_type(source);
|
||||||
|
switch (mtype) {
|
||||||
|
case OBS_MONITORING_TYPE_NONE:
|
||||||
|
monitorType = "none";
|
||||||
|
break;
|
||||||
|
case OBS_MONITORING_TYPE_MONITOR_ONLY:
|
||||||
|
monitorType = "monitorOnly";
|
||||||
|
break;
|
||||||
|
case OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT:
|
||||||
|
monitorType = "monitorAndOutput";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
monitorType = "unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
obs_data_set_string(response, "monitorType", monitorType.toUtf8());
|
||||||
|
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the audio monitoring type of the specified source.
|
||||||
|
*
|
||||||
|
* @param {String} `sourceName` Source name.
|
||||||
|
* @param {String} `monitorType` The monitor type to use. Options: `none`, `monitorOnly`, `monitorAndOutput`.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name SetAudioMonitorType
|
||||||
|
* @category sources
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.hasField("sourceName") || !request.hasField("monitorType")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
|
QString monitorType = obs_data_get_string(request.parameters(), "monitorType");
|
||||||
|
|
||||||
|
if (sourceName.isEmpty() || monitorType.isEmpty()) {
|
||||||
|
return request.failed("invalid request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
||||||
|
if (!source) {
|
||||||
|
return request.failed("specified source doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorType == "none") {
|
||||||
|
obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_NONE);
|
||||||
|
} else if (monitorType == "monitorOnly") {
|
||||||
|
obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_ONLY);
|
||||||
|
} else if (monitorType == "monitorAndOutput") {
|
||||||
|
obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
|
||||||
|
} else {
|
||||||
|
return request.failed("invalid monitorType");
|
||||||
|
}
|
||||||
|
return request.success();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a picture snapshot of a source and then can either or both:
|
* Takes a picture snapshot of a source and then can either or both:
|
||||||
* - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request)
|
* - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request)
|
||||||
@ -1436,9 +1620,11 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques
|
|||||||
* Clients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is
|
* Clients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is
|
||||||
* preserved if only one of these two parameters is specified.
|
* preserved if only one of these two parameters is specified.
|
||||||
*
|
*
|
||||||
* @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name.
|
* @param {String (optional)} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used.
|
||||||
* @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module)
|
* @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module)
|
||||||
* @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path.
|
* @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path.
|
||||||
|
* @param {String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension.
|
||||||
|
* @param {int (optional)} `compressionQuality` Compression ratio between -1 and 100 to write the image with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type.
|
||||||
* @param {int (optional)} `width` Screenshot width. Defaults to the source's base width.
|
* @param {int (optional)} `width` Screenshot width. Defaults to the source's base width.
|
||||||
* @param {int (optional)} `height` Screenshot height. Defaults to the source's base height.
|
* @param {int (optional)} `height` Screenshot height. Defaults to the source's base height.
|
||||||
*
|
*
|
||||||
@ -1452,19 +1638,20 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques
|
|||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
|
RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request.hasField("embedPictureFormat") && !request.hasField("saveToFilePath")) {
|
if (!request.hasField("embedPictureFormat") && !request.hasField("saveToFilePath")) {
|
||||||
return request.failed("At least 'embedPictureFormat' or 'saveToFilePath' must be specified");
|
return request.failed("At least 'embedPictureFormat' or 'saveToFilePath' must be specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBSSourceAutoRelease source;
|
||||||
|
if (!request.hasField("sourceName")) {
|
||||||
|
source = obs_frontend_get_current_scene();
|
||||||
|
} else {
|
||||||
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
|
source = obs_get_source_by_name(sourceName);
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return request.failed("specified source doesn't exist");;
|
return request.failed("specified source doesn't exist");;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const uint32_t sourceWidth = obs_source_get_base_width(source);
|
const uint32_t sourceWidth = obs_source_get_base_width(source);
|
||||||
const uint32_t sourceHeight = obs_source_get_base_height(source);
|
const uint32_t sourceHeight = obs_source_get_base_height(source);
|
||||||
@ -1522,7 +1709,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
|
|||||||
gs_stage_texture(stagesurface, gs_texrender_get_texture(texrender));
|
gs_stage_texture(stagesurface, gs_texrender_get_texture(texrender));
|
||||||
if (gs_stagesurface_map(stagesurface, &videoData, &videoLinesize)) {
|
if (gs_stagesurface_map(stagesurface, &videoData, &videoLinesize)) {
|
||||||
int linesize = sourceImage.bytesPerLine();
|
int linesize = sourceImage.bytesPerLine();
|
||||||
for (int y = 0; y < imgHeight; y++) {
|
for (uint y = 0; y < imgHeight; y++) {
|
||||||
memcpy(sourceImage.scanLine(y), videoData + (y * videoLinesize), linesize);
|
memcpy(sourceImage.scanLine(y), videoData + (y * videoLinesize), linesize);
|
||||||
}
|
}
|
||||||
gs_stagesurface_unmap(stagesurface);
|
gs_stagesurface_unmap(stagesurface);
|
||||||
@ -1541,20 +1728,30 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
|
|||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
|
||||||
|
int compressionQuality {-1};
|
||||||
|
if (request.hasField("compressionQuality")) {
|
||||||
|
compressionQuality = obs_data_get_int(request.parameters(), "compressionQuality");
|
||||||
|
|
||||||
|
if (compressionQuality < -1 || compressionQuality > 100) {
|
||||||
|
QString errorMessage = QString("compression quality out of range: %1").arg(compressionQuality);
|
||||||
|
return request.failed(errorMessage.toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (request.hasField("embedPictureFormat")) {
|
if (request.hasField("embedPictureFormat")) {
|
||||||
const char* pictureFormat = obs_data_get_string(request.parameters(), "embedPictureFormat");
|
const char* pictureFormat = obs_data_get_string(request.parameters(), "embedPictureFormat");
|
||||||
|
|
||||||
QByteArrayList supportedFormats = QImageWriter::supportedImageFormats();
|
QByteArrayList supportedFormats = QImageWriter::supportedImageFormats();
|
||||||
if (!supportedFormats.contains(pictureFormat)) {
|
if (!supportedFormats.contains(pictureFormat)) {
|
||||||
QString errorMessage = QString("Unsupported picture format: %1").arg(pictureFormat);
|
QString errorMessage = QString("unsupported picture format: %1").arg(pictureFormat);
|
||||||
return request.failed(errorMessage.toUtf8());
|
return request.failed(errorMessage.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray encodedImgBytes;
|
QByteArray encodedImgBytes;
|
||||||
QBuffer buffer(&encodedImgBytes);
|
QBuffer buffer(&encodedImgBytes);
|
||||||
buffer.open(QBuffer::WriteOnly);
|
buffer.open(QBuffer::WriteOnly);
|
||||||
if (!sourceImage.save(&buffer, pictureFormat)) {
|
if (!sourceImage.save(&buffer, pictureFormat, compressionQuality)) {
|
||||||
return request.failed("Embed image encoding failed");
|
return request.failed("embed image encoding failed");
|
||||||
}
|
}
|
||||||
buffer.close();
|
buffer.close();
|
||||||
|
|
||||||
@ -1571,7 +1768,18 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
|
|||||||
QFileInfo filePathInfo(filePathStr);
|
QFileInfo filePathInfo(filePathStr);
|
||||||
QString absoluteFilePath = filePathInfo.absoluteFilePath();
|
QString absoluteFilePath = filePathInfo.absoluteFilePath();
|
||||||
|
|
||||||
if (!sourceImage.save(absoluteFilePath)) {
|
const char* fileFormat = nullptr;
|
||||||
|
if (request.hasField("fileFormat")) {
|
||||||
|
fileFormat = obs_data_get_string(request.parameters(), "fileFormat");
|
||||||
|
QByteArrayList supportedFormats = QImageWriter::supportedImageFormats();
|
||||||
|
|
||||||
|
if (!supportedFormats.contains(fileFormat)) {
|
||||||
|
QString errorMessage = QString("unsupported file format: %1").arg(fileFormat);
|
||||||
|
return request.failed(errorMessage.toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceImage.save(absoluteFilePath, fileFormat, compressionQuality)) {
|
||||||
return request.failed("Image save failed");
|
return request.failed("Image save failed");
|
||||||
}
|
}
|
||||||
obs_data_set_string(response, "imageFile", absoluteFilePath.toUtf8());
|
obs_data_set_string(response, "imageFile", absoluteFilePath.toUtf8());
|
||||||
|
@ -43,7 +43,7 @@ RpcResponse WSRequestHandler::GetStreamingStatus(const RpcRequest& request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle streaming on or off.
|
* Toggle streaming on or off (depending on the current stream state).
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStopStreaming
|
* @name StartStopStreaming
|
||||||
@ -103,10 +103,10 @@ RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) {
|
|||||||
&& obs_data_has_user_value(newSettings, "key"))
|
&& obs_data_has_user_value(newSettings, "key"))
|
||||||
{
|
{
|
||||||
const char* key = obs_data_get_string(newSettings, "key");
|
const char* key = obs_data_get_string(newSettings, "key");
|
||||||
int keylen = strlen(key);
|
size_t keylen = strlen(key);
|
||||||
|
|
||||||
bool hasQuestionMark = false;
|
bool hasQuestionMark = false;
|
||||||
for (int i = 0; i < keylen; i++) {
|
for (size_t i = 0; i < keylen; i++) {
|
||||||
if (key[i] == '?') {
|
if (key[i] == '?') {
|
||||||
hasQuestionMark = true;
|
hasQuestionMark = true;
|
||||||
break;
|
break;
|
||||||
@ -292,7 +292,6 @@ RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the provided text as embedded CEA-608 caption data.
|
* Send the provided text as embedded CEA-608 caption data.
|
||||||
* As of OBS Studio 23.1, captions are not yet available on Linux.
|
|
||||||
*
|
*
|
||||||
* @param {String} `text` Captions text
|
* @param {String} `text` Captions text
|
||||||
*
|
*
|
||||||
|
@ -133,6 +133,9 @@ RpcResponse WSRequestHandler::TransitionToProgram(const RpcRequest& request) {
|
|||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) {
|
RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) {
|
||||||
|
if (obs_frontend_preview_program_mode_active()) {
|
||||||
|
return request.failed("studio mode already active");
|
||||||
|
}
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
obs_frontend_set_preview_program_mode(true);
|
obs_frontend_set_preview_program_mode(true);
|
||||||
|
|
||||||
@ -150,6 +153,9 @@ RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) {
|
|||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) {
|
RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) {
|
||||||
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
|
return request.failed("studio mode not active");
|
||||||
|
}
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
||||||
obs_frontend_set_preview_program_mode(false);
|
obs_frontend_set_preview_program_mode(false);
|
||||||
|
|
||||||
@ -160,7 +166,7 @@ RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles Studio Mode.
|
* Toggles Studio Mode (depending on the current state of studio mode).
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ToggleStudioMode
|
* @name ToggleStudioMode
|
||||||
|
@ -121,6 +121,90 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) {
|
|||||||
return request.success(response);
|
return request.success(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position of the current transition.
|
||||||
|
*
|
||||||
|
* @return {double} `position` current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetTransitionPosition
|
||||||
|
* @category transitions
|
||||||
|
* @since 4.8.0
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) {
|
||||||
|
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_double(response, "position", obs_transition_get_time(currentTransition));
|
||||||
|
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current settings of a transition
|
||||||
|
*
|
||||||
|
* @param {String} `transitionName` Transition name
|
||||||
|
*
|
||||||
|
* @return {Object} `transitionSettings` Current transition settings
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetTransitionSettings
|
||||||
|
* @category transitions
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::GetTransitionSettings(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("transitionName")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* transitionName = obs_data_get_string(request.parameters(), "transitionName");
|
||||||
|
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
|
||||||
|
if (!transition) {
|
||||||
|
return request.failed("specified transition doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition);
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_obj(response, "transitionSettings", transitionSettings);
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the current settings of a transition
|
||||||
|
*
|
||||||
|
* @param {String} `transitionName` Transition name
|
||||||
|
* @param {Object} `transitionSettings` Transition settings (they can be partial)
|
||||||
|
*
|
||||||
|
* @return {Object} `transitionSettings` Updated transition settings
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name SetTransitionSettings
|
||||||
|
* @category transitions
|
||||||
|
* @since unreleased
|
||||||
|
*/
|
||||||
|
RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) {
|
||||||
|
if (!request.hasField("transitionName") || !request.hasField("transitionSettings")) {
|
||||||
|
return request.failed("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* transitionName = obs_data_get_string(request.parameters(), "transitionName");
|
||||||
|
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
|
||||||
|
if (!transition) {
|
||||||
|
return request.failed("specified transition doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "transitionSettings");
|
||||||
|
obs_source_update(transition, newSettings);
|
||||||
|
obs_source_update_properties(transition);
|
||||||
|
|
||||||
|
OBSDataAutoRelease updatedSettings = obs_source_get_settings(transition);
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_obj(response, "transitionSettings", updatedSettings);
|
||||||
|
return request.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release the T-Bar. YOU MUST CALL THIS IF YOU SPECIFY `release = false` IN `SetTBarPosition`.
|
* Release the T-Bar. YOU MUST CALL THIS IF YOU SPECIFY `release = false` IN `SetTBarPosition`.
|
||||||
*
|
*
|
||||||
|
@ -60,10 +60,10 @@ WSServer::~WSServer()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::start(quint16 port)
|
void WSServer::start(quint16 port, bool lockToIPv4)
|
||||||
{
|
{
|
||||||
if (_server.is_listening() && port == _serverPort) {
|
if (_server.is_listening() && (port == _serverPort && _lockToIPv4 == lockToIPv4)) {
|
||||||
blog(LOG_INFO, "WSServer::start: server already on this port. no restart needed");
|
blog(LOG_INFO, "WSServer::start: server already on this port and protocol mode. no restart needed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,9 +74,16 @@ void WSServer::start(quint16 port)
|
|||||||
_server.reset();
|
_server.reset();
|
||||||
|
|
||||||
_serverPort = port;
|
_serverPort = port;
|
||||||
|
_lockToIPv4 = lockToIPv4;
|
||||||
|
|
||||||
websocketpp::lib::error_code errorCode;
|
websocketpp::lib::error_code errorCode;
|
||||||
|
if (lockToIPv4) {
|
||||||
|
blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings");
|
||||||
|
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), _serverPort, errorCode);
|
||||||
|
} else {
|
||||||
|
blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings");
|
||||||
_server.listen(_serverPort, errorCode);
|
_server.listen(_serverPort, errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
std::string errorCodeMessage = errorCode.message();
|
std::string errorCodeMessage = errorCode.message();
|
||||||
|
@ -44,7 +44,7 @@ Q_OBJECT
|
|||||||
public:
|
public:
|
||||||
explicit WSServer();
|
explicit WSServer();
|
||||||
virtual ~WSServer();
|
virtual ~WSServer();
|
||||||
void start(quint16 port);
|
void start(quint16 port, bool lockToIPv4);
|
||||||
void stop();
|
void stop();
|
||||||
void broadcast(const RpcEvent& event);
|
void broadcast(const RpcEvent& event);
|
||||||
QThreadPool* threadPool() {
|
QThreadPool* threadPool() {
|
||||||
@ -62,6 +62,7 @@ private:
|
|||||||
|
|
||||||
server _server;
|
server _server;
|
||||||
quint16 _serverPort;
|
quint16 _serverPort;
|
||||||
|
bool _lockToIPv4;
|
||||||
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
||||||
std::map<connection_hdl, ConnectionProperties, std::owner_less<connection_hdl>> _connectionProperties;
|
std::map<connection_hdl, ConnectionProperties, std::owner_less<connection_hdl>> _connectionProperties;
|
||||||
QMutex _clMutex;
|
QMutex _clMutex;
|
||||||
|
@ -45,6 +45,7 @@ void SettingsDialog::showEvent(QShowEvent* event) {
|
|||||||
|
|
||||||
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
||||||
ui->serverPort->setValue(conf->ServerPort);
|
ui->serverPort->setValue(conf->ServerPort);
|
||||||
|
ui->lockToIPv4->setChecked(conf->LockToIPv4);
|
||||||
|
|
||||||
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
||||||
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
|
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
|
||||||
@ -72,6 +73,7 @@ void SettingsDialog::FormAccepted() {
|
|||||||
|
|
||||||
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
||||||
conf->ServerPort = ui->serverPort->value();
|
conf->ServerPort = ui->serverPort->value();
|
||||||
|
conf->LockToIPv4 = ui->lockToIPv4->isChecked();
|
||||||
|
|
||||||
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
||||||
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
|
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
|
||||||
@ -95,7 +97,7 @@ void SettingsDialog::FormAccepted() {
|
|||||||
|
|
||||||
auto server = GetServer();
|
auto server = GetServer();
|
||||||
if (conf->ServerEnabled) {
|
if (conf->ServerEnabled) {
|
||||||
server->start(conf->ServerPort);
|
server->start(conf->ServerPort, conf->LockToIPv4);
|
||||||
} else {
|
} else {
|
||||||
server->stop();
|
server->stop();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>407</width>
|
<width>407</width>
|
||||||
<height>195</height>
|
<height>216</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -79,7 +79,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QCheckBox" name="alertsEnabled">
|
<widget class="QCheckBox" name="alertsEnabled">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.AlertsEnable</string>
|
<string>OBSWebsocket.Settings.AlertsEnable</string>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="QCheckBox" name="debugEnabled">
|
<widget class="QCheckBox" name="debugEnabled">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.DebugEnable</string>
|
<string>OBSWebsocket.Settings.DebugEnable</string>
|
||||||
@ -99,6 +99,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QCheckBox" name="lockToIPv4">
|
||||||
|
<property name="text">
|
||||||
|
<string>OBSWebsocket.Settings.LockToIPv4</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -81,7 +81,7 @@ bool obs_module_load(void) {
|
|||||||
auto eventCallback = [](enum obs_frontend_event event, void *param) {
|
auto eventCallback = [](enum obs_frontend_event event, void *param) {
|
||||||
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
||||||
if (_config->ServerEnabled) {
|
if (_config->ServerEnabled) {
|
||||||
_server->start(_config->ServerPort);
|
_server->start(_config->ServerPort, _config->LockToIPv4);
|
||||||
}
|
}
|
||||||
obs_frontend_remove_event_callback((obs_frontend_event_cb)param, nullptr);
|
obs_frontend_remove_event_callback((obs_frontend_event_cb)param, nullptr);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user