├── .github ├── scripts │ ├── build-linux.zsh │ ├── build-macos.zsh │ ├── package-linux.zsh │ ├── package-macos.zsh │ ├── utils.zsh │ │ ├── mkcd │ │ ├── log_error │ │ ├── log_warning │ │ ├── log_debug │ │ ├── log_output │ │ ├── log_status │ │ ├── log_info │ │ ├── read_codesign │ │ ├── read_codesign_user │ │ ├── read_codesign_installer │ │ ├── setup_ccache │ │ ├── set_loglevel │ │ ├── check_macos │ │ ├── check_linux │ │ ├── read_codesign_pass │ │ ├── setup_linux │ │ ├── check_packages │ │ ├── setup_obs │ │ └── setup_macos │ ├── .Brewfile │ ├── .Wingetfile │ ├── .Aptfile │ ├── check-changes.sh │ ├── build-linux.sh │ ├── package-linux.sh │ ├── utils.pwsh │ │ ├── Check-Git.ps1 │ │ ├── Ensure-Location.ps1 │ │ ├── Invoke-External.ps1 │ │ ├── Install-BuildDependencies.ps1 │ │ ├── Expand-ArchiveExt.ps1 │ │ ├── Logger.ps1 │ │ ├── Setup-Obs.ps1 │ │ ├── Invoke-GitCheckout.ps1 │ │ └── Setup-Host.ps1 │ ├── check-cmake.sh │ ├── check-format.sh │ ├── Package-Windows.ps1 │ ├── Build-Windows.ps1 │ ├── .package.zsh │ └── .build.zsh ├── actions │ ├── build-plugin │ │ └── action.yml │ └── package-plugin │ │ └── action.yml └── workflows │ └── main.yml ├── data └── locale │ └── en-US.ini ├── .gitignore ├── README.md ├── .cmake-format.json ├── cmake ├── bundle │ ├── macos │ │ ├── entitlements.plist │ │ ├── Plugin-Info.plist.in │ │ └── installer-macos.pkgproj.in │ └── windows │ │ ├── resource.rc.in │ │ └── installer-Windows.iss.in └── ObsPluginHelpers.cmake ├── src ├── plugin-macros.h.in └── obs-lottie.cpp ├── .clang-format ├── CMakeLists.txt └── LICENSE /.github/scripts/build-linux.zsh: -------------------------------------------------------------------------------- 1 | .build.zsh -------------------------------------------------------------------------------- /.github/scripts/build-macos.zsh: -------------------------------------------------------------------------------- 1 | .build.zsh -------------------------------------------------------------------------------- /.github/scripts/package-linux.zsh: -------------------------------------------------------------------------------- 1 | .package.zsh -------------------------------------------------------------------------------- /.github/scripts/package-macos.zsh: -------------------------------------------------------------------------------- 1 | .package.zsh -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/mkcd: -------------------------------------------------------------------------------- 1 | [[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1} 2 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /.github/scripts/.Brewfile: -------------------------------------------------------------------------------- 1 | brew "ccache" 2 | brew "coreutils" 3 | brew "cmake" 4 | brew "git" 5 | brew "jq" 6 | brew "ninja" 7 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_warning: -------------------------------------------------------------------------------- 1 | if (( _loglevel > 0 )) { 2 | local icon=' =>' 3 | 4 | print -PR "%F{3} ${(r:5:)icon} ${@}%f" 5 | } 6 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_debug: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 2 )) print -PR -e -- "%F{220}DEBUG: ${@}%f" 4 | -------------------------------------------------------------------------------- /data/locale/en-US.ini: -------------------------------------------------------------------------------- 1 | Looping="Loop" 2 | ClearOnMediaEnd="Show nothing when playback ends" 3 | RestartWhenActivated="Restart playback when source becomes active" 4 | -------------------------------------------------------------------------------- /.github/scripts/.Wingetfile: -------------------------------------------------------------------------------- 1 | package '7zip.7zip', path: '7-zip', bin: '7z' 2 | package 'cmake', path: 'Cmake\bin', bin: 'cmake' 3 | package 'innosetup', path: 'Inno Setup 6', bin: 'iscc' 4 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_output: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon='' 5 | 6 | print -PR " ${(r:5:)icon} ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_status: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' >' 5 | 6 | print -PR "%F{2} ${(r:5:)icon}%f ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_info: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | /build/ 4 | /build_*/ 5 | /release/ 6 | /installer/Output/ 7 | 8 | .vscode 9 | .idea 10 | 11 | # ignore generated files 12 | *.generated.* 13 | **/.Brewfile.lock.json 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OBS Lottie 2 | 3 | This plugin can play [Lottie](https://airbnb.design/lottie/) files in [Open Broadcast Software](https://obsproject.com/). 4 | 5 | # Dependencies 6 | 7 | - [rlottie](https://github.com/Samsung/rlottie) 8 | -------------------------------------------------------------------------------- /.github/scripts/.Aptfile: -------------------------------------------------------------------------------- 1 | package 'cmake' 2 | package 'ccache' 3 | package 'curl' 4 | package 'git' 5 | package 'jq' 6 | package 'ninja-build', bin: 'ninja' 7 | package 'pkg-config' 8 | package 'clang' 9 | package 'clang-format-13' 10 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT} )) { 4 | typeset -g CODESIGN_IDENT 5 | log_info 'Setting up identity for application codesigning...' 6 | read CODESIGN_IDENT'?Apple Developer Application ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_user: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_USER} )) { 4 | typeset -g CODESIGN_IDENT_USER 5 | log_info 'Setting up developer id for codesigning...' 6 | read CODESIGN_IDENT_USER'?Apple Developer ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_installer: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_INSTALLER} )) { 4 | typeset -g CODESIGN_IDENT_INSTALLER 5 | log_info 'Setting up identity for installer package codesigning...' 6 | read CODESIGN_IDENT_INSTALLER'?Apple Developer Installer ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/check-changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dirty=$(git ls-files --modified) 3 | 4 | set +x 5 | if [[ $dirty ]]; then 6 | echo "=================================" 7 | echo "Files were not formatted properly" 8 | echo "$dirty" 9 | echo "=================================" 10 | exit 1 11 | fi -------------------------------------------------------------------------------- /.github/scripts/build-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! type zsh > /dev/null 2>&1; then 4 | echo ' => Installing script dependency Zsh.' 5 | 6 | sudo apt-get -y update 7 | sudo apt-get -y install zsh 8 | fi 9 | 10 | SCRIPT=$(readlink -f "${0}") 11 | SCRIPT_DIR=$(dirname "${SCRIPT}") 12 | 13 | zsh ${SCRIPT_DIR}/build-linux.zsh "${@}" 14 | -------------------------------------------------------------------------------- /.github/scripts/package-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! type zsh > /dev/null 2>&1; then 4 | echo ' => Installing script dependency Zsh.' 5 | 6 | sudo apt-get update 7 | sudo apt-get install zsh 8 | fi 9 | 10 | SCRIPT=$(readlink -f "${0}") 11 | SCRIPT_DIR=$(dirname "${SCRIPT}") 12 | 13 | zsh ${SCRIPT_DIR}/package-linux.zsh "${@}" 14 | -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "additional_commands": { 3 | "find_qt": { 4 | "flags": [], 5 | "kwargs": { 6 | "COMPONENTS": "+", 7 | "COMPONENTS_WIN": "+", 8 | "COMPONENTS_MACOS": "+", 9 | "COMPONENTS_LINUX": "+" 10 | } 11 | } 12 | }, 13 | "format": { 14 | "line_width": 100 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_ccache: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_warning 2 | 3 | if (( ${+commands[ccache]} )) { 4 | log_debug "Found ccache at ${commands[ccache]}" 5 | 6 | if (( ${+CI} )) { 7 | ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache" 8 | ccache --set-config=max_size="${CCACHE_SIZE:-500M}" 9 | ccache --set-config=compression=true 10 | ccache -z > /dev/null 11 | } 12 | } else { 13 | log_warning "No ccache found on the system" 14 | } 15 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/set_loglevel: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_error 2 | 3 | local -r _usage="Usage: %B${0}%b 4 | 5 | Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" 6 | 7 | if (( ! # )); then 8 | log_error 'Called without arguments.' 9 | log_output ${_usage} 10 | return 2 11 | elif (( ${1} >= 4 )); then 12 | log_error 'Called with loglevel > 3.' 13 | log_output ${_usage} 14 | fi 15 | 16 | typeset -g -i -r _loglevel=${1} 17 | log_debug "Log level set to '${1}'" 18 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_macos: -------------------------------------------------------------------------------- 1 | autoload -Uz is-at-least log_info log_error log_status read_codesign 2 | 3 | local macos_version=$(sw_vers -productVersion) 4 | 5 | log_info 'Checking macOS version...' 6 | if ! is-at-least 11.0 "${macos_version}"; then 7 | log_error "Minimum required macOS version is 11.0, but running on macOS ${macos_version}" 8 | return 2 9 | else 10 | log_status "macOS ${macos_version} is recent" 11 | fi 12 | 13 | log_info 'Checking for Homebrew...' 14 | if (( ! ${+commands[brew]} )) { 15 | log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)' 16 | return 2 17 | } 18 | 19 | brew bundle --file "${SCRIPT_HOME}/.Brewfile" 20 | rehash 21 | -------------------------------------------------------------------------------- /cmake/bundle/macos/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.device.camera 8 | 9 | com.apple.security.device.audio-input 10 | 11 | com.apple.security.cs.disable-library-validation 12 | 13 | 14 | com.apple.security.cs.allow-dyld-environment-variables 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Check-Git.ps1: -------------------------------------------------------------------------------- 1 | function Check-Git { 2 | <# 3 | .SYNOPSIS 4 | Ensures available git executable on host system. 5 | .DESCRIPTION 6 | Checks whether a git command is available on the host system. If none is found, 7 | Git is installed via winget. 8 | .EXAMPLE 9 | Check-Git 10 | #> 11 | 12 | if ( ! ( Test-Path function:Log-Info ) ) { 13 | . $PSScriptRoot/Logger.ps1 14 | } 15 | 16 | Log-Information 'Checking for Git executable...' 17 | 18 | if ( ! ( Get-Command git ) ) { 19 | Log-Warning 'No Git executable found. Will try to install via winget.' 20 | winget install git 21 | } else { 22 | Log-Debug "Git found at $(Get-Command git)." 23 | Log-Status "Git found." 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Ensure-Location.ps1: -------------------------------------------------------------------------------- 1 | function Ensure-Location { 2 | <# 3 | .SYNOPSIS 4 | Ensures current location to be set to specified directory. 5 | .DESCRIPTION 6 | If specified directory exists, switch to it. Otherwise create it, 7 | then switch. 8 | .EXAMPLE 9 | Ensure-Location "My-Directory" 10 | Ensure-Location -Path "Path-To-My-Directory" 11 | #> 12 | 13 | param( 14 | [Parameter(Mandatory)] 15 | [string] $Path 16 | ) 17 | 18 | if ( ! ( Test-Path $Path ) ) { 19 | $_Params = @{ 20 | ItemType = "Directory" 21 | Path = ${Path} 22 | ErrorAction = "SilentlyContinue" 23 | } 24 | 25 | New-Item @_Params | Set-Location 26 | } else { 27 | Set-Location -Path ${Path} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cmake/bundle/macos/Plugin-Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | ${MACOSX_PLUGIN_BUNDLE_NAME} 7 | CFBundleIdentifier 8 | ${MACOSX_PLUGIN_GUI_IDENTIFIER} 9 | CFBundleVersion 10 | ${MACOSX_PLUGIN_BUNDLE_VERSION} 11 | CFBundleShortVersionString 12 | ${MACOSX_PLUGIN_SHORT_VERSION_STRING} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleExecutable 16 | ${MACOSX_PLUGIN_EXECUTABLE_NAME} 17 | CFBundlePackageType 18 | ${MACOSX_PLUGIN_BUNDLE_TYPE} 19 | CFBundleSupportedPlatforms 20 | 21 | MacOSX 22 | 23 | LSMinimumSystemVersion 24 | 10.13 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/plugin-macros.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | Plugin Name 3 | Copyright (C) 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program. If not, see 17 | */ 18 | 19 | #ifndef PLUGINNAME_H 20 | #define PLUGINNAME_H 21 | 22 | #define PLUGIN_NAME "@CMAKE_PROJECT_NAME@" 23 | #define PLUGIN_VERSION "@CMAKE_PROJECT_VERSION@" 24 | 25 | #define blog(level, msg, ...) blog(level, "[" PLUGIN_NAME "] " msg, ##__VA_ARGS__) 26 | 27 | #endif // PLUGINNAME_H -------------------------------------------------------------------------------- /cmake/bundle/windows/resource.rc.in: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 3 | PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 4 | FILEFLAGSMASK 0x0L 5 | #ifdef _DEBUG 6 | FILEFLAGS 0x1L 7 | #else 8 | FILEFLAGS 0x0L 9 | #endif 10 | FILEOS 0x0L 11 | FILETYPE 0x2L 12 | FILESUBTYPE 0x0L 13 | BEGIN 14 | BLOCK "StringFileInfo" 15 | BEGIN 16 | BLOCK "040904b0" 17 | BEGIN 18 | VALUE "CompanyName", "${PLUGIN_AUTHOR}" 19 | VALUE "FileDescription", "${PROJECT_NAME}" 20 | VALUE "FileVersion", "${PROJECT_VERSION}" 21 | VALUE "InternalName", "${PROJECT_NAME}" 22 | VALUE "LegalCopyright", "(C) ${PLUGIN_AUTHOR}" 23 | VALUE "OriginalFilename", "${PROJECT_NAME}" 24 | VALUE "ProductName", "${PROJECT_NAME}" 25 | VALUE "ProductVersion", "${PROJECT_VERSION}" 26 | END 27 | END 28 | BLOCK "VarFileInfo" 29 | BEGIN 30 | VALUE "Translation", 0x409, 1200 31 | END 32 | END 33 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info log_status log_error log_debug log_warning 2 | 3 | log_debug 'Checking for apt-get...' 4 | if (( ! ${+commands[apt-get]} )) { 5 | log_error 'No apt-get command found. Please install apt' 6 | return 2 7 | } else { 8 | log_debug "Apt-get located at ${commands[apt-get]}" 9 | } 10 | 11 | local -a dependencies=("${(f)$(<${SCRIPT_HOME}/.Aptfile)}") 12 | local -a install_list 13 | local binary 14 | 15 | for dependency (${dependencies}) { 16 | local -a tokens=(${(s: :)dependency//(,|:|\')/}) 17 | 18 | if [[ ! ${tokens[1]} == 'package' ]] continue 19 | 20 | if [[ ${#tokens} -gt 2 && ${tokens[3]} == 'bin' ]] { 21 | binary=${tokens[4]} 22 | } else { 23 | binary=${tokens[2]} 24 | } 25 | 26 | if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]}) 27 | } 28 | 29 | local -a _quiet=('' '--quiet') 30 | 31 | log_debug "List of dependencies to install: ${install_list}" 32 | if (( ${#install_list} )) { 33 | if (( ! ${+CI} )) log_warning 'Dependency installation via apt may require elevated privileges' 34 | 35 | sudo apt-get -y install ${install_list} ${_quiet[(( (_loglevel == 0) + 1 ))]} 36 | } 37 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Invoke-External.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-External { 2 | <# 3 | .SYNOPSIS 4 | Invokes a non-PowerShell command. 5 | .DESCRIPTION 6 | Runs a non-PowerShell command, and captures its return code. 7 | Throws an exception if the command returns non-zero. 8 | .EXAMPLE 9 | Invoke-External 7z x $MyArchive 10 | #> 11 | 12 | if ( $args.Count -eq 0 ) { 13 | throw 'Invoke-External called without arguments.' 14 | } 15 | 16 | if ( ! ( Test-Path function:Log-Information ) ) { 17 | . $PSScriptRoot/Logger.ps1 18 | } 19 | 20 | $Command = $args[0] 21 | $CommandArgs = @() 22 | 23 | if ( $args.Count -gt 1) { 24 | $CommandArgs = $args[1..($args.Count - 1)] 25 | } 26 | 27 | $_EAP = $ErrorActionPreference 28 | $ErrorActionPreference = "Continue" 29 | 30 | Log-Debug "Invoke-External: ${Command} ${CommandArgs}" 31 | 32 | & $command $commandArgs 33 | $Result = $LASTEXITCODE 34 | 35 | $ErrorActionPreference = $_EAP 36 | 37 | if ( $Result -ne 0 ) { 38 | throw "${Command} ${CommandArgs} exited with non-zero code ${Result}." 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/scripts/check-cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | if [ ${#} -eq 1 -a "${1}" = "VERBOSE" ]; then 7 | VERBOSITY="-l debug" 8 | else 9 | VERBOSITY="" 10 | fi 11 | 12 | if [ "${CI}" ]; then 13 | MODE="--check" 14 | else 15 | MODE="-i" 16 | fi 17 | 18 | # Runs the formatter in parallel on the code base. 19 | # Return codes: 20 | # - 1 there are files to be formatted 21 | # - 0 everything looks fine 22 | 23 | # Get CPU count 24 | OS=$(uname) 25 | NPROC=1 26 | if [[ ${OS} = "Linux" ]] ; then 27 | NPROC=$(nproc) 28 | elif [[ ${OS} = "Darwin" ]] ; then 29 | NPROC=$(sysctl -n hw.physicalcpu) 30 | fi 31 | 32 | # Discover clang-format 33 | if ! type cmake-format 2> /dev/null ; then 34 | echo "Required cmake-format not found" 35 | exit 1 36 | fi 37 | 38 | find . -type d \( \ 39 | -path ./\*build\* -o \ 40 | -path ./release -o \ 41 | -path ./deps/jansson -o \ 42 | -path ./plugins/decklink/\*/decklink-sdk -o \ 43 | -path ./plugins/enc-amf -o \ 44 | -path ./plugins/mac-syphon/syphon-framework -o \ 45 | -path ./plugins/obs-outputs/ftl-sdk -o \ 46 | -path ./plugins/obs-vst -o \ 47 | -path ./plugins/obs-browser -o \ 48 | -path ./plugins/win-dshow/libdshowcapture -o \ 49 | -path ./plugins/obs-websocket/deps \ 50 | \) -prune -false -type f -o \ 51 | -name 'CMakeLists.txt' -or \ 52 | -name '*.cmake' \ 53 | | xargs -L10 -P ${NPROC} cmake-format ${MODE} ${VERBOSITY} 54 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_pass: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Apple Developer credentials necessary: 3 | # 4 | # + Signing for distribution and notarization require an active Apple 5 | # Developer membership 6 | # + An Apple Development identity is needed for code signing 7 | # (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)') 8 | # + Your Apple developer ID is needed for notarization 9 | # + An app-specific password is necessary for notarization from CLI 10 | # + This password will be stored in your macOS keychain under the identifier 11 | # 'OBS-Codesign-Password'with access Apple's 'altool' only. 12 | ############################################################################## 13 | 14 | autoload -Uz read_codesign read_codesign_user log_info 15 | 16 | if (( ! ${+CODESIGN_IDENT} )) { 17 | read_codesign 18 | } 19 | 20 | local codesign_ident_short=$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p') 21 | 22 | if (( ! ${+CODESIGN_IDENT_USER} )) { 23 | read_codesign_user 24 | } 25 | 26 | log_info 'Setting up password for notarization keychain...' 27 | if (( ! ${+CODESIGN_IDENT_PASS} )) { 28 | read -s CODESIGN_IDENT_PASS'?Apple Developer ID password: ' 29 | } 30 | 31 | print '' 32 | log_info 'Setting up notarization keychain...' 33 | xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${codesign_ident_short}" --password "${CODESIGN_IDENT_PASS}" 34 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_error log_status log_info mkcd 2 | 3 | if (( ! ${+project_root} )) { 4 | log_error "'project_root' not set. Please set before running ${0}." 5 | return 2 6 | } 7 | 8 | if (( ! ${+target} )) { 9 | log_error "'target' not set. Please set before running ${0}." 10 | return 2 11 | } 12 | 13 | pushd ${project_root} 14 | 15 | typeset -g QT_VERSION 16 | read -r QT_VERSION <<< \ 17 | "$(jq -r --arg target "${target}" \ 18 | '.platformConfig[$target] | { qtVersion } | join(" ")' \ 19 | ${project_root}/buildspec.json)" 20 | 21 | if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) { 22 | log_info 'Installing obs build dependencies...' 23 | 24 | sudo apt-get install -y \ 25 | build-essential \ 26 | libcurl4-openssl-dev \ 27 | libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev \ 28 | libswresample-dev libswscale-dev \ 29 | libjansson-dev \ 30 | libx11-xcb-dev \ 31 | libgles2-mesa-dev \ 32 | libwayland-dev \ 33 | libpulse-dev 34 | 35 | local -a _qt_packages=() 36 | 37 | if (( QT_VERSION == 5 )) { 38 | _qt_packages+=( 39 | qtbase5-dev 40 | libqt5svg5-dev 41 | qtbase5-private-dev 42 | libqt5x11extras5-dev 43 | ) 44 | } elif (( QT_VERSION == 6 )) { 45 | _qt_packages+=( 46 | qt6-base-dev 47 | libqt6svg6-dev 48 | qt6-base-private-dev 49 | ) 50 | } else { 51 | log_error "Unsupported Qt version '${QT_VERSION}' specified." 52 | return 2 53 | } 54 | 55 | sudo apt-get install -y ${_qt_packages} 56 | } 57 | 58 | local deps_version 59 | read -r deps_version <<< \ 60 | "$(jq -r '.dependencies.prebuilt.version' ${buildspec_file})" 61 | 62 | typeset -g OBS_DEPS_VERSION=${deps_version} 63 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_packages: -------------------------------------------------------------------------------- 1 | if (( ! ${+commands[packagesbuild]} )) { 2 | autoload -Uz log_info log_status mkcd 3 | 4 | if (( ! ${+commands[curl]} )) { 5 | log_error 'curl not found. Please install curl.' 6 | return 2 7 | } 8 | 9 | if (( ! ${+project_root} )) { 10 | log_error "'project_root' not set. Please set before running ${0}." 11 | return 2 12 | } 13 | 14 | local -a curl_opts=() 15 | if (( ! ${+CI} )) { 16 | curl_opts+=(--progress-bar) 17 | } else { 18 | curl_opts+=(--show-error --silent) 19 | } 20 | curl_opts+=(--location -O ${@}) 21 | 22 | log_info 'Installing Packages.app...' 23 | 24 | pushd 25 | mkcd ${project_root:h}/obs-build-dependencies 26 | 27 | local packages_url='http://s.sudre.free.fr/Software/files/Packages.dmg' 28 | local packages_hash='6afdd25386295974dad8f078b8f1e41cabebd08e72d970bf92f707c7e48b16c9' 29 | 30 | if [[ ! -f Packages.dmg ]] { 31 | log_status 'Download Packages.app' 32 | curl ${curl_opts} ${packages_url} 33 | } 34 | 35 | local image_checksum 36 | read -r image_checksum _ <<< "$(sha256sum Packages.dmg)" 37 | 38 | if [[ ${packages_hash} != ${image_checksum} ]] { 39 | log_error "Checksum mismatch of Packages.app download. 40 | Expected : ${packages_hash} 41 | Actual : ${image_checksum}" 42 | return 2 43 | } 44 | 45 | hdiutil attach -noverify Packages.dmg &> /dev/null && log_status 'Packages.dmg image mounted.' 46 | 47 | log_info 'Installing Packages.app...' 48 | packages_volume=$(hdiutil info -plist | grep '/Volumes/Packages' | sed 's/.*\(\/Volumes\/[^<]*\)<\/string>/\1/') 49 | 50 | sudo installer -pkg "${packages_volume}/packages/Packages.pkg" -target / && rehash 51 | hdiutil detach ${packages_volume} &> /dev/null && log_status 'Packages.dmg image unmounted.' 52 | } 53 | -------------------------------------------------------------------------------- /.github/scripts/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh 3 | 4 | set -o errexit 5 | set -o pipefail 6 | set -o nounset 7 | 8 | if [ ${#} -eq 1 ]; then 9 | VERBOSITY="--verbose" 10 | else 11 | VERBOSITY="" 12 | fi 13 | 14 | # Runs the Clang Formatter in parallel on the code base. 15 | # Return codes: 16 | # - 1 there are files to be formatted 17 | # - 0 everything looks fine 18 | 19 | # Get CPU count 20 | OS=$(uname) 21 | NPROC=1 22 | if [[ ${OS} = "Linux" ]] ; then 23 | NPROC=$(nproc) 24 | elif [[ ${OS} = "Darwin" ]] ; then 25 | NPROC=$(sysctl -n hw.physicalcpu) 26 | fi 27 | 28 | # Discover clang-format 29 | if type clang-format-13 2> /dev/null ; then 30 | CLANG_FORMAT=clang-format-13 31 | elif type clang-format 2> /dev/null ; then 32 | # Clang format found, but need to check version 33 | CLANG_FORMAT=clang-format 34 | V=$(clang-format --version) 35 | if [[ $V != *"version 13.0"* ]]; then 36 | echo "clang-format is not 13.0 (returned ${V})" 37 | exit 1 38 | fi 39 | else 40 | echo "No appropriate clang-format found (expected clang-format-13.0.0, or clang-format)" 41 | exit 1 42 | fi 43 | 44 | find . -type d \( \ 45 | -path ./\*build\* -o \ 46 | -path ./release -o \ 47 | -path ./cmake -o \ 48 | -path ./plugins/decklink/\*/decklink-sdk -o \ 49 | -path ./plugins/enc-amf -o \ 50 | -path ./plugins/mac-syphon/syphon-framework -o \ 51 | -path ./plugins/obs-outputs/ftl-sdk -o \ 52 | -path ./plugins/obs-websocket/deps \ 53 | \) -prune -false -type f -o \ 54 | -name '*.h' -or \ 55 | -name '*.hpp' -or \ 56 | -name '*.m' -or \ 57 | -name '*.mm' -or \ 58 | -name '*.c' -or \ 59 | -name '*.cpp' \ 60 | | xargs -L100 -P ${NPROC} "${CLANG_FORMAT}" ${VERBOSITY} -i -style=file -fallback-style=none 61 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Install-BuildDependencies.ps1: -------------------------------------------------------------------------------- 1 | function Install-BuildDependencies { 2 | <# 3 | .SYNOPSIS 4 | Installs required build dependencies. 5 | .DESCRIPTION 6 | Additional packages might be needed for successful builds. This module contains additional 7 | dependencies available for installation via winget and, if possible, adds their locations 8 | to the environment path for future invocation. 9 | .EXAMPLE 10 | Install-BuildDependencies 11 | #> 12 | 13 | param( 14 | [string] $WingetFile = "$PSScriptRoot/.Wingetfile" 15 | ) 16 | 17 | if ( ! ( Test-Path function:Log-Warning ) ) { 18 | . $PSScriptRoot/Logger.ps1 19 | } 20 | 21 | $Host64Bit = [System.Environment]::Is64BitOperatingSystem 22 | 23 | $Paths = $Env:Path -split [System.IO.Path]::PathSeparator 24 | 25 | $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements') 26 | 27 | if ( $script:Quiet ) { 28 | $WingetOptions += '--silent' 29 | } 30 | 31 | Get-Content $WingetFile | ForEach-Object { 32 | $_, $Package, $_, $Path, $_, $Binary = ([regex]::Split($_, " (?=(?:[^']|'[^']*')*$)")) -replace ',', '' -replace "'",'' 33 | 34 | (${Env:ProgramFiles(x86)}, $Env:ProgramFiles) | ForEach-Object { 35 | $Prefix = $_ 36 | $FullPath = "${Prefix}\${Path}" 37 | if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) { 38 | $Paths += $FullPath 39 | $Env:Path = $Paths -join [System.IO.Path]::PathSeparator 40 | } 41 | } 42 | 43 | Log-Debug "Checking for command ${Binary}" 44 | $Found = Get-Command -ErrorAction SilentlyContinue $Binary 45 | 46 | if ( $Found ) { 47 | Log-Status "Found dependency ${Binary} as $($Found.Source)" 48 | } else { 49 | Log-Status "Installing package ${Package}" 50 | 51 | try { 52 | $Params = $WingetOptions + $Package 53 | 54 | winget @Params 55 | } catch { 56 | throw "Error while installing winget package ${Package}: $_" 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/actions/build-plugin/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup and build plugin' 2 | description: 'Builds the plugin for specified architecture and build config.' 3 | inputs: 4 | target: 5 | description: 'Build target for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'Release' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | codesignIdent: 16 | description: 'Developer ID for application codesigning (macOS only)' 17 | required: false 18 | default: '-' 19 | visualStudio: 20 | description: 'Visual Studio version (Windows only)' 21 | required: false 22 | default: 'Visual Studio 16 2019' 23 | workingDirectory: 24 | description: 'Working directory for packaging' 25 | required: false 26 | default: ${{ github.workspace }} 27 | runs: 28 | using: 'composite' 29 | steps: 30 | - name: Run macOS Build 31 | if: ${{ runner.os == 'macOS' }} 32 | shell: zsh {0} 33 | env: 34 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 35 | run: | 36 | build_args=( 37 | -c ${{ inputs.config }} 38 | -t macos-${{ inputs.target }} 39 | ) 40 | 41 | if [[ '${{ inputs.codesign }}' == 'true' ]] build_args+=(-s) 42 | if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug) 43 | 44 | ${{ inputs.workingDirectory }}/.github/scripts/build-macos.zsh ${build_args} 45 | 46 | - name: Run Linux Build 47 | if: ${{ runner.os == 'Linux' }} 48 | shell: bash 49 | run: | 50 | build_args=( 51 | -c ${{ inputs.config }} 52 | -t linux-${{ inputs.target }} 53 | ) 54 | 55 | if [[ -n "${CI}" && -n "${RUNNER_DEBUG}" ]]; then 56 | build_args+=(--debug) 57 | fi 58 | 59 | ${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh "${build_args[@]}" 60 | 61 | - name: Run Windows Build 62 | if: ${{ runner.os == 'Windows' }} 63 | shell: pwsh 64 | run: | 65 | $BuildArgs = @{ 66 | Target = '${{ inputs.target }}' 67 | Configuration = '${{ inputs.config }}' 68 | CMakeGenerator = '${{ inputs.visualStudio }}' 69 | } 70 | 71 | if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) { 72 | $BuildArgs += @{ 73 | Debug = $true 74 | } 75 | } 76 | 77 | ${{ inputs.workingDirectory }}/.github/scripts/Build-Windows.ps1 @BuildArgs 78 | -------------------------------------------------------------------------------- /cmake/bundle/windows/installer-Windows.iss.in: -------------------------------------------------------------------------------- 1 | #define MyAppName "@CMAKE_PROJECT_NAME@" 2 | #define MyAppVersion "@CMAKE_PROJECT_VERSION@" 3 | #define MyAppPublisher "@PLUGIN_AUTHOR@" 4 | #define MyAppURL "http://www.mywebsite.com" 5 | 6 | [Setup] 7 | ; NOTE: The value of AppId uniquely identifies this application. 8 | ; Do not use the same AppId value in installers for other applications. 9 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 10 | AppId={{CD703FE5-1F2C-4837-BD3D-DD840D83C3E3} 11 | AppName={#MyAppName} 12 | AppVersion={#MyAppVersion} 13 | AppPublisher={#MyAppPublisher} 14 | AppPublisherURL={#MyAppURL} 15 | AppSupportURL={#MyAppURL} 16 | AppUpdatesURL={#MyAppURL} 17 | DefaultDirName={code:GetDirName} 18 | DefaultGroupName={#MyAppName} 19 | OutputBaseFilename={#MyAppName}-{#MyAppVersion}-Windows-Installer 20 | Compression=lzma 21 | SolidCompression=yes 22 | DirExistsWarning=no 23 | 24 | [Languages] 25 | Name: "english"; MessagesFile: "compiler:Default.isl" 26 | 27 | [Files] 28 | Source: "..\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 29 | Source: "..\LICENSE"; Flags: dontcopy 30 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 31 | 32 | [Icons] 33 | Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}" 34 | Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" 35 | 36 | [Code] 37 | procedure InitializeWizard(); 38 | var 39 | GPLText: AnsiString; 40 | Page: TOutputMsgMemoWizardPage; 41 | begin 42 | ExtractTemporaryFile('LICENSE'); 43 | LoadStringFromFile(ExpandConstant('{tmp}\LICENSE'), GPLText); 44 | Page := CreateOutputMsgMemoPage(wpWelcome, 45 | 'License Information', 'Please review the license terms before installing {#MyAppName}', 46 | 'Press Page Down to see the rest of the agreement. Once you are aware of your rights, click Next to continue.', 47 | String(GPLText) 48 | ); 49 | end; 50 | 51 | // credit where it's due : 52 | // following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45 53 | function GetDirName(Value: string): string; 54 | var 55 | InstallPath: string; 56 | begin 57 | // initialize default path, which will be returned when the following registry 58 | // key queries fail due to missing keys or for some different reason 59 | Result := '{autopf}\obs-studio'; 60 | // query the first registry value; if this succeeds, return the obtained value 61 | if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then 62 | Result := InstallPath 63 | end; 64 | 65 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Expand-ArchiveExt.ps1: -------------------------------------------------------------------------------- 1 | function Expand-ArchiveExt { 2 | <# 3 | .SYNOPSIS 4 | Expands archive files. 5 | .DESCRIPTION 6 | Allows extraction of zip, 7z, gz, and xz archives. 7 | Requires tar and 7-zip to be available on the system. 8 | Archives ending with .zip but created using LZMA compression are 9 | expanded using 7-zip as a fallback. 10 | .EXAMPLE 11 | Expand-ArchiveExt -Path 12 | Expand-ArchiveExt -Path -DestinationPath 13 | #> 14 | 15 | param( 16 | [Parameter(Mandatory)] 17 | [string] $Path, 18 | [string] $DestinationPath = [System.IO.Path]::GetFileNameWithoutExtension($Path), 19 | [switch] $Force 20 | ) 21 | 22 | switch ( [System.IO.Path]::GetExtension($Path) ) { 23 | .zip { 24 | try { 25 | Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force:$Force 26 | } catch { 27 | if ( Get-Command 7z ) { 28 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 29 | } else { 30 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 31 | } 32 | } 33 | break 34 | } 35 | { ( $_ -eq ".7z" ) -or ( $_ -eq ".exe" ) } { 36 | if ( Get-Command 7z ) { 37 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 38 | } else { 39 | throw "Extraction utility 7-zip not found. Please install 7-zip first." 40 | } 41 | break 42 | } 43 | .gz { 44 | try { 45 | Invoke-External tar -x -o $DestinationPath -f $Path 46 | } catch { 47 | if ( Get-Command 7z ) { 48 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 49 | } else { 50 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 51 | } 52 | } 53 | break 54 | } 55 | .xz { 56 | try { 57 | Invoke-External tar -x -o $DestinationPath -f $Path 58 | } catch { 59 | if ( Get-Command 7z ) { 60 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 61 | } else { 62 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 63 | } 64 | } 65 | } 66 | default { 67 | throw "Unsupported archive extension provided." 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /.github/scripts/Package-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 4 | [string] $Configuration = 'RelWithDebInfo', 5 | [ValidateSet('x86', 'x64', 'x86+x64')] 6 | [string] $Target, 7 | [switch] $BuildInstaller = $false 8 | ) 9 | 10 | $ErrorActionPreference = 'Stop' 11 | 12 | if ( $DebugPreference -eq 'Continue' ) { 13 | $VerbosePreference = 'Continue' 14 | $InformationPreference = 'Continue' 15 | } 16 | 17 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 18 | Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 19 | exit 2 20 | } 21 | 22 | function Package { 23 | trap { 24 | Write-Error $_ 25 | exit 2 26 | } 27 | 28 | $ScriptHome = $PSScriptRoot 29 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 30 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 31 | 32 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 33 | 34 | foreach( $Utility in $UtilityFunctions ) { 35 | Write-Debug "Loading $($Utility.FullName)" 36 | . $Utility.FullName 37 | } 38 | 39 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 40 | $ProductName = $BuildSpec.name 41 | $ProductVersion = $BuildSpec.version 42 | 43 | $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}" 44 | 45 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 46 | 47 | Log-Information "Packaging ${ProductName}..." 48 | 49 | $RemoveArgs = @{ 50 | ErrorAction = 'SilentlyContinue' 51 | Path = @( 52 | "${ProjectRoot}/release/${ProductName}-*-windows-*.zip" 53 | "${ProjectRoot}/release/${ProductName}-*-windows-*.exe" 54 | ) 55 | } 56 | 57 | Remove-Item @RemoveArgs 58 | 59 | if ( ( $BuildInstaller ) ) { 60 | if ( $Target -eq 'x86+x64' ) { 61 | $IsccCandidates = Get-ChildItem -Recurse -Path '*.iss' 62 | 63 | if ( $IsccCandidates.length -gt 0 ) { 64 | $IsccFile = $IsccCandidates[0].FullName 65 | } else { 66 | $IsccFile = '' 67 | } 68 | } else { 69 | $IsccFile = "${ProjectRoot}/build_${Target}/installer-Windows.generated.iss" 70 | } 71 | 72 | if ( ! ( Test-Path -Path $IsccFile ) ) { 73 | throw 'InnoSetup install script not found. Run the build script or the CMake build and install procedures first.' 74 | } 75 | 76 | Log-Information 'Creating InnoSetup installer...' 77 | Push-Location -Stack BuildTemp 78 | Ensure-Location -Path "${ProjectRoot}/release" 79 | Invoke-External iscc ${IsccFile} /O. /F"${OutputName}-Installer" 80 | Pop-Location -Stack BuildTemp 81 | } 82 | 83 | $CompressArgs = @{ 84 | Path = (Get-ChildItem -Path "${ProjectRoot}/release" -Exclude "${OutputName}*.*") 85 | CompressionLevel = 'Optimal' 86 | DestinationPath = "${ProjectRoot}/release/${OutputName}.zip" 87 | } 88 | 89 | Compress-Archive -Force @CompressArgs 90 | } 91 | 92 | Package 93 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Logger.ps1: -------------------------------------------------------------------------------- 1 | function Log-Debug { 2 | [CmdletBinding()] 3 | param( 4 | [Parameter(Mandatory,ValueFromPipeline)] 5 | [ValidateNotNullOrEmpty()] 6 | [string[]] $Message 7 | ) 8 | 9 | Process { 10 | foreach($m in $Message) { 11 | Write-Debug $m 12 | } 13 | } 14 | } 15 | 16 | function Log-Verbose { 17 | [CmdletBinding()] 18 | param( 19 | [Parameter(Mandatory,ValueFromPipeline)] 20 | [ValidateNotNullOrEmpty()] 21 | [string[]] $Message 22 | ) 23 | 24 | Process { 25 | foreach($m in $Message) { 26 | Write-Verbose $m 27 | } 28 | } 29 | } 30 | 31 | function Log-Warning { 32 | [CmdletBinding()] 33 | param( 34 | [Parameter(Mandatory,ValueFromPipeline)] 35 | [ValidateNotNullOrEmpty()] 36 | [string[]] $Message 37 | ) 38 | 39 | Process { 40 | foreach($m in $Message) { 41 | Write-Warning $m 42 | } 43 | } 44 | } 45 | 46 | function Log-Error { 47 | [CmdletBinding()] 48 | param( 49 | [Parameter(Mandatory,ValueFromPipeline)] 50 | [ValidateNotNullOrEmpty()] 51 | [string[]] $Message 52 | ) 53 | 54 | Process { 55 | foreach($m in $Message) { 56 | Write-Error $m 57 | } 58 | } 59 | } 60 | 61 | function Log-Information { 62 | [CmdletBinding()] 63 | param( 64 | [Parameter(Mandatory,ValueFromPipeline)] 65 | [ValidateNotNullOrEmpty()] 66 | [string[]] $Message 67 | ) 68 | 69 | Process { 70 | if ( ! ( $script:Quiet ) ) { 71 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 72 | $Icon = ' =>' 73 | 74 | foreach($m in $Message) { 75 | Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) " 76 | Write-Host "${m}" 77 | } 78 | } 79 | } 80 | } 81 | 82 | function Log-Status { 83 | [CmdletBinding()] 84 | param( 85 | [Parameter(Mandatory,ValueFromPipeline)] 86 | [ValidateNotNullOrEmpty()] 87 | [string[]] $Message 88 | ) 89 | 90 | Process { 91 | if ( ! ( $script:Quiet ) ) { 92 | $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' }) 93 | $Icon = ' >' 94 | 95 | foreach($m in $Message) { 96 | Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) " 97 | Write-Host "${m}" 98 | } 99 | } 100 | } 101 | } 102 | 103 | function Log-Output { 104 | [CmdletBinding()] 105 | param( 106 | [Parameter(Mandatory,ValueFromPipeline)] 107 | [ValidateNotNullOrEmpty()] 108 | [string[]] $Message 109 | ) 110 | 111 | Process { 112 | if ( ! ( $script:Quiet ) ) { 113 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 114 | $Icon = '' 115 | 116 | foreach($m in $Message) { 117 | Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}" 118 | } 119 | } 120 | } 121 | } 122 | 123 | $Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5 124 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Setup-Obs.ps1: -------------------------------------------------------------------------------- 1 | function Setup-Obs { 2 | if ( ! ( Test-Path function:Log-Output ) ) { 3 | . $PSScriptRoot/Logger.ps1 4 | } 5 | 6 | if ( ! ( Test-Path function:Check-Git ) ) { 7 | . $PSScriptRoot/Check-Git.ps1 8 | } 9 | 10 | Check-Git 11 | 12 | if ( ! ( Test-Path function:Ensure-Location ) ) { 13 | . $PSScriptRoot/Ensure-Location.ps1 14 | } 15 | 16 | if ( ! ( Test-Path function:Invoke-GitCheckout ) ) { 17 | . $PSScriptRoot/Invoke-GitCheckout.ps1 18 | } 19 | 20 | if ( ! ( Test-Path function:Invoke-External ) ) { 21 | . $PSScriptRoot/Invoke-External.ps1 22 | } 23 | 24 | Log-Information 'Setting up OBS Studio...' 25 | 26 | $ObsVersion = $BuildSpec.dependencies.'obs-studio'.version 27 | $ObsRepository = $BuildSpec.dependencies.'obs-studio'.repository 28 | $ObsBranch = $BuildSpec.dependencies.'obs-studio'.branch 29 | $ObsHash = $BuildSpec.dependencies.'obs-studio'.hash 30 | 31 | if ( $ObsVersion -eq '' ) { 32 | throw 'No obs-studio version found in buildspec.json.' 33 | } 34 | 35 | Push-Location -Stack BuildTemp 36 | Ensure-Location -Path "$(Resolve-Path -Path "${ProjectRoot}/../")/obs-studio" 37 | 38 | if ( ! ( ( $script:SkipAll ) -or ( $script:SkipUnpack ) ) ) { 39 | Invoke-GitCheckout -Uri $ObsRepository -Commit $ObsHash -Path . -Branch $ObsBranch 40 | } 41 | 42 | if ( ! ( ( $script:SkipAll ) -or ( $script:SkipBuild ) ) ) { 43 | Log-Information 'Configuring OBS Studio...' 44 | 45 | $NumProcessors = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors 46 | 47 | if ( $NumProcessors -gt 1 ) { 48 | $env:UseMultiToolTask = $true 49 | $env:EnforceProcessCountAcrossBuilds = $true 50 | } 51 | 52 | $DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}" 53 | 54 | $CmakeArgs = @( 55 | '-G', $CmakeGenerator 56 | "-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}" 57 | "-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })" 58 | "-DCMAKE_BUILD_TYPE=${script:Configuration}" 59 | "-DQT_VERSION=${script:QtVersion}" 60 | '-DENABLE_PLUGINS=OFF' 61 | '-DENABLE_UI=OFF' 62 | '-DENABLE_SCRIPTING=OFF' 63 | "-DCMAKE_INSTALL_PREFIX:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")" 64 | "-DCMAKE_PREFIX_PATH:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")" 65 | ) 66 | 67 | Log-Debug "Attempting to configure OBS with CMake arguments: $($CmakeArgs | Out-String)" 68 | Log-Information "Configuring OBS..." 69 | Invoke-External cmake -S . -B plugin_build_${script:Target} @CmakeArgs 70 | 71 | Log-Information 'Building libobs and obs-frontend-api...' 72 | $CmakeArgs = @( 73 | '--config', "$( if ( $script:Configuration -eq '' ) { 'RelWithDebInfo' } else { $script:Configuration })" 74 | ) 75 | 76 | if ( $VerbosePreference -eq 'Continue' ) { 77 | $CmakeArgs+=('--verbose') 78 | } 79 | 80 | Invoke-External cmake --build plugin_build_${script:Target} @CmakeArgs -t obs-frontend-api 81 | Invoke-External cmake --install plugin_build_${script:Target} @CmakeArgs --component obs_libraries 82 | } 83 | Pop-Location -Stack BuildTemp 84 | } 85 | -------------------------------------------------------------------------------- /.github/actions/package-plugin/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Package plugin' 2 | description: 'Packages the plugin for specified architecture and build config.' 3 | inputs: 4 | target: 5 | description: 'Build target for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'Release' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | notarize: 16 | description: 'Enable notarization (macOS only)' 17 | required: false 18 | default: 'false' 19 | codesignIdent: 20 | description: 'Developer ID for application codesigning (macOS only)' 21 | required: false 22 | default: '-' 23 | installerIdent: 24 | description: 'Developer ID for installer package codesigning (macOS only)' 25 | required: false 26 | default: '' 27 | codesignUser: 28 | description: 'Apple ID username for notarization (macOS only)' 29 | required: false 30 | default: '' 31 | codesignPass: 32 | description: 'Apple ID password for notarization (macOS only)' 33 | required: false 34 | default: '' 35 | createInstaller: 36 | description: 'Create InnoSetup installer (Windows only)' 37 | required: false 38 | default: 'false' 39 | workingDirectory: 40 | description: 'Working directory for packaging' 41 | required: false 42 | default: ${{ github.workspace }} 43 | runs: 44 | using: 'composite' 45 | steps: 46 | - name: Run macOS packaging 47 | if: ${{ runner.os == 'macOS' }} 48 | shell: zsh {0} 49 | env: 50 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 51 | CODESIGN_IDENT_INSTALLER: ${{ inputs.installerIdent }} 52 | CODESIGN_IDENT_USER: ${{ inputs.codesignUser }} 53 | CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }} 54 | run: | 55 | package_args=( 56 | -c ${{ inputs.config }} 57 | -t macos-${{ inputs.target }} 58 | ) 59 | 60 | if [[ '${{ inputs.codesign }}' == 'true' ]] package_args+=(-s) 61 | if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(-n) 62 | if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug) 63 | 64 | ${{ inputs.workingDirectory }}/.github/scripts/package-macos.zsh ${package_args} 65 | 66 | - name: Run Linux packaging 67 | if: ${{ runner.os == 'Linux' }} 68 | shell: bash 69 | run: | 70 | package_args=( 71 | -c ${{ inputs.config }} 72 | -t linux-${{ inputs.target }} 73 | ) 74 | if [[ -n "${CI}" && -n "${RUNNER_DEBUG}" ]]; then 75 | build_args+=(--debug) 76 | fi 77 | 78 | ${{ inputs.workingDirectory }}/.github/scripts/package-linux.sh "${package_args[@]}" 79 | 80 | - name: Run Windows packaging 81 | if: ${{ runner.os == 'Windows' }} 82 | shell: pwsh 83 | run: | 84 | $PackageArgs = @{ 85 | Target = '${{ inputs.target }}' 86 | Configuration = '${{ inputs.config }}' 87 | } 88 | 89 | if ( '${{ inputs.createInstaller }}' -eq 'true' ) { 90 | $PackageArgs += @{BuildInstaller = $true} 91 | } 92 | 93 | if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) { 94 | $BuildArgs += @{ 95 | Debug = $true 96 | } 97 | } 98 | 99 | ${{ inputs.workingDirectory }}/.github/scripts/Package-Windows.ps1 @PackageArgs 100 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # please use clang-format version 8 or later 2 | 3 | Standard: Cpp11 4 | AccessModifierOffset: -8 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | #AllowAllArgumentsOnNextLine: false # requires clang-format 9 12 | #AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: false 18 | #AllowShortLambdasOnASingleLine: Inline # requires clang-format 9 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: false 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BraceWrapping: 27 | AfterClass: false 28 | AfterControlStatement: false 29 | AfterEnum: false 30 | AfterFunction: true 31 | AfterNamespace: false 32 | AfterObjCDeclaration: false 33 | AfterStruct: false 34 | AfterUnion: false 35 | AfterExternBlock: false 36 | BeforeCatch: false 37 | BeforeElse: false 38 | IndentBraces: false 39 | SplitEmptyFunction: true 40 | SplitEmptyRecord: true 41 | SplitEmptyNamespace: true 42 | BreakBeforeBinaryOperators: None 43 | BreakBeforeBraces: Custom 44 | BreakBeforeTernaryOperators: true 45 | BreakConstructorInitializers: BeforeColon 46 | BreakStringLiterals: false # apparently unpredictable 47 | ColumnLimit: 80 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 50 | ConstructorInitializerIndentWidth: 8 51 | ContinuationIndentWidth: 8 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | FixNamespaceComments: false 56 | ForEachMacros: 57 | - 'json_object_foreach' 58 | - 'json_object_foreach_safe' 59 | - 'json_array_foreach' 60 | IncludeBlocks: Preserve 61 | IndentCaseLabels: false 62 | IndentPPDirectives: None 63 | IndentWidth: 8 64 | IndentWrappedFunctionNames: false 65 | KeepEmptyLinesAtTheStartOfBlocks: true 66 | MaxEmptyLinesToKeep: 1 67 | NamespaceIndentation: None 68 | #ObjCBinPackProtocolList: Auto # requires clang-format 7 69 | ObjCBlockIndentWidth: 8 70 | ObjCSpaceAfterProperty: true 71 | ObjCSpaceBeforeProtocolList: true 72 | 73 | PenaltyBreakAssignment: 10 74 | PenaltyBreakBeforeFirstCallParameter: 30 75 | PenaltyBreakComment: 10 76 | PenaltyBreakFirstLessLess: 0 77 | PenaltyBreakString: 10 78 | PenaltyExcessCharacter: 100 79 | PenaltyReturnTypeOnItsOwnLine: 60 80 | 81 | PointerAlignment: Right 82 | ReflowComments: false 83 | SortIncludes: false 84 | SortUsingDeclarations: false 85 | SpaceAfterCStyleCast: false 86 | #SpaceAfterLogicalNot: false # requires clang-format 9 87 | SpaceAfterTemplateKeyword: false 88 | SpaceBeforeAssignmentOperators: true 89 | #SpaceBeforeCtorInitializerColon: true # requires clang-format 7 90 | #SpaceBeforeInheritanceColon: true # requires clang-format 7 91 | SpaceBeforeParens: ControlStatements 92 | #SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7 93 | SpaceInEmptyParentheses: false 94 | SpacesBeforeTrailingComments: 1 95 | SpacesInAngles: false 96 | SpacesInCStyleCastParentheses: false 97 | SpacesInContainerLiterals: false 98 | SpacesInParentheses: false 99 | SpacesInSquareBrackets: false 100 | #StatementMacros: # requires clang-format 8 101 | # - 'Q_OBJECT' 102 | TabWidth: 8 103 | #TypenameMacros: # requires clang-format 9 104 | # - 'DARRAY' 105 | UseTab: ForContinuationAndIndentation 106 | --- 107 | Language: ObjC 108 | -------------------------------------------------------------------------------- /.github/scripts/Build-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 4 | [string] $Configuration = 'RelWithDebInfo', 5 | [ValidateSet('x86', 'x64')] 6 | [string] $Target, 7 | [ValidateSet('Visual Studio 17 2022', 'Visual Studio 16 2019')] 8 | [string] $CMakeGenerator, 9 | [switch] $SkipAll, 10 | [switch] $SkipBuild, 11 | [switch] $SkipDeps, 12 | [switch] $SkipUnpack 13 | ) 14 | 15 | $ErrorActionPreference = 'Stop' 16 | 17 | if ( $DebugPreference -eq 'Continue' ) { 18 | $VerbosePreference = 'Continue' 19 | $InformationPreference = 'Continue' 20 | } 21 | 22 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 23 | Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 24 | exit 2 25 | } 26 | 27 | function Build { 28 | trap { 29 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 30 | Write-Error $_ 31 | exit 2 32 | } 33 | 34 | $ScriptHome = $PSScriptRoot 35 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 36 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 37 | 38 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 39 | 40 | foreach($Utility in $UtilityFunctions) { 41 | Write-Debug "Loading $($Utility.FullName)" 42 | . $Utility.FullName 43 | } 44 | 45 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 46 | $ProductName = $BuildSpec.name 47 | $ProductVersion = $BuildSpec.version 48 | 49 | $script:DepsVersion = '' 50 | $script:QtVersion = '5' 51 | $script:VisualStudioVersion = '' 52 | $script:PlatformSDK = '10.0.18363.657' 53 | 54 | Setup-Host 55 | 56 | if ( $CmakeGenerator -eq '' ) { 57 | $CmakeGenerator = $script:VisualStudioVersion 58 | } 59 | 60 | (Get-Content -Path ${ProjectRoot}/CMakeLists.txt -Raw) ` 61 | -replace "project\((.*) VERSION (.*)\)", "project(${ProductName} VERSION ${ProductVersion})" ` 62 | | Out-File -Path ${ProjectRoot}/CMakeLists.txt -NoNewline 63 | 64 | Setup-Obs 65 | 66 | Push-Location -Stack BuildTemp 67 | if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) { 68 | Ensure-Location $ProjectRoot 69 | 70 | $DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}" 71 | $CmakeArgs = @( 72 | '-G', $CmakeGenerator 73 | "-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}" 74 | "-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })" 75 | "-DCMAKE_BUILD_TYPE=${Configuration}" 76 | "-DCMAKE_PREFIX_PATH:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")" 77 | "-DQT_VERSION=${script:QtVersion}" 78 | ) 79 | 80 | Log-Debug "Attempting to configure OBS with CMake arguments: $($CmakeArgs | Out-String)" 81 | Log-Information "Configuring ${ProductName}..." 82 | Invoke-External cmake -S . -B build_${script:Target} @CmakeArgs 83 | 84 | $CmakeArgs = @( 85 | '--config', "${Configuration}" 86 | ) 87 | 88 | if ( $VerbosePreference -eq 'Continue' ) { 89 | $CmakeArgs+=('--verbose') 90 | } 91 | 92 | Log-Information "Building ${ProductName}..." 93 | Invoke-External cmake --build "build_${script:Target}" @CmakeArgs 94 | } 95 | Log-Information "Install ${ProductName}..." 96 | Invoke-External cmake --install "build_${script:Target}" --prefix "${ProjectRoot}/release" @CmakeArgs 97 | 98 | Pop-Location -Stack BuildTemp 99 | } 100 | 101 | Build 102 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Invoke-GitCheckout.ps1: -------------------------------------------------------------------------------- 1 | function Set-GitConfig { 2 | <# 3 | .SYNOPSIS 4 | Sets a git config value. 5 | .DESCRIPTION 6 | Allows setting single or multiple config values in a PowerShell-friendly fashion. 7 | .EXAMPLE 8 | Set-GitConfig advice.detachedHead false 9 | #> 10 | 11 | if ( $args.Count -lt 2 ) { 12 | throw 'Set-GitConfig called without required arguments