Merge branch '4.x-current' into feature/component-management

This commit is contained in:
tt2468 2020-07-03 07:07:52 -07:00
commit 355cee0db9
24 changed files with 1889 additions and 108 deletions

View File

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

@ -0,0 +1,3 @@
open_collective: obs-websocket
github: Palakis
custom: https://www.paypal.me/stephanelepin

View File

@ -1,16 +1,20 @@
##### Issue type
Bug report? Feature request? Other?
<!--- Uncomment one of the two options below. -->
<!--- - Bug report -->
<!--- - Feature request -->
##### 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
*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
- **Operating System** :
- **OBS Studio version** :
- **obs-websocket version** :
##### 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.
Remove this section if it doesn't apply to your case.*
<!--- 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 does not apply. -->

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
View File

@ -0,0 +1,35 @@
<!--- Please fill out the following template, which will help other contributors review your Pull Request. -->
<!--- Make sure youve 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
View 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
View 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

View File

@ -29,12 +29,18 @@ make -j4
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
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.
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
existing obs-studio development environment is not required, as
`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
- Windows: [![Automated Build status for Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
- Linux: [![Automated Build status for Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket)
- macOS: [![Automated Build status for macOS](https://img.shields.io/azure-devops/build/Palakis/obs-websocket/Palakis.obs-websocket.svg)](https://dev.azure.com/Palakis/obs-websocket/_build)
[![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)

View File

@ -34,6 +34,7 @@ git checkout $OBSLatestTag
mkdir build && cd build
echo "[obs-websocket] Building obs-studio.."
cmake .. \
-DBUILD_CAPTIONS=true \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
-DDISABLE_PLUGINS=true \
-DENABLE_SCRIPTING=0 \

10
CI/macos/Brewfile Normal file
View 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"

View File

@ -35,6 +35,7 @@ set(obs-websocket_SOURCES
src/WSRequestHandler_StudioMode.cpp
src/WSRequestHandler_Transitions.cpp
src/WSRequestHandler_Outputs.cpp
src/WSRequestHandler_MediaControl.cpp
src/WSEvents.cpp
src/Config.cpp
src/Utils.cpp

82
CONTRIBUTING.md Normal file
View 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").

View File

@ -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.
Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis)
[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A)
[![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40LePalakis)](https://twitter.com/LePalakis)
## Downloads
@ -38,7 +41,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
- 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
@ -46,80 +55,23 @@ See the [build instructions](BUILDING.md).
## Contributing
### Branches
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();
```
See [the contributing document](/CONTRIBUTING.md)
## 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
In (almost) order of appearance:
- [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
Thank you so much to all of the contibutors [(here)](https://github.com/Palakis/obs-websocket/graphs/contributors) for your amazing help.
And also: special thanks to supporters of the project!
## 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
View 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.

View File

@ -684,8 +684,8 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
/**
* @typedef {Object} `SceneItemTransform`
* @property {int} `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.x` The x position of the scene item from the left.
* @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 {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.

View File

@ -271,10 +271,21 @@ void WSEvents::connectSourceSignals(obs_source_t* source) {
signal_handler_connect(sh, "volume", OnSourceVolumeChange, this);
signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, 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_remove", OnSourceFilterRemoved, 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) {
signal_handler_connect(sh, "reorder", OnSceneReordered, this);
@ -303,6 +314,8 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) {
signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this);
signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, 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_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_stop", OnTransitionEnd, 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) {
@ -407,6 +429,16 @@ QString WSEvents::getRecordingTimecode() {
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, "sourceTypeId", obs_source_get_id(source));
return fields;
}
/**
* Indicates a scene change.
*
@ -1098,6 +1130,52 @@ void WSEvents::OnSourceMuteStateChange(void* param, calldata_t* data) {
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.
*
@ -1338,6 +1416,174 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) {
self->broadcastUpdate("SourceFiltersReordered", fields);
}
/**
* 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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaPlaying
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaPaused
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaRestarted
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaStopped
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaNext
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaPrevious
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaStarted
* @category media
* @since 4.9.0
*/
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} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaEnded
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaEnded(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaEnded", fields);
}
/**
* Scene items have been reordered.
*

View File

@ -126,6 +126,8 @@ private:
static void OnSourceMuteStateChange(void* param, calldata_t* data);
static void OnSourceAudioSyncOffsetChanged(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);
@ -133,6 +135,15 @@ private:
static void OnSourceFilterRemoved(void* param, calldata_t* data);
static void OnSourceFilterVisibilityChanged(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 OnSceneItemAdd(void* param, calldata_t* data);

View File

@ -72,11 +72,13 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
{ "StartStreaming", &WSRequestHandler::StartStreaming },
{ "StopStreaming", &WSRequestHandler::StopStreaming },
{ "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus },
{ "StartRecording", &WSRequestHandler::StartRecording },
{ "StopRecording", &WSRequestHandler::StopRecording },
{ "PauseRecording", &WSRequestHandler::PauseRecording },
{ "ResumeRecording", &WSRequestHandler::ResumeRecording },
{ "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus },
{ "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer },
{ "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer },
{ "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer },
@ -97,6 +99,7 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
{ "ToggleMute", &WSRequestHandler::ToggleMute },
{ "SetMute", &WSRequestHandler::SetMute },
{ "GetMute", &WSRequestHandler::GetMute },
{ "GetAudioActive", &WSRequestHandler::GetAudioActive },
{ "SetSourceName", &WSRequestHandler::SetSourceName },
{ "SetSyncOffset", &WSRequestHandler::SetSyncOffset },
{ "GetSyncOffset", &WSRequestHandler::GetSyncOffset },
@ -153,7 +156,19 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
{ "ListOutputs", &WSRequestHandler::ListOutputs },
{ "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
{ "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 {

View File

@ -89,11 +89,13 @@ class WSRequestHandler {
RpcResponse StartStreaming(const RpcRequest&);
RpcResponse StopStreaming(const RpcRequest&);
RpcResponse GetRecordingStatus(const RpcRequest&);
RpcResponse StartRecording(const RpcRequest&);
RpcResponse StopRecording(const RpcRequest&);
RpcResponse PauseRecording(const RpcRequest&);
RpcResponse ResumeRecording(const RpcRequest&);
RpcResponse GetReplayBufferStatus(const RpcRequest&);
RpcResponse StartStopReplayBuffer(const RpcRequest&);
RpcResponse StartReplayBuffer(const RpcRequest&);
RpcResponse StopReplayBuffer(const RpcRequest&);
@ -114,6 +116,7 @@ class WSRequestHandler {
RpcResponse ToggleMute(const RpcRequest&);
RpcResponse SetMute(const RpcRequest&);
RpcResponse GetMute(const RpcRequest&);
RpcResponse GetAudioActive(const RpcRequest&);
RpcResponse SetSourceName(const RpcRequest&);
RpcResponse SetSyncOffset(const RpcRequest&);
RpcResponse GetSyncOffset(const RpcRequest&);
@ -171,4 +174,16 @@ class WSRequestHandler {
RpcResponse GetOutputInfo(const RpcRequest&);
RpcResponse StartOutput(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&);
};

View 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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 4.9.0
*/
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 sourceTypeId = obs_source_get_id(source);
if (isMediaSource(sourceTypeId)) {
OBSDataAutoRelease sourceData = obs_data_create();
obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source));
obs_data_set_string(sourceData, "sourceKind", sourceTypeId.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);
}

View File

@ -1,8 +1,10 @@
#include "obs-websocket.h"
#include "WSRequestHandler.h"
#include <functional>
#include <util/platform.h>
#include "Utils.h"
#include "WSEvents.h"
RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> callback)
{
@ -13,6 +15,33 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> c
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.
*

View File

@ -1,7 +1,27 @@
#include "obs-websocket.h"
#include "WSEvents.h"
#include "Utils.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.
*

View File

@ -92,8 +92,8 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) {
*
* @return {String} `name` Scene Item name.
* @return {int} `itemId` Scene Item ID.
* @return {int} `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.x` The x position of the source from the left.
* @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 {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.
@ -158,8 +158,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 (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)} `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.x` The new x 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 {double (optional)} `rotation` The new clockwise rotation of the item in degrees.
* @param {double (optional)} `scale.x` The new x scale of the item.
@ -213,10 +213,10 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
vec2 newPosition = oldPosition;
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")) {
newPosition.y = obs_data_get_int(reqPosition, "y");
newPosition.y = obs_data_get_double(reqPosition, "y");
}
if (obs_data_has_user_value(reqPosition, "alignment")) {

View File

@ -192,6 +192,10 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request)
if (useDecibel) {
volume = obs_mul_to_db(volume);
}
if (volume == -INFINITY) {
volume = -100.0;
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "name", obs_source_get_name(source));
@ -341,6 +345,40 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request)
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.
*